﻿using System.Linq;
using Gamelogic;
using Gamelogic.Grids;
using UnityEngine;

namespace Gamelogic.Grids.Examples
{
	public class MobiusStripRectTest : GLMonoBehaviour
	{
		private const int CellSize = 4;

		private readonly Vector2[] baseUVs =
		{
			new Vector2(0, 1),
			new Vector2(0, 0),
			new Vector2(1, 1),
			new Vector2(0, 0),
			new Vector2(1, 0),
			new Vector2(1, 1)
		};

		private readonly int[] rawVertexIndexOffsets =
		{
			0, 1, 2, 1, 3, 2
		};

		public int halfTwistCount = 1;
		public int columnCount = 50;
		public int rowCount = 20;
		public float bigRadius = 20;
		public float smallRadius = 40;

		public void Start()
		{
			MakeMobiusStrip();
		}

		private void MakeMobiusStrip()
		{
			var meshes = new CombineInstance[rowCount];

			for (int row = 0; row < rowCount; row++)
			{
				var rowMesh = CreateMesh(row, rawVertexIndexOffsets, baseUVs);

				meshes[row] = new CombineInstance
				{
					mesh = rowMesh,
					transform = transform.localToWorldMatrix
				};
			}

			var mesh = new Mesh();
			mesh.CombineMeshes(meshes);
			GetComponent<MeshFilter>().mesh = mesh;
		}

		private Mesh CreateMesh(int row, int[] rawVertexIndexOffsets, Vector2[] baseUVs)
		{
			var rawVertices = MakeRawVertices(row);
			var vertices = MakeVertices(rawVertices, rawVertexIndexOffsets);
			var tris = MakeTris();
			var colors = MakeColors(row);
			var uvs = MakeUVs(baseUVs);

			var mesh = new Mesh {vertices = vertices, triangles = tris, uv = uvs, colors = colors};

			mesh.Optimize();
			mesh.RecalculateNormals();
			mesh.RecalculateBounds();

			return mesh;
		}

		private Vector3[] MakeVertices(Vector3[] rawVertices, int[] rawVertexIndexOffsets)
		{
			return (
				from index in Enumerable.Range(0, columnCount)
				from rawVertexIndexOffset in rawVertexIndexOffsets
				select rawVertices[2*index + rawVertexIndexOffset]).ToArray();
		}

		private int[] MakeTris()
		{
			return Enumerable.Range(0, columnCount*6).ToArray();
		}

		private Vector2[] MakeUVs(Vector2[] baseUVs)
		{
			return Enumerable.Repeat(baseUVs, columnCount).SelectMany(v => v).ToArray();
		}

		private Color[] MakeColors(int row)
		{
			Color[] colors =
				Enumerable.Range(0, columnCount)
					.SelectMany(
						index =>
							Enumerable.Repeat(ExampleUtils.Colors[new RectPoint(index/CellSize, row/CellSize).GetColor4()], 6).ToArray())
					.ToArray();
			return colors;
		}

		private Vector3[] MakeRawVertices(int row)
		{
			int vertexCount = 2*columnCount;
			Vector3[] rawVertices = new Vector3[vertexCount + 2];

			int rawVertexIndex = 0;

			for (int i = 0; i < columnCount + 1; i++)
			{
				float angle = i/(float) columnCount*Mathf.PI*2;

				float smallCircleX1 = smallRadius*Mathf.Cos(halfTwistCount*angle/2);

				float x1 = (bigRadius + smallCircleX1)*Mathf.Cos(angle*2);
				float y1 = smallRadius*Mathf.Sin(halfTwistCount*angle/2); //smallCircleY
				float z1 = (bigRadius + smallCircleX1)*Mathf.Sin(angle*2);

				float smallCircleX2 = -smallRadius*Mathf.Cos(halfTwistCount*angle/2);

				float x2 = (bigRadius + smallCircleX2)*Mathf.Cos(angle*2);
				float y2 = -smallRadius*Mathf.Sin(halfTwistCount*angle/2); //smallCircleY
				float z2 = (bigRadius + smallCircleX2)*Mathf.Sin(angle*2);


				var position1 = new Vector3(x1, y1, z1);
				var position2 = new Vector3(x2, y2, z2);

				rawVertices[rawVertexIndex + 0] = Vector3.Lerp(position1, position2, row/(float) rowCount);
				rawVertices[rawVertexIndex + 1] = Vector3.Lerp(position1, position2, (row + 1)/(float) rowCount);
				rawVertexIndex += 2;
			}

			return rawVertices;
		}
	}
}