using System;
using System.Collections.Generic;
using Extensions;
using Interactivity.Events;
using TPUModelerEditor;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
using Utility.Attributes;

namespace Editor.PropertyDrawers
{
    [CustomPropertyDrawer(typeof(ExposeAttribute))]
    public class ScriptableExposerDrawer : PropertyDrawer
    {
        bool b = false;
        private SerializedObject _so;
        float _totalHeight;

        private Rect _areaRect;
        private float _spacing = 2.5f;

        private float _boxSizeOffset = 15f;

        private readonly Dictionary<string, ReorderableList> _reorderableListDictionary =
            new Dictionary<string, ReorderableList>();


        //0.95f,0.62f
        //Color.HSVToRGB(0, 0.95f, 0.42f);
        // public Color BackgroundColor =>
        //     EditorGUIUtility.isProSkin
        //         ? GUI.backgroundColor - new Color(0.8f, 0.8f, 0.8f, 0f)
        //         : Color.HSVToRGB(0, 0.95f, 0.82f);

        public override void OnGUI(Rect position, SerializedProperty property,
            GUIContent label)
        {
            //EditorGUIUtility.GetBuiltinSkin(EditorSkin.Inspector);

            ExposeScriptableObject(property, position, ref _so);
           
        }


        private bool IsCorrectType(Type firstValue, Type secondValue)
        {
            return firstValue.IsSubclassOf(secondValue) || firstValue == secondValue;
        }

        private bool IsCorrectType(SerializedProperty element, Type secondValue)
        {
            if (element.objectReferenceValue == default ||
                element.propertyType != SerializedPropertyType.ObjectReference)
                return false;

            return element.objectReferenceValue.GetType().IsSubclassOf(secondValue) ||
                   element.objectReferenceValue.GetType() == secondValue;
        }

        private void ExposeScriptableObject(SerializedProperty property, Rect position, ref SerializedObject so)
        {
            Rect r = position;
            r.y += 5f;

            r.height = EditorGUI.GetPropertyHeight(property, property.isExpanded);
            EditorGUI.PropertyField(r, property, property.isExpanded);

            if (property.objectReferenceValue != null)
                b = EditorGUI.Toggle(
                    new Rect(r.x + (EditorStyles.inspectorDefaultMargins.padding.horizontal + r.width / 2f) / 1.5f, r.y,
                        r.width / 4f,
                        r.height), b);


            if (!IsCorrectType(property, typeof(ScriptableObject)))
            {
                return;
            }

            if (so == null)
            {
                so = new SerializedObject(property.objectReferenceValue);
            }

            if (!b)
            {
                return;
            }

            r.y += r.height;
            GUI.Box(
                new Rect(r.x + OffsetByIndentLevel(_boxSizeOffset), r.y, r.width - OffsetByIndentLevel(_boxSizeOffset),
                    _totalHeight - 10f), GUIContent.none, "box");
            SerializedProperty arrayP = default;
            SerializedProperty it = so.GetIterator();
            it.Next(true);


            int ogIndentLevel = EditorGUI.indentLevel;
            EditorGUI.indentLevel++;

            bool disableFirstElement = true;
            bool indentOnSecondElement = false;


            while (it.NextVisible(true))
            {
                if (indentOnSecondElement)
                {
                    EditorGUI.indentLevel++;
                    indentOnSecondElement = false;
                }

                if (disableFirstElement)
                {
                    r.y += _spacing;
                    indentOnSecondElement = true;
                }

                if (arrayP != null && it.propertyPath.Contains(arrayP.propertyPath))
                {
                    continue;
                }

                if (it.propertyType == SerializedPropertyType.Generic && it.isArray)
                {
                    //DrawReorderableList(disableFirstElement, ref r, it);
                    arrayP = it;
                }


                DrawSingleElement(disableFirstElement, ref r, it);
                disableFirstElement = false;
                it.serializedObject.ApplyModifiedProperties();
            }

            EditorGUI.indentLevel = ogIndentLevel;
            property.serializedObject.ApplyModifiedProperties();
        }

        private static float OffsetByIndentLevel(float offset)
        {
            return offset * EditorGUI.indentLevel;
        }

