using System;
using System.Collections.Generic;
using KitsuneCafe.Extension;
using KitsuneCafe.System.Attributes;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;

namespace KitsuneCafe.Editor
{
    [CustomPropertyDrawer(typeof(AnimatorParamAttribute))]
    public class AnimatorParamPropertyDrawer : PropertyDrawer
    {
        public override VisualElement CreatePropertyGUI(SerializedProperty property)
        {
            var attribute = base.attribute as AnimatorParamAttribute;

            return property.type switch
            {
                "int" => new AnimatorIntParameterElement(property, attribute.AnimatorProperty),
                "string" => new AnimatorStringParameterElement(property, attribute.AnimatorProperty),
                _ => throw new ArgumentException("AnimatorParam must be bound to an int or string.")
            };
        }

        public abstract class AnimatorParameterElement : PopupField<string>, IDisposable
        {
            public readonly SerializedProperty Property;
            public string Label => ObjectNames.NicifyVariableName(Property.name);
            public readonly SerializedProperty AnimatorProperty;

            public AnimatorParameterElement(SerializedProperty property, string animatorPropertyName) : base(ObjectNames.NicifyVariableName(property.name))
            {
                Property = property;
                AnimatorProperty = property.serializedObject.FindProperty(animatorPropertyName);
                this.RegisterValueChangedCallback(OnValueChanged);

                OnAnimatorChanged(AnimatorProperty);
                this.TrackPropertyValue(AnimatorProperty, OnAnimatorChanged);

                AddToClassList(BaseField<int>.ussClassName);
                AddToClassList(BaseField<int>.alignedFieldUssClassName);
            }

            private void OnAnimatorChanged(SerializedProperty property)
            {
                if (property.TryGetValue(out Animator animator))
                {
                    choices = new List<string>(GetParameterNames(animator));
                }

                OnAnimatorChanged(animator);
            }

            protected virtual void OnAnimatorChanged(Animator animator) { }

            protected abstract void OnValueChanged(ChangeEvent<string> evt);

            private static string[] GetParameterNames(Animator animator)
            {
                var parameters = animator.parameters;
                var len = parameters.Length;
                var names = new string[len];

                for (int i = 0; i < len; i++)
                {
                    names[i] = parameters[i].name;
                }

                return names;
            }

            public void Dispose()
            {
                this.UnregisterValueChangedCallback(OnValueChanged);
            }
        }

        public class AnimatorIntParameterElement : AnimatorParameterElement
        {
            public AnimatorIntParameterElement(SerializedProperty property, string animatorName) : base(property, animatorName)
            {
            }

            protected override void OnAnimatorChanged(Animator animator)
            {
                for (int i = 0; i < animator.parameterCount; i++)
                {
                    if (animator.GetParameter(i).nameHash == Property.intValue)
                    {
                        index = i;
                        return;
                    }
                }

                index = 0;
            }

            protected override void OnValueChanged(ChangeEvent<string> evt)
            {
                Property.intValue = Animator.StringToHash(evt.newValue);
                Property.serializedObject.ApplyModifiedProperties();
                Property.serializedObject.Update();
            }
        }

        public class AnimatorStringParameterElement : AnimatorParameterElement
        {
            public AnimatorStringParameterElement(SerializedProperty property, string animatorName) : base(property, animatorName)
            {
            }

            protected override void OnAnimatorChanged(Animator animator)
            {
                for (int i = 0; i < animator.parameterCount; i++)
                {
                    if (animator.GetParameter(i).name == Property.stringValue)
                    {
                        index = i;
                        return;
                    }
                }

                index = 0;
            }

            protected override void OnValueChanged(ChangeEvent<string> evt)
            {
                Property.stringValue = evt.newValue;
                Property.serializedObject.ApplyModifiedProperties();
            }
        }
    }
}