using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Jovian.Utilities;
using UnityEngine;

namespace Jovian.InspectorTools
{
    /// <summary>
    /// Finds all objects in the project of 'objectType' then creates a dropdown showing all values matching 'memberName'
    /// E.g. Find all 'ScriptableObject' and list their 'name' values in a list.
    /// </summary>
    public class ObjectsPropertyAttribute : PropertyAttribute
    {
        public readonly Type objectType;
        public readonly string memberName;

        public ObjectsPropertyAttribute(Type objectType, string memberName)
        {
            this.objectType = objectType;
            this.memberName = memberName;
        }
    }
}

#if UNITY_EDITOR
namespace Jovian.InspectorTools.Internal
{
    using UnityEditor;
    using System.Reflection;

    [CustomPropertyDrawer(typeof(ObjectsPropertyAttribute))]
    public class ObjectsPropertyAttributeDrawer : PropertyDrawer
    {
        private bool isInitialized;
        private bool hasValidTarget;
        private string[] values;

        private int selectedIndex = -1;

        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
        {
            if (property.propertyType != SerializedPropertyType.String)
            {
                label.text = $"(!) {label.text}";
                label.tooltip = $"ObjectPropertiesAttribute only works on strings.\n\n{label.tooltip}";
                EditorGUI.PropertyField(position, property, label);
                return;
            }

            if (!isInitialized)
            {
                Initialize(property.stringValue);
            }

            EditorGUI.BeginChangeCheck();
            selectedIndex = Array.IndexOf(values, property.stringValue);
            selectedIndex = EditorGUI.Popup(position, label.text, selectedIndex, values);
            if (EditorGUI.EndChangeCheck())
            {
                property.stringValue = values[selectedIndex];
                property.serializedObject.ApplyModifiedProperties();
            }
        }

        private void Initialize(string currentValue)
        {
            ObjectsPropertyAttribute objectsPropertyAttribute = (ObjectsPropertyAttribute)attribute;

            List<UnityEngine.Object> objectsOfType =
                AssetUtility.FindAllAssetsInProject(objectsPropertyAttribute.objectType);

            const BindingFlags searchFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy | BindingFlags.Instance;
            FieldInfo field = objectsPropertyAttribute.objectType.GetFields(searchFlags)
                .FirstOrDefault(memberInfo => memberInfo.Name == objectsPropertyAttribute.memberName);
            PropertyInfo property = objectsPropertyAttribute.objectType.GetProperties(searchFlags)
                .FirstOrDefault(memberInfo => memberInfo.Name == objectsPropertyAttribute.memberName);

            if (field != null)
            {
                values = objectsOfType.Select(obj => field.GetValue(obj).ToString()).ToArray();
            }
            else if (property != null)
            {
                values = objectsOfType.Select(obj => property.GetValue(obj).ToString()).ToArray();
            }
            else
            {
                values = new string[] { };
                Debug.LogError($"Cannot find member '{objectsPropertyAttribute.memberName}' for type '{objectsPropertyAttribute.objectType.Name}'");
            }

            isInitialized = true;
            selectedIndex = Array.IndexOf(values, currentValue);
        }
    }
}
#endif
