using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.Linq;
using UnityEngine.UI;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using System;
using System.Reflection;


namespace SimplifyXR
{

    [CustomEditor(typeof(StandardAssetInfo))]
    public class StandardToolboxAssetInfoInspector : ToolboxAssetInfoInspector { }

    [CustomEditor(typeof(HoverDepth))]
    public class HoverDepthEditor : SimpleAREditor {

        public override void NodeInspector(float width, bool showInfo) { }
    }

    [CustomEditor(typeof(BasePanelFeatures), true), CanEditMultipleObjects]
    public class BasePanelFeatureEditor : SimpleAREditor
    {
        BasePanelFeatures features;
        string docs;
        List<DirectiveCategory> directiveInfo;

        public new void OnEnable()
        {
            features = (BasePanelFeatures)target;
            //selectedObject = target as T;
            docs = features?.gameObject.GetComponent<StandardAssetInfo>().documentationLink;
            directiveInfo = PanelHelpers.GetCompatableDirectiveInfo(features);
        }

        public new void OnSceneGUI() { }


        public override void OnInspectorGUI()
        {
            base.OnInspectorGUI();

            if (!Application.isPlaying)
            {
                PanelHelpers.PanelDocs(docs);
                PanelHelpers.DisplayDirectiveInfo(directiveInfo);
                Repaint();
            }
        }

        public override void NodeInspector(float width, bool showInfo) { }
    }


    public static class PanelHelpers
    {

        public static GUISkin skin;

        public static void PanelDocs(
            string docLink)
        {
            skin = skin == null ? 
                Resources.Load<GUISkin>("Utilities/AppManager/AppManagerSkin") : skin; 

            Styles.HorizontalBlock(() =>
            {
                if (GUILayout.Button("Learn",
                    skin.button
                   ))
                {
                    if (docLink != null && docLink.Length != 0)
                        Application.OpenURL(docLink);
                    else
                        Debug.LogWarning("Panel does not contain documentation URL");
                }
            }, skin.box);
        }

        public static void DisplayDirectiveInfo(List<DirectiveCategory> info)
        {

            if(info != null && info.Count > 0)
            {
                GUILayout.BeginVertical(skin.box);
                GUILayout.Label("Supported Directives", EditorStyles.boldLabel);


                info?.ForEach(info =>
                {
                    GUILayout.BeginVertical(skin.box);

                    GUILayout.BeginHorizontal();
                    GUILayout.Label(info.prettyName, EditorStyles.boldLabel);
                    GUILayout.Label(info.myCategory.ToString(), EditorStyles.helpBox);
                    GUILayout.Label(info.subCategory.ToString(), EditorStyles.helpBox);
                    GUILayout.FlexibleSpace();

                    GUILayout.EndHorizontal();
                    GUILayout.Label(info.directiveInfo, LabelText());
                    GUILayout.EndVertical();
                });

                GUILayout.EndVertical();
            }         
        }

        static GUIStyle LabelText()
        {
            var gs = new GUIStyle();
            gs.richText = true;
            gs.normal.textColor = Color.white;
            gs.wordWrap = true;
            return gs;
        }


        public static List<DirectiveCategory> GetCompatableDirectiveInfo<T>(T objectType)
        {
            var type = typeof(T);
            var objType = objectType.GetType();
            List<Type> compatibleDirectives = new List<Type>();
            List<DirectiveCategory> info = new List<DirectiveCategory>();

            Assembly.GetAssembly(typeof(T))?.GetTypes()?.ToList()?.ForEach(t =>
            {
                //if (t.IsSubclassOf(typeof(Directive)) 
                //    || t.IsSubclassOf(typeof(Initiator)) )
                //{
                    t.GetFields().ToList().ForEach(f =>
                    {
                        if (f.FieldType.Equals(objType))
                        {
                            compatibleDirectives.Add(t);
                        }
                    });
                //}
            });

            compatibleDirectives?.ForEach(dir =>
            {
                var dirInfo = dir.GetCustomAttribute<DirectiveCategory>();
                if (dirInfo != null) info.Add(dirInfo);
            });


            return info;
        }
    }


    [CustomEditor(typeof(StandardPanel), true), CanEditMultipleObjects]
    public class StandardPanelEditor : SimpleAREditor
    {
        public StandardPanel panel;

        public Dictionary<string, bool> displayElements;
        public Dictionary<XRCompatibility, IXRPanelConfig> configurations;

        const string docs = 
            "https://simplearpro.z13.web.core.windows.net/simpleARPro/contentdevelopmentguide/#standard-panel-component";

        public new void OnEnable()
        {
            base.OnEnable();
            panel = (StandardPanel)target;

            var root = panel.GetComponentInChildren<ElementRoot>();
            GenerateDisplayElements(root);
            //UpdateConfigurations();
        }

        public override void OnInspectorGUI()
        {
            base.OnInspectorGUI();
            UpdatePanel();

            if (!Application.isPlaying)
            {
                PanelHelpers.PanelDocs(docs);
                Repaint();
            }
           
            //ResetStyles();
        }       


        private void GenerateDisplayElements(ElementRoot root)
        {
            displayElements = new Dictionary<string, bool>();
            var elements = root.GetDisplayOptionalElements();
            elements.ForEach(e => {
                displayElements.Add(e.id, true);
            });

            if (panel.displayElements == null || 
                panel.displayElements.Count != elements.Count)
            {
                panel.displayElements = new List<DisplayElement>();
                displayElements.Keys.ToList().ForEach(key =>
                {
                    panel.displayElements.Add(new DisplayElement(
                       key,
                       displayElements[key])
                        );
                });
            }

        }


