using UnityEditor;
using UnityEngine;

#if UNITY_EDITOR
namespace Jovian.InspectorTools.Internal {

    public interface IGUILayoutPropertyField {
        void GUILayoutPropertyField(SerializedProperty serializedProperty, params GUILayoutOption[] options);
        void GUILayoutPropertyField(SerializedProperty serializedProperty, bool includeChildren, params GUILayoutOption[] options);
        void GUILayoutPropertyField(SerializedProperty serializedProperty, GUIContent label, bool includeChildren, params GUILayoutOption[] options);
    }

    [CustomEditor(typeof(Object), true)] [CanEditMultipleObjects]
    public class UnityObjectEditor : Editor, IGUILayoutPropertyField {
        private FoldoutAttributeHandler foldout;
        private ButtonMethodHandler buttonMethod;
        private ShowInInspectorAttributeHandler showInInspectorHandler;
        private float lastShowInInspectorTime;


        private static ButtonMethodHandler[] previewButtonMethods;
        private static Vector2 previewScroll;
        private Rect previewRect;

        private void OnEnable() {
            if(target == null) {
                return;
            }

            foldout = new FoldoutAttributeHandler(target, serializedObject, this);
            buttonMethod = new ButtonMethodHandler(target);
            showInInspectorHandler = new ShowInInspectorAttributeHandler(target, serializedObject);

            if (previewButtonMethods == null && target is Component component) {
                previewScroll = Vector2.zero;
                Component[] objectComponents = component.gameObject.GetComponents<Component>();
                previewButtonMethods = new ButtonMethodHandler[objectComponents.Length];
                for (int i = 0; i < objectComponents.Length; i++) {
                    previewButtonMethods[i] = new ButtonMethodHandler(objectComponents[i]);
                }
            }

            EditorApplication.update -= OnUpdate;
            EditorApplication.update += OnUpdate;
        }

        private void OnDisable() {
            EditorApplication.update -= OnUpdate;
            foldout?.OnDisable();

            previewButtonMethods = null;
        }

        private void OnUpdate() {
            if(showInInspectorHandler?.HasValues == true) {
                float newShowInInspectorTime = Time.realtimeSinceStartup % 1f;
                if(newShowInInspectorTime < lastShowInInspectorTime) {
                    Repaint();
                }
                lastShowInInspectorTime = newShowInInspectorTime;
            }
        }

        public override void OnInspectorGUI() {
            if (buttonMethod != null && buttonMethod.HasAnyVisibleMethods()) {
                buttonMethod?.OnBeforeInspectorGUI();
                EditorGUILayout.Space();
            }

            if(foldout != null) {
                foldout.Update();
                if(!foldout.OverrideInspector) {
                    base.OnInspectorGUI();
                }
                else {
                    foldout.OnInspectorGUI();
                }
            }

            if(showInInspectorHandler != null) {
                showInInspectorHandler.Update();
                if(showInInspectorHandler.HasValues) {
                    showInInspectorHandler.OnInspectorGUI(this);
                }
            }

            if(buttonMethod != null && buttonMethod.HasAnyVisibleMethods()){
                EditorGUILayout.Space();
                buttonMethod?.OnAfterInspectorGUI();
            }
        }

        private static bool HasAnyVisiblePreviewMethods() {
            if (previewButtonMethods == null) {
                return false;
            }

            foreach (ButtonMethodHandler previewButtonMethod in previewButtonMethods) {
                if (previewButtonMethod.HasAnyVisibleMethods()) {
                    return true;
                }
            }

            return false;
        }

        public override bool HasPreviewGUI() => targets != null && targets.Length == 1 && target != null && HasAnyVisiblePreviewMethods();

        public override void OnPreviewGUI(Rect r, GUIStyle background) {
            // Only Repaint has the true Rect size - so store it whenever we repaint.
            if (Event.current.type == EventType.Repaint) {
                previewRect = r;
            }
            GUILayout.BeginArea(previewRect);
            previewScroll = GUILayout.BeginScrollView(previewScroll);

            foreach (ButtonMethodHandler previewButtonMethod in previewButtonMethods) {
                if (previewButtonMethod.HasAnyVisibleMethods()) {
                    GUILayout.BeginVertical(EditorStyles.helpBox);
                    GUILayout.Label(ObjectNames.NicifyVariableName(previewButtonMethod.Target.GetType().Name), EditorStyles.boldLabel);
                    previewButtonMethod.OnBeforeInspectorGUI();
                    previewButtonMethod.OnAfterInspectorGUI();
                    GUILayout.EndVertical();
                }
            }

            GUILayout.EndScrollView();
            GUILayout.EndArea();
        }

        public void GUILayoutPropertyField(SerializedProperty serializedProperty, params GUILayoutOption[] options) {
            GUILayoutPropertyField(serializedProperty, true, options);
        }

        public void GUILayoutPropertyField(SerializedProperty serializedProperty, bool includeChildren, params GUILayoutOption[] options) {
            GUILayoutPropertyField(serializedProperty, new GUIContent(ObjectNames.NicifyVariableName(serializedProperty.name), serializedProperty.tooltip), includeChildren, options);
        }

        public void GUILayoutPropertyField(SerializedProperty serializedProperty, GUIContent label, bool includeChildren, params GUILayoutOption[] options) {
            if (serializedProperty.isArray) {
                ArrayAttributePropertyHandler.DrawArrayProperty(serializedProperty, label, includeChildren, options);
            }
            else {
                EditorGUILayout.PropertyField(serializedProperty, label, includeChildren, options);
            }
        }
    }
}
#endif
