using UnityEngine;
using UnityEngine.Tilemaps;
using UnityEditorInternal;
using System.Collections.Generic;

namespace UnityEditor
{
    /// <summary>
    /// The Editor for an AdvancedRuleOverrideTileEditor.
    /// </summary>
    [CustomEditor(typeof(AdvancedRuleOverrideTile))]
    public class AdvancedRuleOverrideTileEditor : RuleOverrideTileEditor
    {
        private static class Styles
        {
            public static readonly GUIContent defaultSprite = EditorGUIUtility.TrTextContent("Default Sprite"
                , "Overrides the default Sprite for the original Rule Tile.");
            public static readonly GUIContent defaultGameObject = EditorGUIUtility.TrTextContent("Default GameObject"
                , "Overrides the default GameObject for the original Rule Tile.");
            public static readonly GUIContent defaultCollider = EditorGUIUtility.TrTextContent("Default Collider"
                , "Overrides the default Collider for the original Rule Tile.");
        }

        /// <summary>
        /// The AdvancedRuleOverrideTile being edited.
        /// </summary>
        public new AdvancedRuleOverrideTile overrideTile => target as AdvancedRuleOverrideTile;

        List<KeyValuePair<RuleTile.TilingRule, RuleTile.TilingRuleOutput>> m_Rules = new List<KeyValuePair<RuleTile.TilingRule, RuleTile.TilingRuleOutput>>();
        private ReorderableList m_RuleList;
        private int m_MissingOriginalRuleIndex;
        private HashSet<int> m_UniqueIds = new HashSet<int>();

        static float k_DefaultElementHeight { get { return RuleTileEditor.k_DefaultElementHeight; } }
        static float k_SingleLineHeight { get { return RuleTileEditor.k_SingleLineHeight; } }

        /// <summary>
        /// OnEnable for the AdvancedRuleOverrideTileEditor
        /// </summary>
        public override void OnEnable()
        {
            if (m_RuleList == null)
            {
                m_RuleList = new ReorderableList(m_Rules, typeof(KeyValuePair<RuleTile.TilingRule, RuleTile.TilingRule>), false, true, false, false);
                m_RuleList.drawHeaderCallback = DrawRulesHeader;
                m_RuleList.drawElementCallback = DrawRuleElement;
                m_RuleList.elementHeightCallback = GetRuleElementHeight;
            }
        }

        /// <summary>
        /// Draws the Inspector GUI for the AdvancedRuleOverrideTileEditor
        /// </summary>
        public override void OnInspectorGUI()
        {
            serializedObject.UpdateIfRequiredOrScript();

            DrawTileField();

            EditorGUI.BeginChangeCheck();
            overrideTile.m_DefaultSprite = EditorGUILayout.ObjectField(Styles.defaultSprite, overrideTile.m_DefaultSprite, typeof(Sprite), false) as Sprite;
            overrideTile.m_DefaultGameObject = EditorGUILayout.ObjectField(Styles.defaultGameObject, overrideTile.m_DefaultGameObject, typeof(GameObject), false) as GameObject;
            overrideTile.m_DefaultColliderType = (Tile.ColliderType)EditorGUILayout.EnumPopup(Styles.defaultCollider, overrideTile.m_DefaultColliderType);
            if (EditorGUI.EndChangeCheck())
                SaveTile();

            DrawCustomFields();

            if (overrideTile.m_Tile)
            {
                ValidateRuleTile(overrideTile.m_Tile);
                overrideTile.GetOverrides(m_Rules, ref m_MissingOriginalRuleIndex);
            }

            m_RuleList.DoLayoutList();
        }

        private void ValidateRuleTile(RuleTile ruleTile)
        {
            // Ensure that each Tiling Rule in the RuleTile has a unique ID
            m_UniqueIds.Clear();
            var startId = 0;
            foreach (var rule in ruleTile.m_TilingRules)
            {
                if (m_UniqueIds.Contains(rule.m_Id))
                {
                    do
                    {
                        rule.m_Id = startId++;
                    } while (m_UniqueIds.Contains(rule.m_Id));
                    EditorUtility.SetDirty(ruleTile);
                }
                m_UniqueIds.Add(rule.m_Id);
                startId++;
            }
        }
        
        /// <summary>
        /// Draws the Header for the Rule list
        /// </summary>
        /// <param name="rect">Rect to draw the header in</param>
        public void DrawRulesHeader(Rect rect)
        {
            GUI.Label(rect, "Tiling Rules", EditorStyles.label);
        }

