﻿using UnityEngine;

namespace Gamelogic.Grids
{

	[Experimental]
	public class TriangularPolarPointyBrickMap : AbstractMap<PointyHexPoint>, IPolarMap<PointyHexPoint>
	{
		#region Fields

		private readonly float[] sectorAngleRad;

		#endregion

		#region Properties

		public float[] SectorAngle
		{
			get;
			private set;
		}

		public Vector2 Center
		{
			get;
			private set;
		}

		public int SectorsInFirstRow
		{
			get;
			private set;
		}

		public int BandsCount
		{
			get; private set;
		}

		public float InnerRadius
		{
			get;
			private set;
		}

		public float OuterRadius
		{
			get;
			private set;
		}

		public PointyHexPoint Infinity
		{
			get
			{
				return new PointyHexPoint(0, BandsCount);
			}
		}

		#endregion

		#region Construction
		public TriangularPolarPointyBrickMap(Vector2 center, float innerRadius, float outerRadius, int sectorsInFirstRow, int bandsCount)
			: base(Vector2.one)
		{
			Center = center;
			InnerRadius = innerRadius;
			OuterRadius = outerRadius;
			SectorsInFirstRow = sectorsInFirstRow;
			BandsCount = bandsCount;
			sectorAngleRad = new float[bandsCount];
			SectorAngle = new float[bandsCount];

			for (int i = 0; i < bandsCount; i++)
			{
				sectorAngleRad[i] = (2f * Mathf.PI) / ((float)SectorsInFirstRow + i);
				SectorAngle[i] = 360f / ((float)SectorsInFirstRow + i);
			}
		}
		#endregion

		#region AbstractMap Implementation
		public override PointyHexPoint RawWorldToGrid(Vector2 worldPoint)
		{
			Vector2 correctedWorldPoint = Transform(worldPoint - Center, OuterRadius);
			float angleRad = Mathf.Atan2(correctedWorldPoint.y, correctedWorldPoint.x);

			if (angleRad < 0)
			{
				angleRad += 2 * Mathf.PI;
			}

			float radius = correctedWorldPoint.magnitude;

			int n = Mathf.FloorToInt((radius - InnerRadius) / (OuterRadius - InnerRadius) * BandsCount);

			if (n < 0 || n >= BandsCount)
			{
				return Infinity;
			}

			int m = Mathf.FloorToInt((angleRad - sectorAngleRad[n] * n / 2) / sectorAngleRad[n]);

			//m = Mathi.Mod(m, SectorsInFirstRow + n);

			Debug.Log(new PointyHexPoint(m, n));
			Debug.Log(new PointyHexPoint(m == 0 ? 0 : Mathi.Mod(m, n + 1) - n - 1, n));
			return new PointyHexPoint(m == 0 ? 0 : Mathi.Mod(m, n + 1) - n - 1, n);
			//return new PointyHexPoint(m, n);
		}

		public override Vector2 GridToWorld(PointyHexPoint gridPoint)
		{
			float m = gridPoint.X;
			float n = gridPoint.Y;
			float sectorCount = (SectorsInFirstRow + n);
			float angleRad = (m / sectorCount) * 2f * Mathf.PI + (Mathf.PI / sectorCount) + n * sectorAngleRad[gridPoint.Y] / 2f;
			float radius = (n / BandsCount) * (OuterRadius - InnerRadius) + InnerRadius +
						   (OuterRadius - InnerRadius) / (2f * BandsCount);
			float x = radius * Mathf.Cos(angleRad) + Center.x;
			float y = radius * Mathf.Sin(angleRad) + Center.y;

			return InverseTransform(new Vector2(x, y), OuterRadius);
		}

		public static Vector2 Transform(Vector2 v, float radius)
		{
			return v;
		}

		public static Vector2 InverseTransform(Vector2 v, float radius)
		{
			return v;
		}

		public Vector2 CalcGridDimensions_(IGridSpace<PointyHexPoint> grid)
		{
			return new Vector2(2 * OuterRadius, 2 * OuterRadius);
		}

		public Vector2 CalcBottomLeft_(IGridSpace<PointyHexPoint> grid)
		{
			return Center - new Vector2(OuterRadius, OuterRadius);
		}

		override public Vector2 CalcGridDimensions(IGridSpace<PointyHexPoint> grid)
		{
			var bottomLeft = CalcBottomLeft_(grid);
			var dimensions = CalcGridDimensions_(grid);
			var topRight = bottomLeft + dimensions;
			return Transform(bottomLeft, OuterRadius) - Transform(topRight, OuterRadius);
		}

		override public Vector2 CalcBottomLeft(IGridSpace<PointyHexPoint> grid)
		{
			return Transform(CalcBottomLeft_(grid), OuterRadius);
		}

		#endregion

		#region Interface

		/**
			Returns the Z angle in degrees of the given grid point.

			This can be used to rotate cells appropriately.
		*/
		public float GetStartAngleZ(PointyHexPoint gridPoint)
		{
			int m = gridPoint.X;
			int n = gridPoint.Y;
			float angle = m * SectorAngle[n] + n * SectorAngle[n] / 2;

			return angle;
		}

		public float GetEndAngleZ(PointyHexPoint gridPoint)
		{
			float angle = GetStartAngleZ(gridPoint) + SectorAngle[gridPoint.Y];

			return angle;
		}

		public float GetInnerRadius(PointyHexPoint gridPoint)
		{
			return gridPoint.Y / (float)BandsCount * (OuterRadius - InnerRadius) + InnerRadius;
		}

		public float GetOuterRadius(PointyHexPoint gridPoint)
		{
			return ((gridPoint.Y + 1) / (float)BandsCount) * (OuterRadius - InnerRadius) + InnerRadius;
		}

		#endregion
	}
}