using System;
using UnityEditor;
using UnityEngine;

namespace VRCExpressionSetupTool.Editor.Layout
{
    internal static class EditorGUICustomLayout
    {
        /// <summary>
        /// チェックボックスのスタイル。
        /// </summary>
        private static readonly GUIStyle SmallTickbox;

        /// <summary>
        /// オプションアイコンの黒い版
        /// </summary>
        private static readonly Texture2D PaneOptionsIconDark;

        /// <summary>
        /// オプションアイコンの白い版
        /// </summary>
        private static readonly Texture2D PaneOptionsIconLight;

        /// <summary>
        /// 黒ヘッダー
        /// </summary>
        private static readonly Color HeaderBackgroundDarkColor;

        /// <summary>
        /// 白ヘッダー
        /// </summary>
        private static readonly Color HeaderBackgroundLightColor;

        /// <inheritdoc />
        static EditorGUICustomLayout()
        {
            SmallTickbox = new GUIStyle("ShurikenToggle");

            PaneOptionsIconDark = (Texture2D) EditorGUIUtility.Load("Builtin Skins/DarkSkin/Images/pane options.png");
            PaneOptionsIconLight = (Texture2D) EditorGUIUtility.Load("Builtin Skins/LightSkin/Images/pane options.png");

            HeaderBackgroundDarkColor = new Color(0.1f, 0.1f, 0.1f, 0.2f);
            HeaderBackgroundLightColor = new Color(1f, 1f, 1f, 0.2f);
        }

        /// <summary>
        /// ヘッダーのオプションアイコン。
        /// エディタがPro版かで色を変える。
        /// </summary>
        private static Texture2D PaneOptionsIcon =>
            EditorGUIUtility.isProSkin ? PaneOptionsIconDark : PaneOptionsIconLight;

        /// <summary>
        /// ヘッダーの背景色。
        /// エディタがPro版かで色を変える。
        /// </summary>
        private static Color HeaderBackgroundColor =>
            EditorGUIUtility.isProSkin ? HeaderBackgroundDarkColor : HeaderBackgroundLightColor;

        /// <summary>
        /// タイトル付きのカスタム折り畳みグループUI
        /// </summary>
        /// <param name="title">タイトル</param>
        /// <param name="foldField">たたまれているか</param>
        /// <param name="materialPropertyAction">グループ内のコンテンツ</param>
        public static bool PropertyFoldGroup(string title, bool foldField, Action materialPropertyAction)
        {
            var backgroundRect = GUILayoutUtility.GetRect(1f, 17f);

            var labelRect = backgroundRect;
            labelRect.xMin += 32f;
            labelRect.xMax -= 20f;

            var foldoutRect = backgroundRect;
            foldoutRect.y += 1f;
            foldoutRect.width = 13f;
            foldoutRect.height = 13f;

            backgroundRect.xMin = 0f;
            backgroundRect.width += 4f;

            // Background
            EditorGUI.DrawRect(backgroundRect, HeaderBackgroundColor);

            // Title
            EditorGUI.LabelField(labelRect, new GUIContent(title), EditorStyles.boldLabel);

            // foldout
            foldField = GUI.Toggle(foldoutRect, foldField, GUIContent.none, EditorStyles.foldout);

            // Handle events
            var e = Event.current;

            if (e.type == EventType.MouseDown)
            {
                if (labelRect.Contains(e.mousePosition))
                {
                    if (e.button == 0)
                    {
                        foldField = !foldField;
                    }

                    e.Use();
                }
            }

            if (foldField)
            {
                using (new GUILayout.VerticalScope(GUI.skin.box))
                {
                    materialPropertyAction();
                }
            }

            GUILayout.Space(1);

            return foldField;
        }

        /// <summary>
        /// タイトル、トグル付きのカスタム折り畳みグループUI
        /// </summary>
        /// <param name="title">タイトル</param>
        /// <param name="foldField">たたまれているか</param>
        /// <param name="isActive"></param>
        /// <param name="materialPropertyAction">グループ内のコンテンツ</param>
        internal static void PropertyToggleFoldGroup(string title, ref bool foldField, ref bool isActive,
            Action materialPropertyAction)
        {
            var backgroundRect = GUILayoutUtility.GetRect(1f, 17f);

            var labelRect = backgroundRect;
            labelRect.xMin += 32f;
            labelRect.xMax -= 20f;

            var foldoutRect = backgroundRect;
            foldoutRect.y += 1f;
            foldoutRect.width = 13f;
            foldoutRect.height = 13f;

            var toggleRect = backgroundRect;
            toggleRect.x += 16f;
            toggleRect.y += 2f;
            toggleRect.width = 13f;
            toggleRect.height = 13f;

            backgroundRect.xMin = 0f;
            backgroundRect.width += 4f;

            // Background
            EditorGUI.DrawRect(backgroundRect, HeaderBackgroundColor);

            // Title
            using (new EditorGUI.DisabledScope(!isActive))
            {
                EditorGUI.LabelField(labelRect, new GUIContent(title), EditorStyles.boldLabel);
            }

            // foldout
            foldField = GUI.Toggle(foldoutRect, foldField, GUIContent.none, EditorStyles.foldout);

            // Active checkbox
            isActive = GUI.Toggle(toggleRect, isActive, GUIContent.none, SmallTickbox);

            // Handle events
            var e = Event.current;

            if (e.type == EventType.MouseDown)
            {
                if (labelRect.Contains(e.mousePosition))
                {
                    if (e.button == 0)
                    {
                        foldField = !foldField;
                    }

                    e.Use();
                }
            }

            if (foldField)
            {
                using (new EditorGUI.DisabledScope(!isActive))
                using (new GUILayout.VerticalScope(GUI.skin.box))
                {
                    materialPropertyAction();
                }
            }

            GUILayout.Space(1);
        }

