using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif

namespace Jovian.InspectorTools {
    public class AnimatorParameterAttribute : PropertyAttribute {
        public readonly string animatorPropertyName;
        public bool allParameterTypes;
        public AnimatorControllerParameterType parameterType;

        public AnimatorParameterAttribute(string animatorPropertyName) {
            this.animatorPropertyName = animatorPropertyName;
            allParameterTypes = true;
        }

        public AnimatorParameterAttribute(string animatorPropertyName, AnimatorControllerParameterType parameterType) {
            this.animatorPropertyName = animatorPropertyName;
            allParameterTypes = false;
            this.parameterType = parameterType;
        }
    }
}
#if UNITY_EDITOR
namespace Jovian.InspectorTools.Internal {
    [CustomPropertyDrawer(typeof(AnimatorParameterAttribute))]
    public class AnimatorParameterAttributeDrawer : PropertyDrawer {
        private bool hasAnimator;
        private Animator animator;

        private string[] parameterNames;

        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
            label = EditorGUI.BeginProperty(position, label, property);
            position = EditorGUI.PrefixLabel(position, label);

            if(parameterNames == null) {
                var animatorParameterAttribute = (AnimatorParameterAttribute)attribute;
                string animatorFieldName = animatorParameterAttribute.animatorPropertyName;
                parameterNames = UpdateParameterNames(property, animatorFieldName, animatorParameterAttribute.allParameterTypes, animatorParameterAttribute.parameterType);
            }

            EditorGUI.BeginChangeCheck();

            bool didChange = false;
            if(parameterNames is { Length: > 0 }) {
                int parameterIndex = Array.IndexOf(parameterNames, property.stringValue);
                int newIndex = EditorGUI.Popup(position, parameterIndex, parameterNames);
                if(parameterIndex != newIndex) {
                    property.stringValue = parameterNames[newIndex];
                    didChange = true;
                }
            }
            else {
                EditorGUI.PropertyField(position, property, GUIContent.none);
            }

            int indentLevel = EditorGUI.indentLevel;
            EditorGUI.indentLevel = 0;

            if(EditorGUI.EndChangeCheck() || didChange) {
                property.serializedObject.ApplyModifiedProperties();
            }

            EditorGUI.indentLevel = indentLevel;
            EditorGUI.EndProperty();
        }

        private string[] UpdateParameterNames(SerializedProperty property, string animatorFieldName, bool allParameterTypesAllowed, AnimatorControllerParameterType parameterType) {
            int pathIndexOfPropertyName = property.propertyPath.LastIndexOf(property.name, StringComparison.Ordinal);
            bool isTopLevelObject = pathIndexOfPropertyName == 0;
            SerializedProperty animatorProperty;
            if(isTopLevelObject) {
                animatorProperty = property.serializedObject.FindProperty(animatorFieldName);
            }
            else {
                SerializedProperty parentProperty = property.serializedObject.FindProperty(property.propertyPath.Substring(0, pathIndexOfPropertyName - 1));
                animatorProperty = parentProperty.FindPropertyRelative(animatorFieldName);
            }

            if(animatorProperty is { objectReferenceValue: Animator targetAnimator }) {
                if(!targetAnimator.gameObject.activeSelf ||
                    !targetAnimator.gameObject.activeInHierarchy ||
                    targetAnimator.runtimeAnimatorController == null) {
                    return new string[] { }; // return an empty array so it uses the fallback of just text
                }

                AnimatorControllerParameter[] parameters = targetAnimator.parameters;

                if(allParameterTypesAllowed) {
                    return parameters.Select(p => p.name).ToArray();
                }
                else {
                    return parameters.Where(p => p.type == parameterType).Select(p => p.name).ToArray();
                }
            }
            return new[] { "<MISSING>" };
        }
    }
}
#endif