using UnityEngine;
using UnityEngine.Tilemaps;

namespace UnityEditor
{
    /// <summary>
    /// The Editor for a HexagonalRuleTile.
    /// </summary>
    [CustomEditor(typeof(HexagonalRuleTile), true)]
    [CanEditMultipleObjects]
    public class HexagonalRuleTileEditor : RuleTileEditor
    {
        /// <summary>
        /// The HexagonalRuleTile being edited.
        /// </summary>
        public HexagonalRuleTile hexTile => target as HexagonalRuleTile;

        /// <summary>
        /// Gets the index for a Rule with the HexagonalRuleTile to display an arrow.
        /// </summary>
        /// <param name="position">The adjacent position of the arrow.</param>
        /// <returns>Returns the index for a Rule with the HexagonalRuleTile to display an arrow.</returns>
        public override int GetArrowIndex(Vector3Int position)
        {
            if (position.y % 2 != 0)
            {
                position *= 2;
                position.x += 1;
            }

            if (position.x == 0)
            {
                if (position.y > 0)
                    return hexTile.m_FlatTop ? 5 : 1;
                else
                    return hexTile.m_FlatTop ? 3 : 7;
            }
            else if (position.y == 0)
            {
                if (position.x > 0)
                    return hexTile.m_FlatTop ? 1 : 5;
                else
                    return hexTile.m_FlatTop ? 7 : 3;
            }
            else
            {
                if (position.x < 0 && position.y > 0)
                    return hexTile.m_FlatTop ? 8 : 0;
                else if (position.x > 0 && position.y > 0)
                    return hexTile.m_FlatTop ? 2 : 2;
                else if (position.x < 0 && position.y < 0)
                    return hexTile.m_FlatTop ? 6 : 6;
                else if (position.x > 0 && position.y < 0)
                    return hexTile.m_FlatTop ? 0 : 8;
            }

            return -1;
        }

        /// <summary>
        /// Get the GUI bounds for a Rule.
        /// </summary>
        /// <param name="bounds">Cell bounds of the Rule.</param>
        /// <param name="rule">Rule to get GUI bounds for.</param>
        /// <returns>The GUI bounds for a rule.</returns>
        public override BoundsInt GetRuleGUIBounds(BoundsInt bounds, RuleTile.TilingRule rule)
        {
            foreach (var n in rule.GetNeighbors())
            {
                if (n.Key.x == bounds.xMax - 1 && n.Key.y % 2 != 0)
                {
                    bounds.xMax++;
                    break;
                }
            }
            return base.GetRuleGUIBounds(bounds, rule);
        }

        /// <summary>
        /// Gets the GUI matrix size for a Rule of a HexagonalRuleTile
        /// </summary>
        /// <param name="bounds">Cell bounds of the Rule.</param>
        /// <returns>Returns the GUI matrix size for a Rule of a HexagonalRuleTile.</returns>
        public override Vector2 GetMatrixSize(BoundsInt bounds)
        {
            Vector2 size = base.GetMatrixSize(bounds);
            return hexTile.m_FlatTop ? new Vector2(size.y, size.x) : size;
        }

