using System;
namespace UnityEngine
{
    /// 
    /// Generic visual tile for creating different tilesets like terrain, pipeline, random or animated tiles.
    /// This is templated to accept a Neighbor Rule Class for Custom Rules.
    /// Use this for Hexagonal Grids. 
    /// 
    /// Neighbor Rule Class for Custom Rules
    public class HexagonalRuleTile : HexagonalRuleTile
    {
        /// 
        /// Returns the Neighbor Rule Class type for this Rule Tile.
        /// 
        public sealed override Type m_NeighborType => typeof(T);
    }
    /// 
    /// Generic visual tile for creating different tilesets like terrain, pipeline, random or animated tiles.
    /// Use this for Hexagonal Grids.
    /// 
    [Serializable]
    [HelpURL("https://docs.unity3d.com/Packages/com.unity.2d.tilemap.extras@latest/index.html?subfolder=/manual/RuleTile.html")]
    public class HexagonalRuleTile : RuleTile
    {
        /// 
        /// Angle in which the HexagonalRuleTile is rotated by for matching in Degrees.
        /// 
        public override int m_RotationAngle => 60;
        private static float[] m_CosAngleArr1 = {
            Mathf.Cos(0 * Mathf.Deg2Rad),
            Mathf.Cos(-60 * Mathf.Deg2Rad),
            Mathf.Cos(-120 * Mathf.Deg2Rad),
            Mathf.Cos(-180 * Mathf.Deg2Rad),
            Mathf.Cos(-240 * Mathf.Deg2Rad),
            Mathf.Cos(-300 * Mathf.Deg2Rad),
        };
        private static float[] m_SinAngleArr1 = {
            Mathf.Sin(0 * Mathf.Deg2Rad),
            Mathf.Sin(-60 * Mathf.Deg2Rad),
            Mathf.Sin(-120 * Mathf.Deg2Rad),
            Mathf.Sin(-180 * Mathf.Deg2Rad),
            Mathf.Sin(-240 * Mathf.Deg2Rad),
            Mathf.Sin(-300 * Mathf.Deg2Rad),
        };
        private static float[] m_CosAngleArr2 = {
            Mathf.Cos(0 * Mathf.Deg2Rad),
            Mathf.Cos(60 * Mathf.Deg2Rad),
            Mathf.Cos(120 * Mathf.Deg2Rad),
            Mathf.Cos(180 * Mathf.Deg2Rad),
            Mathf.Cos(240 * Mathf.Deg2Rad),
            Mathf.Cos(300 * Mathf.Deg2Rad),
        };
        private static float[] m_SinAngleArr2 = {
            Mathf.Sin(0 * Mathf.Deg2Rad),
            Mathf.Sin(60 * Mathf.Deg2Rad),
            Mathf.Sin(120 * Mathf.Deg2Rad),
            Mathf.Sin(180 * Mathf.Deg2Rad),
            Mathf.Sin(240 * Mathf.Deg2Rad),
            Mathf.Sin(300 * Mathf.Deg2Rad),
        };
        /// 
        /// Whether this is a flat top Hexagonal Tile
        /// 
        [DontOverride] public bool m_FlatTop;
        static float m_TilemapToWorldYScale = Mathf.Pow(1 - Mathf.Pow(0.5f, 2f), 0.5f);
        /// 
        /// Converts a Tilemap Position to World Position.
        /// 
        /// Tilemap Position to convert.
        /// World Position.
        public static Vector3 TilemapPositionToWorldPosition(Vector3Int tilemapPosition)
        {
            Vector3 worldPosition = new Vector3(tilemapPosition.x, tilemapPosition.y);
            if (tilemapPosition.y % 2 != 0)
                worldPosition.x += 0.5f;
            worldPosition.y *= m_TilemapToWorldYScale;
            return worldPosition;
        }
        /// 
        /// Converts a World Position to Tilemap Position.
        /// 
        /// World Position to convert.
        /// Tilemap Position.
        public static Vector3Int WorldPositionToTilemapPosition(Vector3 worldPosition)
        {
            worldPosition.y /= m_TilemapToWorldYScale;
            Vector3Int tilemapPosition = new Vector3Int();
            tilemapPosition.y = Mathf.RoundToInt(worldPosition.y);
            if (tilemapPosition.y % 2 != 0)
                tilemapPosition.x = Mathf.RoundToInt(worldPosition.x - 0.5f);
            else
                tilemapPosition.x = Mathf.RoundToInt(worldPosition.x);
            return tilemapPosition;
        }
        /// 
        /// Get the offset for the given position with the given offset.
        /// 
        /// Position to offset.
        /// Offset for the position.
        /// The offset position.
        public override Vector3Int GetOffsetPosition(Vector3Int position, Vector3Int offset)
        {
            Vector3Int offsetPosition = position + offset;
            if (offset.y % 2 != 0 && position.y % 2 != 0)
                offsetPosition.x += 1;
            return offsetPosition;
        }
        /// 
        /// Get the reversed offset for the given position with the given offset.
        /// 
        /// Position to offset.
        /// Offset for the position.
        /// The reversed offset position.
        public override Vector3Int GetOffsetPositionReverse(Vector3Int position, Vector3Int offset)
        {
            return GetOffsetPosition(position, GetRotatedPosition(offset, 180));
        }
        /// 
        /// Gets a rotated position given its original position and the rotation in degrees. 
        /// 
        /// Original position of Tile.
        /// Rotation in degrees.
        /// Rotated position of Tile.
        public override Vector3Int GetRotatedPosition(Vector3Int position, int rotation)
        {
            if (rotation != 0)
            {
                Vector3 worldPosition = TilemapPositionToWorldPosition(position);
                int index = rotation / 60;
                if (m_FlatTop)
                {
                    worldPosition = new Vector3(
                        worldPosition.x * m_CosAngleArr2[index] - worldPosition.y * m_SinAngleArr2[index],
                        worldPosition.x * m_SinAngleArr2[index] + worldPosition.y * m_CosAngleArr2[index]
                    );
                }
                else
                {
                    worldPosition = new Vector3(
                        worldPosition.x * m_CosAngleArr1[index] - worldPosition.y * m_SinAngleArr1[index],
                        worldPosition.x * m_SinAngleArr1[index] + worldPosition.y * m_CosAngleArr1[index]
                    );
                }
                position = WorldPositionToTilemapPosition(worldPosition);
            }
            return position;
        }
        /// 
        /// Gets a mirrored position given its original position and the mirroring axii.
        /// 
        /// Original position of Tile.
        /// Mirror in the X Axis.
        /// Mirror in the Y Axis.
        /// Mirrored position of Tile.
        public override Vector3Int GetMirroredPosition(Vector3Int position, bool mirrorX, bool mirrorY)
        {
            if (mirrorX || mirrorY)
            {
                Vector3 worldPosition = TilemapPositionToWorldPosition(position);
                if (m_FlatTop)
                {
                    if (mirrorX)
                        worldPosition.y *= -1;
                    if (mirrorY)
                        worldPosition.x *= -1;
                }
                else
                {
                    if (mirrorX)
                        worldPosition.x *= -1;
                    if (mirrorY)
                        worldPosition.y *= -1;
                }
                position = WorldPositionToTilemapPosition(worldPosition);
            }
            return position;
        }
    }
}