        /// <summary>
        /// タイトルにObjectFireld付きのカスタム折り畳みグループUI
        /// </summary>
        /// <param name="title">タイトル</param>
        /// <param name="foldField">たたまれているか</param>
        /// <param name="materialPropertyAction">グループ内のコンテンツ</param>
        public static bool ObjectFieldFoldGroup<T>(ref T content, string title, bool foldField,
            Action materialPropertyAction) where T : UnityEngine.Object
        {
            var backgroundRect = GUILayoutUtility.GetRect(1f, 17f);

            var labelRect = backgroundRect;
            labelRect.xMin += 32f;
            labelRect.xMax -= 20f;

            var foldoutRect = backgroundRect;
            foldoutRect.y += 1f;
            foldoutRect.width = 13f;
            foldoutRect.height = 13f;

            backgroundRect.xMin = 0f;
            backgroundRect.width += 4f;

            // Background
            EditorGUI.DrawRect(backgroundRect, HeaderBackgroundColor);

            content = (T) EditorGUI.ObjectField(labelRect, "", content, typeof(T), false);

            // Title
            EditorGUI.LabelField(labelRect, new GUIContent(title), EditorStyles.boldLabel);

            // foldout
            foldField = GUI.Toggle(foldoutRect, foldField, GUIContent.none, EditorStyles.foldout);

            // Handle events
            var e = Event.current;

            if (e.type == EventType.MouseDown)
            {
                if (labelRect.Contains(e.mousePosition))
                {
                    if (e.button == 0)
                    {
                        foldField = !foldField;
                    }

                    e.Use();
                }
            }

            if (foldField)
            {
                using (new GUILayout.VerticalScope(GUI.skin.box))
                {
                    materialPropertyAction();
                }
            }

            GUILayout.Space(1);

            return foldField;
        }

        /// <summary>
        /// ObjectFieldの簡易版
        /// </summary>
        /// <param name="title"></param>
        /// <param name="content"></param>
        /// <param name="onValueChanged"></param>
        /// <param name="allowSceneObject"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static void ObjectField<T>(string title, T content, bool allowSceneObject, Action<T> onValueChanged, params GUILayoutOption[] options)
            where T : UnityEngine.Object
        {
            EditorGUI.BeginChangeCheck();
            var value = (T) EditorGUILayout.ObjectField(title, content, typeof(T), allowSceneObject, options);
            if (!EditorGUI.EndChangeCheck()) return;
            
            onValueChanged.Invoke(value);
        }

        /// <summary>
        /// ObjectFieldの簡易版
        /// </summary>
        /// <param name="rect"></param>
        /// <param name="title"></param>
        /// <param name="content"></param>
        /// <param name="onValueChanged"></param>
        /// <param name="allowSceneObject"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static void ObjectField<T>(Rect rect, string title, T content, bool allowSceneObject, Action<T> onValueChanged)
            where T : UnityEngine.Object
        {
            EditorGUI.BeginChangeCheck();
            var value = (T) EditorGUI.ObjectField(rect, title, content, typeof(T), allowSceneObject);
            if (!EditorGUI.EndChangeCheck()) return;
            onValueChanged.Invoke(value);
        }
        
        /// <summary>
        /// ObjectFieldの簡易版
        /// </summary>
        /// <param name="title"></param>
        /// <param name="content"></param>
        /// <param name="onValueChanged"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static void EnumPopup<T>(string title, Enum content, Action<T> onValueChanged, params GUILayoutOption[] options) where T : Enum
        {
            EditorGUI.BeginChangeCheck();
            var value = (T)EditorGUILayout.EnumPopup(title, content, options);
            if (!EditorGUI.EndChangeCheck()) return;
            onValueChanged.Invoke(value);
        }

        /// <summary>
        /// ObjectFieldの簡易版
        /// </summary>
        /// <param name="rect"></param>
        /// <param name="title"></param>
        /// <param name="content"></param>
        /// <param name="onValueChanged"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static void EnumPopup<T>(Rect rect, string title, Enum content, Action<T> onValueChanged) where T : Enum
        {
            EditorGUI.BeginChangeCheck();
            var value = (T)EditorGUI.EnumPopup(rect, title, content);
            if (!EditorGUI.EndChangeCheck()) return;
            onValueChanged.Invoke(value);
        }

        /// <summary>
        /// プレファブかどうか
        /// </summary>
        /// <param name="self"></param>
        /// <returns></returns>
        public static bool IsPrefab(this UnityEngine.Object self)
        {
            return PrefabUtility.GetPrefabAssetType(self) != PrefabAssetType.NotAPrefab;
        }
    }
}