        /// <summary>
        /// Draws a Rule Matrix for the given Rule for a HexagonalRuleTile.
        /// </summary>
        /// <param name="tile">Tile to draw rule for.</param>
        /// <param name="rect">GUI Rect to draw rule at.</param>
        /// <param name="bounds">Cell bounds of the Rule.</param>
        /// <param name="tilingRule">Rule to draw Rule Matrix for.</param>
        public override void RuleMatrixOnGUI(RuleTile tile, Rect rect, BoundsInt bounds, RuleTile.TilingRule tilingRule)
        {
            bool flatTop = hexTile.m_FlatTop;

            Handles.color = EditorGUIUtility.isProSkin ? new Color(1f, 1f, 1f, 0.2f) : new Color(0f, 0f, 0f, 0.2f);
            float w = rect.width / (flatTop ? bounds.size.y : bounds.size.x);
            float h = rect.height / (flatTop ? bounds.size.x : bounds.size.y);

            // Grid
            if (flatTop)
            {
                for (int y = 0; y <= bounds.size.y; y++)
                {
                    float left = rect.xMin + y * w;
                    float offset = 0;

                    if (y == 0 && bounds.yMax % 2 == 0)
                        offset = h / 2;
                    else if (y == bounds.size.y && bounds.yMin % 2 != 0)
                        offset = h / 2;

                    Handles.DrawLine(new Vector3(left, rect.yMin + offset), new Vector3(left, rect.yMax - offset));

                    if (y < bounds.size.y)
                    {
                        bool noOffset = (y + bounds.yMax) % 2 != 0;
                        for (int x = 0; x < (noOffset ? (bounds.size.x + 1) : bounds.size.x); x++)
                        {
                            float top = rect.yMin + x * h + (noOffset ? 0 : h / 2);
                            Handles.DrawLine(new Vector3(left, top), new Vector3(left + w, top));
                        }
                    }
                }
            }
            else
            {
                for (int y = 0; y <= bounds.size.y; y++)
                {
                    float top = rect.yMin + y * h;
                    float offset = 0;

                    if (y == 0 && bounds.yMax % 2 == 0)
                        offset = w / 2;
                    else if (y == bounds.size.y && bounds.yMin % 2 != 0)
                        offset = w / 2;

                    Handles.DrawLine(new Vector3(rect.xMin + offset, top), new Vector3(rect.xMax - offset, top));

                    if (y < bounds.size.y)
                    {
                        bool noOffset = (y + bounds.yMax) % 2 != 0;
                        for (int x = 0; x < (noOffset ? (bounds.size.x + 1) : bounds.size.x); x++)
                        {
                            float left = rect.xMin + x * w + (noOffset ? 0 : w / 2);
                            Handles.DrawLine(new Vector3(left, top), new Vector3(left, top + h));
                        }
                    }
                }
            }

            var neighbors = tilingRule.GetNeighbors();

            // Icons
            Handles.color = Color.white;
            for (int y = bounds.yMin; y < bounds.yMax; y++)
            {
                int xMax = y % 2 == 0 ? bounds.xMax : (bounds.xMax - 1);
                for (int x = bounds.xMin; x < xMax; x++)
                {
                    Vector3Int pos = new Vector3Int(x, y, 0);
                    Vector2 offset = new Vector2(x - bounds.xMin, -y + bounds.yMax - 1);
                    Rect r = flatTop ? new Rect(rect.xMax - offset.y * w - w, rect.yMax - offset.x * h - h, w - 1, h - 1)
                        : new Rect(rect.xMin + offset.x * w, rect.yMin + offset.y * h, w - 1, h - 1);

                    if (y % 2 != 0)
                    {
                        if (flatTop)
                            r.y -= h / 2;
                        else
                            r.x += w / 2;
                    }

                    RuleMatrixIconOnGUI(tilingRule, neighbors, pos, r);
                }
            }
        }

        /// <summary>
        /// Creates a Preview for the HexagonalRuleTile.
        /// </summary>
        protected override void CreatePreview()
        {
            base.CreatePreview();

            m_PreviewGrid.cellLayout = GridLayout.CellLayout.Hexagon;
            m_PreviewGrid.cellSize = new Vector3(0.8659766f, 1.0f, 1.0f);
            m_PreviewGrid.cellSwizzle = hexTile.m_FlatTop ? GridLayout.CellSwizzle.YXZ : GridLayout.CellSwizzle.XYZ;

            foreach (var tilemap in m_PreviewTilemaps)
            {
                tilemap.tileAnchor = Vector3.zero;
                tilemap.ClearAllTiles();
            }

            for (int x = -1; x <= 0; ++x)
                for (int y = -1; y <= 1; ++y)
                    m_PreviewTilemaps[0].SetTile(new Vector3Int(x, y, 0), tile);

            m_PreviewTilemaps[1].SetTile(new Vector3Int(1, -1, 0), tile);
            m_PreviewTilemaps[1].SetTile(new Vector3Int(2, 0, 0), tile);
            m_PreviewTilemaps[1].SetTile(new Vector3Int(2, 1, 0), tile);

            for (int x = -1; x <= 1; x++)
                m_PreviewTilemaps[2].SetTile(new Vector3Int(x, -2, 0), tile);

            m_PreviewTilemaps[3].SetTile(new Vector3Int(1, 1, 0), tile);

            foreach (var tilemapRenderer in m_PreviewTilemapRenderers)
                tilemapRenderer.sortOrder = TilemapRenderer.SortOrder.TopRight;

            m_PreviewTilemapRenderers[0].sortingOrder = 0;
            m_PreviewTilemapRenderers[1].sortingOrder = -1;
            m_PreviewTilemapRenderers[2].sortingOrder = 1;
            m_PreviewTilemapRenderers[3].sortingOrder = 0;
        }
    }
}
