using UnityEngine;
using UnityEditor;
using UnityEditor.Rendering;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace HorizonBasedAmbientOcclusion
{
    [CustomEditor(typeof(HBAO))]
    public class HBAOEditor : Editor
    {
        private HBAO m_HBAO;
        private Texture2D m_HBAOTex;
        private GUIStyle m_SettingsGroupStyle;
        private GUIStyle m_TitleLabelStyle;
        private int m_SelectedPreset;
        // settings group <setting, property reference>
        private Dictionary<FieldInfo, List<SerializedProperty>> m_GroupFields = new Dictionary<FieldInfo, List<SerializedProperty>>();
        private readonly Dictionary<int, HBAO.Preset> m_Presets = new Dictionary<int, HBAO.Preset>()
        {
            { 0, HBAO.Preset.Normal },
            { 1, HBAO.Preset.FastPerformance },
            { 2, HBAO.Preset.FastestPerformance },
            { 3, HBAO.Preset.Custom },
            { 4, HBAO.Preset.HighQuality },
            { 5, HBAO.Preset.HighestQuality }
        };


        void OnEnable()
        {
            m_HBAO = (HBAO)target;
            m_HBAOTex = Resources.Load<Texture2D>("hbao");

            var settingsGroups = typeof(HBAO).GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
                    .Where(x => x.GetCustomAttributes(typeof(HBAO.SettingsGroup), false).Any());
            foreach (var group in settingsGroups)
            {
                foreach (var setting in group.FieldType.GetFields(BindingFlags.Instance | BindingFlags.Public))
                {
                    if (!m_GroupFields.ContainsKey(group))
                        m_GroupFields[group] = new List<SerializedProperty>();

                    var property = serializedObject.FindProperty(group.Name + "." + setting.Name);
                    if (property != null)
                        m_GroupFields[group].Add(property);
                }
            }

            m_SelectedPreset = m_Presets.Values.ToList().IndexOf(m_HBAO.GetCurrentPreset());
        }

        public override void OnInspectorGUI()
        {
            serializedObject.Update();

            SetStyles();

            EditorGUILayout.BeginVertical();
            {
                // header
                GUILayout.Space(10.0f);
                GUILayout.Label(m_HBAOTex, m_TitleLabelStyle, GUILayout.ExpandWidth(true));

                //if (m_HBAO.GetComponents<MonoBehaviour>()[0] != m_HBAO)
                //{
                //GUILayout.Space(6.0f);
                //EditorGUILayout.HelpBox("This Post FX should be one of the first in your effect stack", MessageType.Info);
                //}

                Event e = Event.current;

                // settings groups
                foreach (var group in m_GroupFields)
                {
                    var groupProperty = serializedObject.FindProperty(group.Key.Name);
                    if (groupProperty == null)
                        continue;

                    GUILayout.Space(6.0f);
                    Rect rect = GUILayoutUtility.GetRect(16f, 22f, m_SettingsGroupStyle);
                    GUI.Box(rect, ObjectNames.NicifyVariableName(groupProperty.displayName), m_SettingsGroupStyle);
                    if (e.type == EventType.MouseDown && rect.Contains(e.mousePosition))
                    {
                        groupProperty.isExpanded = !groupProperty.isExpanded;
                        e.Use();
                    }

                    if (!groupProperty.isExpanded)
                        continue;

                    // presets is a special case
                    if (group.Key.FieldType == typeof(HBAO.Presets))
                    {
                        GUILayout.Space(6.0f);
                        m_SelectedPreset = GUILayout.SelectionGrid(m_SelectedPreset, m_Presets.Values.Select(x => ObjectNames.NicifyVariableName(x.ToString())).ToArray(), 3);
                        GUILayout.Space(6.0f);
                        if (GUILayout.Button("Apply Preset"))
                        {
                            Undo.RecordObject(target, "Apply Preset");
                            m_HBAO.ApplyPreset(m_Presets[m_SelectedPreset]);
                            EditorUtility.SetDirty(target);
                            if (!EditorApplication.isPlaying)
                            {
                                UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(UnityEngine.SceneManagement.SceneManager.GetActiveScene());
                            }
                        }
                    }

                    foreach (var field in group.Value)
                    {
                        // hide real presets
                        if (group.Key.FieldType == typeof(HBAO.Presets))
                            continue;

                        // hide resolution when deinterleaved HBAO is on
                        if (group.Key.FieldType == typeof(HBAO.GeneralSettings) && field.name == "resolution")
                        {
                            if (m_HBAO.GetDeinterleaving() != HBAO.Deinterleaving.Disabled)
                            {
                                continue;
                            }
                        }
                        // warn about deinterleaving not supported with SPSR
                        else if (group.Key.FieldType == typeof(HBAO.GeneralSettings) && field.name == "debugMode")
                        {
#if UNITY_2019_3_OR_NEWER
                        if (m_HBAO.GetDeinterleaving() != HBAO.Deinterleaving.Disabled &&
                            IsVrRunning() && PlayerSettings.stereoRenderingPath == StereoRenderingPath.SinglePass)
                        {
                            GUILayout.Space(6.0f);
                            EditorGUILayout.HelpBox("Deinterleaving is not supported with Single Pass Stereo Rendering...", MessageType.Warning);
                        }
#else
                            if (m_HBAO.GetDeinterleaving() != HBAO.Deinterleaving.Disabled &&
                                PlayerSettings.virtualRealitySupported && PlayerSettings.stereoRenderingPath == StereoRenderingPath.SinglePass)
                            {
                                GUILayout.Space(6.0f);
                                EditorGUILayout.HelpBox("Deinterleaving is not supported with Single Pass Stereo Rendering...", MessageType.Warning);
                            }
#endif
                        }
                        // hide noise type when deinterleaved HBAO is on
                        else if (group.Key.FieldType == typeof(HBAO.GeneralSettings) && field.name == "noiseType")
                        {
                            if (m_HBAO.GetDeinterleaving() != HBAO.Deinterleaving.Disabled)
                            {
                                continue;
                            }
                        }
                        // hide useMultiBounce setting in BeforeReflections integration stage
                        else if (group.Key.FieldType == typeof(HBAO.AOSettings) && field.name == "useMultiBounce")
                        {
                            if (m_HBAO.GetPipelineStage() == HBAO.PipelineStage.BeforeReflections)
                            {
                                continue;
                            }
                        }
                        // hide multiBounceInfluence setting when not used
                        else if (group.Key.FieldType == typeof(HBAO.AOSettings) && field.name == "multiBounceInfluence")
                        {
                            if (m_HBAO.GetPipelineStage() == HBAO.PipelineStage.BeforeReflections || !m_HBAO.UseMultiBounce())
                            {
                                continue;
                            }
                        }
                        // warn about distance falloff greater than max distance
                        else if (group.Key.FieldType == typeof(HBAO.AOSettings) && field.name == "perPixelNormals")
                        {
                            if (m_HBAO.GetAoDistanceFalloff() > m_HBAO.GetAoMaxDistance())
                            {
                                GUILayout.Space(6.0f);
                                EditorGUILayout.HelpBox("Distance Falloff shoudn't be superior to Max Distance", MessageType.Warning);
                            }
                        }
                        // hide albedoMultiplier when not in deferred
                        else if (group.Key.FieldType == typeof(HBAO.ColorBleedingSettings) && field.name == "albedoMultiplier")
                        {
                            if (m_HBAO.GetPipelineStage() == HBAO.PipelineStage.BeforeImageEffectsOpaque)
                                continue;
                        }

                        EditorGUILayout.BeginHorizontal();
                        GUILayout.Space(12.0f);
                        EditorGUILayout.PropertyField(field);
                        EditorGUILayout.EndHorizontal();
                    }
                }
            }
            EditorGUILayout.EndVertical();

            serializedObject.ApplyModifiedProperties();
        }

        private void SetStyles()
        {
            // set banner label style
            m_TitleLabelStyle = new GUIStyle(GUI.skin.label);
            m_TitleLabelStyle.alignment = TextAnchor.MiddleCenter;
            m_TitleLabelStyle.contentOffset = new Vector2(0f, 0f);

            // get shuriken module title style
            GUIStyle skurikenModuleTitleStyle = "ShurikenModuleTitle";

            // clone it as to not interfere with the original, and adjust it
            m_SettingsGroupStyle = new GUIStyle(skurikenModuleTitleStyle);
            m_SettingsGroupStyle.font = (new GUIStyle("Label")).font;
            m_SettingsGroupStyle.fontStyle = FontStyle.Bold;
            m_SettingsGroupStyle.border = new RectOffset(15, 7, 4, 4);
            m_SettingsGroupStyle.fixedHeight = 22;
            m_SettingsGroupStyle.contentOffset = new Vector2(10f, -2f);
        }

#if UNITY_2019_3_OR_NEWER
        List<UnityEngine.XR.XRDisplaySubsystemDescriptor> displaysDescs = new List<UnityEngine.XR.XRDisplaySubsystemDescriptor>();
        List<UnityEngine.XR.XRDisplaySubsystem> displays = new List<UnityEngine.XR.XRDisplaySubsystem>();

        private bool IsVrRunning()
        {
            bool vrIsRunning = false;
            displays.Clear();
            SubsystemManager.GetSubsystems(displays);
            foreach (var displaySubsystem in displays)
            {
                if (displaySubsystem.running)
                {
                    vrIsRunning = true;
                    break;
                }
            }

            return vrIsRunning;
        }
#endif

        [CustomPropertyDrawer(typeof(HBAO.MinMaxSliderAttribute))]
        public class MinMaxSliderDrawer : PropertyDrawer
        {

            public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
            {

                if (property.propertyType == SerializedPropertyType.Vector2)
                {
                    Vector2 range = property.vector2Value;
                    float min = range.x;
                    float max = range.y;
                    var attr = attribute as HBAO.MinMaxSliderAttribute;
                    EditorGUI.BeginChangeCheck();
                    EditorGUI.MinMaxSlider(position, label, ref min, ref max, attr.min, attr.max);
                    if (EditorGUI.EndChangeCheck())
                    {
                        range.x = min;
                        range.y = max;
                        property.vector2Value = range;
                    }
                }
                else
                {
                    EditorGUI.LabelField(position, label, "Use only with Vector2");
                }
            }
        }
    }
}