        private void DrawReorderableList(bool disableFirstElement, ref Rect rect,
            SerializedProperty it)
        {
            string key = it.Copy().propertyPath;
            if (!_reorderableListDictionary.ContainsKey(key))
            {
                ReorderableList list = new ReorderableList(_so, it, true, true, true, true);
                list.elementHeightCallback += index => ElementHeightCallback(list, index);
                list.drawElementCallback += (elementRect, index, isActive, isFocused) =>
                    DrawElementCallback(elementRect, index, isActive, isFocused, list);
                list.drawHeaderCallback += (headerRect) => DrawHeaderCallback(headerRect, it);
                _reorderableListDictionary.Add(key, list);
            }

            //This DoList call back also throws the type error as well as the operation thingy.
            _reorderableListDictionary[key].DoList(rect);
        }

        private void DrawHeaderCallback(Rect rect, SerializedProperty property)
        {
            EditorGUI.LabelField(rect, new GUIContent(property.displayName));
        }

        private void DrawElementCallback(Rect rect, int index, bool isactive, bool isfocused,
            ReorderableList reorderableList)
        {
            SerializedProperty element = reorderableList.serializedProperty.GetArrayElementAtIndex(index);

            if (element.CountInProperty() == 1 &&
                IsCorrectType(element, typeof(ScriptableObject)))
            {
                SerializedObject localSo = new SerializedObject(element.objectReferenceValue);
                ExposeScriptableObject(element, rect, ref localSo);
            }
            else if (element.CountInProperty() > 1)
            {
                foreach (SerializedProperty p in element)
                {
                    DrawSingleElement(false, ref rect, p);
                }
            }
        }


        private float ElementHeightCallback(ReorderableList list, int index)
        {
            SerializedObject localSo = default;
            SerializedProperty element = list.serializedProperty.GetArrayElementAtIndex(index);
            if (element.CountInProperty() == 1 &&
                IsCorrectType(element, typeof(ScriptableObject)))
            {
                localSo = new SerializedObject(element.objectReferenceValue);
            }

            if (CalculatePropertyHeight(element, localSo, out var propertyHeight, false)) return propertyHeight;

            propertyHeight += EditorGUI.GetPropertyHeight(element, element.isExpanded);
            foreach (SerializedProperty p in element)
            {
                propertyHeight += EditorGUI.GetPropertyHeight(p, p.isExpanded) + _spacing;
            }

            return EditorGUI.GetPropertyHeight(element, element.isExpanded) + propertyHeight;
        }

        private void DrawSingleElement(bool disableFirstElement, ref Rect r, SerializedProperty it)
        {
            EditorGUI.BeginDisabledGroup(disableFirstElement);
            r.height = EditorGUI.GetPropertyHeight(it, it.isExpanded) +
                       _spacing;
            EditorGUI.PropertyField(r, it, it.isExpanded);
            EditorGUI.EndDisabledGroup();
            r.y += r.height + _spacing;
        }


        public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
        {
            if (property.objectReferenceValue != null && IsCorrectType(fieldInfo.FieldType, typeof(ScriptableObject)))
            {
                _so = new SerializedObject(property.objectReferenceValue);
            }

            if (CalculatePropertyHeight(property, _so, out var propertyHeight)) return propertyHeight;

            return base.GetPropertyHeight(property, label) + 5f;
        }

        private bool CalculatePropertyHeight(SerializedProperty property, SerializedObject so, out float propertyHeight,
            bool isLocal = true)
        {
            GUIContent label = default;
            if (so != null && b)
            {
                _totalHeight = 0;
                _totalHeight += 15f;
                SerializedProperty arrayP = default;
                SerializedProperty it = so.GetIterator();

                it.Next(true);


                while (it.NextVisible(true))
                {
                    if (arrayP != null && it.propertyPath.Contains(arrayP.propertyPath))
                    {
                        continue;
                    }

                    if (it.propertyType == SerializedPropertyType.Generic)
                    {
                        arrayP = it;
                    }

                    _totalHeight += EditorGUI.GetPropertyHeight(it, it.isExpanded) +
                                    _spacing * (it.isExpanded ? it.Copy().CountRemaining() + 1f : 1 + 2);
                }

                propertyHeight = isLocal
                    ? base.GetPropertyHeight(property, label) + _totalHeight
                    : EditorGUI.GetPropertyHeight(property, property.isExpanded) + _totalHeight;
                return true;
            }

            propertyHeight = 0;
            return false;
        }
    }
}