        public override void NodeInspector(float width, bool showInfo)
        {
        }

        public void UpdatePanel()
        {

            if (!Application.isPlaying)
            {
                panel = panel == null ?
              (StandardPanel)target : panel;

                var root = panel.GetComponentInChildren<ElementRoot>();
                if (root != null)
                {
                    try
                    {
                        UpdatePanelAspect(root, panel);
                        UpdatePanelDepth(root);
                        UpdateColors(root);
                        CreateDisplayElementsUI(root);
                        panel.UpdateHoverDepth(root);
                        if(GUI.changed) UpdateConfigurations();
                    }
                    catch (System.Exception e)
                    {
                        Debug.Log(e);
                    }

                }
            }           
        }

        private void UpdateConfigurations()
        {
            configurations = configurations == null ?
                panel.GetAllConfigurations() :
                configurations;

            panel.HandleConfigurations(configurations);
        }




        private void UpdatePanelDepth(ElementRoot root)
        {
            var transforms = root.GetComponentsInChildren<RectTransform>(true);
            float maxDepth = 0;
            transforms.ToList().ForEach(t => {
                if (t.GetSiblingIndex() > maxDepth) maxDepth = t.GetSiblingIndex();
            });
            transforms.ToList().ForEach(trans =>
            {
                if (trans.gameObject.GetComponent<ElementRoot>()) return;

                var index = trans.GetSiblingIndex();

                trans.localPosition = new Vector3(
                    trans.localPosition.x,
                    trans.localPosition.y,
                    0f - panel.depthCurve.Evaluate((float)index / maxDepth)
                        * (panel.panelDepth * 50f)
                        * trans.localScale.x);
            });
        }

        private void UpdatePanelAspect(ElementRoot root, StandardPanel panel)
        {
            var trans = root.gameObject.GetComponent<RectTransform>();
            var startWidth = trans.rect.width;
            var startHeight = trans.rect.height;

            switch (panel.panelAspect)
            {
                case PanelAspect.square:
                    trans.sizeDelta = new Vector3(
                        startHeight,
                        startHeight
                        );
                    break;
                case PanelAspect.wide:
                    trans.sizeDelta = new Vector3(
                     startHeight * 1.5f,
                     startHeight
                     );
                    break;
                case PanelAspect.custom:                    
                    var hratio = panel.panelAspectProperties.horizontal
                        / panel.panelAspectProperties.vertical;
                    if (float.IsInfinity(hratio) || float.IsNaN(hratio))
                    {
                        //don't use calculated ratio until we have a valid float
                        hratio = 1.0f;
                        trans.sizeDelta = new Vector3(
                        startHeight * hratio,
                        startHeight);
                    }
                    else if (hratio > 0)
                    {
                        trans.sizeDelta = new Vector3(
                        startHeight * hratio,
                        startHeight);
                    }                  
                    break;
            }
        }       


        private void UpdateColorByClass(IThemable element)
        {
            if (element.GetThemeClass().Equals(ThemeClass.None))  return;


            if (element.GetThemeObject().GetType()
                .IsAssignableFrom(typeof(Image)))
            {
                var e = (Image)element.GetThemeObject();
                e.color = panel.panelTheme.GetColorByClass(element.GetThemeClass());
                e.sprite = element.IsImageThemeable() ?
                    panel.panelTheme.GetSpriteByClass(element.GetThemeClass()) :
                    e.sprite;
                EditorUtility.SetDirty(e);
            }

            if (element.GetThemeObject().GetType()
               .IsAssignableFrom(typeof(Button)))
            {
                var e = (Button)element.GetThemeObject();
                e.colors = panel.panelTheme.UpdateColorBlock(e.colors);
                EditorUtility.SetDirty(e);
            }
        }

        private void UpdateColors(ElementRoot root)
        {
            root.elements.ForEach(e => UpdateColorByClass(e));
            var themableElements = panel.gameObject.GetComponentsInChildren<IThemable>();
            themableElements.ToList().ForEach(te => UpdateColorByClass(te));

        }

        public void CreateDisplayElementsUI(ElementRoot root)
        {

            if (panel.customizeDisplay)
            {
                EditorGUI.indentLevel++;

                var elements = root.GetDisplayOptionalElements();
                GenerateDisplayElements(root);
                elements.ForEach(e =>
                {
                    GUILayout.BeginHorizontal();
                    GUILayout.Label("Display " + e.id, GUILayout.Width(150));
                    bool display =
                    GUILayout.Toggle(panel.GetDisplayElementByID(e.id).display, "");
                    GUILayout.EndHorizontal();
                    panel.UpdateDisplayElementByID(e.id, display);
                    if (e.element.GetType().IsSubclassOf(typeof(MonoBehaviour)))
                    {
                        var component = (MonoBehaviour)e.element;
                        component.gameObject.SetActive(display);
                    }
                });

                if (GUI.changed) EditorUtility.SetDirty(panel);

            }         
        }


        private void ResetStyles()
        {

            if (GUILayout.Button("Reset Colors"))
            {
                panel.panelTheme.buttonColors.buttonColor =
               new Color(0.5882353f, 0.7882354f, 0.2392157f, 1.0f);
                panel.panelTheme.buttonColors.buttonHoverColor =
                new Color(0.2392157f, 0.7648195f, 0.7882353f, 1.0f);
                panel.panelTheme.primaryColor =
                new Color(0.02745098f, 0.1686275f, 0.3372549f, 1f);
                panel.panelTheme.secondaryColor =
                new Color(1f, 1f, 1f, 1f);
            }
        }

    }

}