        /// <summary>
        /// Draws the Rule element for the Rule list
        /// </summary>
        /// <param name="rect">Rect to draw the Rule Element in</param>
        /// <param name="index">Index of the Rule Element to draw</param>
        /// <param name="active">Whether the Rule Element is active</param>
        /// <param name="focused">Whether the Rule Element is focused</param>
        public void DrawRuleElement(Rect rect, int index, bool active, bool focused)
        {
            RuleTile.TilingRule originalRule = m_Rules[index].Key;
            if (originalRule == null)
                return;
            
            RuleTile.TilingRuleOutput overrideRule = m_Rules[index].Value;
            bool isMissing = index >= m_MissingOriginalRuleIndex;

            DrawToggleInternal(new Rect(rect.xMin, rect.yMin, 16, rect.height));
            DrawRuleInternal(new Rect(rect.xMin + 16, rect.yMin, rect.width - 16, rect.height));

            void DrawToggleInternal(Rect r)
            {
                EditorGUI.BeginChangeCheck();

                bool enabled = EditorGUI.Toggle(new Rect(r.xMin, r.yMin, r.width, k_SingleLineHeight), overrideRule != null);

                if (EditorGUI.EndChangeCheck())
                {
                    if (enabled)
                        overrideTile[originalRule] = originalRule;
                    else
                        overrideTile[originalRule] = null;

                    SaveTile();
                }
            }
            void DrawRuleInternal(Rect r)
            {
                EditorGUI.BeginChangeCheck();

                DrawRule(r, overrideRule ?? originalRule, overrideRule != null, originalRule, isMissing);

                if (EditorGUI.EndChangeCheck())
                    SaveTile();
            }
        }

        /// <summary>
        /// Draw a Rule Override for the AdvancedRuleOverrideTileEditor
        /// </summary>
        /// <param name="rect">Rect to draw the Rule in</param>
        /// <param name="rule">The Rule Override to draw</param>
        /// <param name="isOverride">Whether the original Rule is being overridden</param>
        /// <param name="originalRule">Original Rule to override</param>
        /// <param name="isMissing">Whether the original Rule is missing</param>
        public void DrawRule(Rect rect, RuleTile.TilingRuleOutput rule, bool isOverride, RuleTile.TilingRule originalRule, bool isMissing)
        {
            if (isMissing)
            {
                EditorGUI.HelpBox(new Rect(rect.xMin, rect.yMin, rect.width, 16), "Original Tiling Rule missing", MessageType.Warning);
                rect.yMin += 16;
            }

            using (new EditorGUI.DisabledScope(!isOverride))
            {
                float yPos = rect.yMin + 2f;
                float height = rect.height - k_PaddingBetweenRules;
                float matrixWidth = k_DefaultElementHeight;

                BoundsInt ruleBounds = originalRule.GetBounds();
                BoundsInt ruleGuiBounds = ruleTileEditor.GetRuleGUIBounds(ruleBounds, originalRule);
                Vector2 matrixSize = ruleTileEditor.GetMatrixSize(ruleGuiBounds);
                Vector2 matrixSizeRate = matrixSize / Mathf.Max(matrixSize.x, matrixSize.y);
                Vector2 matrixRectSize = new Vector2(matrixWidth * matrixSizeRate.x, k_DefaultElementHeight * matrixSizeRate.y);
                Vector2 matrixRectPosition = new Vector2(rect.xMax - matrixWidth * 2f - 10f, yPos);
                matrixRectPosition.x += (matrixWidth - matrixRectSize.x) * 0.5f;
                matrixRectPosition.y += (k_DefaultElementHeight - matrixRectSize.y) * 0.5f;

                Rect inspectorRect = new Rect(rect.xMin, yPos, rect.width - matrixWidth * 2f - 20f, height);
                Rect matrixRect = new Rect(matrixRectPosition, matrixRectSize);
                Rect spriteRect = new Rect(rect.xMax - matrixWidth - 5f, yPos, matrixWidth, k_DefaultElementHeight);

                ruleTileEditor.RuleInspectorOnGUI(inspectorRect, rule);
                ruleTileEditor.SpriteOnGUI(spriteRect, rule);

                if (!isMissing)
                    using (new EditorGUI.DisabledScope(true))
                        ruleTileEditor.RuleMatrixOnGUI(overrideTile.m_InstanceTile, matrixRect, ruleGuiBounds, originalRule);
            }
        }

        /// <summary>
        /// Returns the height for an indexed Rule Element
        /// </summary>
        /// <param name="index">Index of the Rule Element</param>
        /// <returns>Height of the indexed Rule Element</returns>
        public float GetRuleElementHeight(int index)
        {
            var originalRule = m_Rules[index].Key;
            var overrideRule = m_Rules[index].Value;
            float height = overrideRule != null ? ruleTileEditor.GetElementHeight(overrideRule) : ruleTileEditor.GetElementHeight(originalRule);

            bool isMissing = index >= m_MissingOriginalRuleIndex;
            if (isMissing)
                height += 16;

            return height;
        }
    }
}