using UnityEngine;

#if UNITY_EDITOR
using System.Reflection;
using UnityEditor;
using Jovian.InspectorTools.EditorTools;
#endif

namespace Jovian.InspectorTools {
    /// <summary>
    /// Does not work on objects with children - will break their UI
    /// </summary>
    public class RequireMethodAttribute : PropertyAttribute, IPropertyCondition {

        private readonly string methodName;

        public RequireMethodAttribute(string methodName) {
            this.methodName = methodName;
        }

#if UNITY_EDITOR
        public bool DoesPropertyMeetCondition(SerializedProperty property, out string description) {
            Object target = property.serializedObject.targetObject;
            MethodInfo methodInfo = target.GetType().GetMethod(methodName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
            description = string.Empty;
            if (methodInfo != null && methodInfo.ReturnParameter?.ParameterType == typeof(bool)) {
                ParameterInfo[] methodParameters = methodInfo.GetParameters();

                object[] parameterValues = null;
                if (methodParameters.Length == 1 && methodParameters[0].ParameterType == property.GetValue().GetType()) {
                    parameterValues = new[] { property.GetValue() };
                }

                object returnValue = methodInfo.Invoke(target, parameterValues);
                if (returnValue is bool boolReturnValue) {
                    if (!boolReturnValue) {
                        description = $"{property.name} is failing {methodName}.";
                        return false;
                    }

                    return true;
                }
                else {
                    description = $"{property.name} {methodName} is invalid - does not return a boolean.";
                    return false;
                }
            }

            description = $"{property.name} {methodName} is invalid - no method can be found on {target}.";
            return false;
        }
#endif

    }
}

#if UNITY_EDITOR
namespace Jovian.InspectorTools.Internal {
    [CustomPropertyDrawer(typeof(RequireMethodAttribute))]
    public class RequireMethodAttributePropertyDrawer : PropertyDrawer {

        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
            RequireMethodAttribute requireMethodAttribute = (RequireMethodAttribute)attribute;

            if (property.hasChildren) {
                EditorGUI.LabelField(position, new GUIContent(property.displayName), new GUIContent("RequireMethod GUI does not work on properties with children"));
                return;
            }

            Color guiColor = GUI.color;

            if (!requireMethodAttribute.DoesPropertyMeetCondition(property, out string description)) {
                RequiredUtil.LayoutRequired(ref position, description, false);
                RequiredUtil.DrawRequired(ref position, description, false);
            }

            label = EditorGUI.BeginProperty(position, label, property);
            position = EditorGUI.PrefixLabel(position, label);

            EditorGUI.BeginChangeCheck();

            EditorGUI.PropertyField(position, property, GUIContent.none);

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

            EditorGUI.EndProperty();

            GUI.color = guiColor;
        }

    }
}
#endif