using System;
using UnityEngine;

[CreateAssetMenu]
public class MapData : ScriptableObject {
	static readonly Vector2[] AxialDirections = {
		new Vector2(0, 1),
		new Vector2(1, 0),
		new Vector2(1, -1),
		new Vector2(0, -1),
		new Vector2(-1, 0),
		new Vector2(-1, 1)
	};

	//With flat hexes
	public enum Direction {
		Up = 0,
		UpRight = 1,
		DownRight = 2,
		Down = 3,
		DownLeft = 4,
		UpLeft = 5
	}

	/* With pointy hexes
	enum Direction {
		Right,
		UpRight,
		UpLeft,
		Left,
		DownLeft,
		DownRight
	}*/

	public float hexSize = 1f;
	//TODO Make hexSize modify hexHeight on set
	public float hexHeight = Mathf.Sqrt(3f);

	//TODO would be nice if I could store a Matrix2x2
	public Vector3 AxialToWorld(Vector2 axial)
		=> hexSize * new Vector3(
			(3f / 2) * axial.x,
			0,
			(Mathf.Sqrt(3) / 2) * axial.x + Mathf.Sqrt(3) * axial.y
		);

	public Vector2 WorldToAxial(Vector2 position)
		=> RoundAxial(new Vector2(
			2f / 3 * position.x,
			-1f / 3 * position.x + Mathf.Sqrt(3) / 3 * position.y
		) / hexSize);

	public Vector2 WorldToAxial(Vector3 position)
		=> WorldToAxial(new Vector2(position.x, position.z));

	public static Vector2 RoundAxial(Vector2 axial) {
		Vector3 cube = AxialToCube(axial);
		Vector3 nearestCube = new Vector3(
			Mathf.Round(cube.x),
			Mathf.Round(cube.y),
			Mathf.Round(cube.z)
		);

		Vector3 diff = new Vector3(
			Mathf.Abs(nearestCube.x - cube.x),
			Mathf.Abs(nearestCube.y - cube.y),
			Mathf.Abs(nearestCube.z - cube.z)
		);

		if (diff.x > diff.y && diff.x > diff.z)
			nearestCube.x = -nearestCube.y - nearestCube.z;
		else if (diff.y > diff.z)
			nearestCube.y = -nearestCube.x - nearestCube.z;
		else
			nearestCube.z = -nearestCube.x - nearestCube.y;

		return CubeToAxial(nearestCube);
	}

	public static Vector3 AxialToCube(Vector2 axial)
		=> new Vector3(axial.x, -axial.x - axial.y, axial.y);

	public static Vector2 CubeToAxial(Vector3 cube)
		=> new Vector2(cube.x, cube.z);

	public static Vector2 GetNeighbor(Vector2 axial, Direction direction)
		=> axial + AxialDirections[(int) direction];

	public Vector3 GetClosestTilePos(Vector3 worldPos) {
		Vector2 coords = WorldToAxial(worldPos);
		return AxialToWorld(coords) + Vector3.up * worldPos.y;
	}
}