// Copyright (C) 2014-2024 Gleechi Technology AB. All rights reserved.

//#define HIGHLIGHT_PLUS
//#define USE_CAKESLICE_OUTLINE // https://github.com/cakeslice/Outline-Effect

using UnityEngine;
using System.Collections.Generic;
#if HIGHLIGHT_PLUS
using HighlightPlus;
#endif
#if USE_CAKESLICE_OUTLINE
using cakeslice;
#endif

namespace VirtualGrasp.Scripts
{

#if UNITY_EDITOR
    using UnityEditor;

    [CustomEditor(typeof(VG_Highlighter))]
    class VG_Highlighter_GUI : Editor
    {
        private class State
        {
            public bool enabled = true;
            public string text = "";
            public string tooltip = "";
            public State(string _text, string _tooltip, bool _enabled = true)
            {
                text = _text;
                tooltip = _tooltip;
                enabled = _enabled;
            }
        }

        private Dictionary<VG_ReturnCode, State> m_states = new Dictionary<VG_ReturnCode, State>() {
        { VG_ReturnCode.OBJECT_NO_BAKE, new State("No bakes.", "These objects have not been baked.") },
        { VG_ReturnCode.OBJECT_NO_GRASPS, new State("Dynamic Grasps", "These objects have been baked and will be available for dynamic grasping.") },
        { VG_ReturnCode.SUCCESS, new State("+ Static Grasps", "These objects have been baked and grasps have been added in GraspStudio.") }
    };

        private List<VG_ReturnCode> state_copy = new List<VG_ReturnCode>();
        private HashSet<VG_ReturnCode> state_selected = new HashSet<VG_ReturnCode>();

        public void OnEnable()
        {
            foreach (var state in m_states)
            {
                state_copy.Add(state.Key);
                if (state.Value.enabled) state_selected.Add(state.Key);
            }
        }

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

            if (!VG_Controller.IsEnabled())
            {
                GUILayout.Label(
                    "Play scene to highlight the state of objects in the scene.",
                    EditorStyles.wordWrappedLabel);
                return;
            }

            GUILayout.Label(
                "Select to highlight the state of objects in the scene.",
                EditorStyles.wordWrappedLabel);

            Color color;
            bool state_changed = false;
            GUIStyle myStyle = new GUIStyle(GUI.skin.button);
            VG_Highlighter highlighter = target as VG_Highlighter;
            EditorGUILayout.BeginHorizontal();
            foreach (VG_ReturnCode code in state_copy)
            {
                color = highlighter.GetColor(code);
                myStyle.normal.textColor = color;
                myStyle.onNormal.textColor = color;
                myStyle.onHover.textColor = color;
                m_states[code].enabled = GUILayout.Toggle(m_states[code].enabled, new GUIContent(m_states[code].text, m_states[code].tooltip), myStyle);
                bool this_enabled = !m_states[code].enabled;
                if (this_enabled ^ state_selected.Contains(code)) state_changed |= true;
                if (this_enabled) state_selected.Add(code);
                else state_selected.Remove(code);
            }
            EditorGUILayout.EndHorizontal();

            if (state_changed) highlighter.HighlightObjectStatus(state_selected);
        }
    }
#endif

    /**
     * VG_Highlighter exemplifies how you could enable object highlighting based on the current hand status.
     * The MonoBehavior provides a tutorial on the VG API functions for some of the VG_Controller event functions, such as OnObjectSelected and OnObjectDeselected.
     */
    [LIBVIRTUALGRASP_UNITY_SCRIPT]
    [HelpURL("https://docs.virtualgrasp.com/unity_component_vghighlighter." + VG_Version.__VG_VERSION__ + ".html")]
    public class VG_Highlighter : MonoBehaviour
    {
        [Tooltip("Set the shader that is used for highlighting.")]
        public Shader m_shader = null;

#if !HIGHLIGHT_PLUS && !USE_CAKESLICE_OUTLINE
        private List<Material>[] m_unhighlightedMaterials = new List<Material>[2];
        private List<Material>[] m_highlightedMaterials = new List<Material>[2];
#endif
        [Tooltip("Set the color that are used for highlighting objects selected by the left hand.")]
        public Color m_leftHandColor = Color.green;
        [Tooltip("Set the color that are used for highlighting objects selected by the right hand.")]
        public Color m_rightHandColor = Color.green;

        // Dictionary to keep track of highlighted objects.
        private Dictionary<VG_HandSide, Transform> m_highlightedObjects = new Dictionary<VG_HandSide, Transform>();

#if USE_CAKESLICE_OUTLINE
    private Outline[] m_outlines = new Outline[2] { new Outline(), new Outline() };
#endif
#if HIGHLIGHT_PLUS
    
#endif

        public Color GetColor(VG_ReturnCode code)
        {
            switch (code)
            {
                case VG_ReturnCode.OBJECT_NO_BAKE: return Color.red;
                case VG_ReturnCode.OBJECT_NO_GRASPS: return Color.green;
                case VG_ReturnCode.SUCCESS: return Color.cyan;
                default: return Color.black;
            }
        }

        void Start()
        {
            m_shader = Resources.Load<Shader>("SH_RimLight");
            if (m_shader == null) Debug.LogWarning("No shader found/assigned to VG_Highlighter. Please assign a shader.", gameObject);

            // Initialize the highlighted objects dictionary.
            m_highlightedObjects[VG_HandSide.LEFT] = null;
            m_highlightedObjects[VG_HandSide.RIGHT] = null;

            m_leftHandColor.a = 0.5f;
            m_rightHandColor.a = 0.5f;

            VG_Controller.OnObjectSelected.AddListener(Highlight);
            VG_Controller.OnObjectDeselected.AddListener(Unhighlight);
            VG_Controller.OnObjectGrasped.AddListener(Unhighlight);
        }

        public void HighlightObjectStatus(HashSet<VG_ReturnCode> states)
        {
            bool isSelected;
            int numSelected = 0;
            int numAll = 0;

            Color color;
            foreach (Transform t in VG_Controller.GetSelectableObjects())
            {
                color = Color.black;

                isSelected = false;
                foreach (VG_ReturnCode state in states)
                {
                    isSelected = VG_Controller.GetUnbakedObjects(state).Contains(t);
                    if (state == VG_ReturnCode.SUCCESS) isSelected = !isSelected;
                    if (isSelected) { color = GetColor(state); break; }
                }
                color.a = 0.5f;

                if (t != null && t.GetComponentInChildren<MeshRenderer>() != null)
                {
                    foreach (Material m in t.GetComponentInChildren<MeshRenderer>().materials)
                    {
                        m.shader = isSelected ? m_shader : Shader.Find("Legacy Shaders/Specular");
                        m.SetFloat("_RimPower", 0.25f);
                        m.SetColor("_RimColor", color);
                    }
                }

                if (isSelected) numSelected++;
                numAll++;
            }

            if (states.Count > 0)
                Debug.Log("Highlighting " + numSelected + " out of " + numAll + " interactable objects.");
        }

        private void Highlight(VG_HandStatus hand)
        {
            //if (VG_Controller.IsPushable(obj) && 
            //    !VG_Controller.IsGraspable(obj))
            //return;

            // If the selected object is already highlighted
            if (hand.m_selectedObject == m_highlightedObjects[hand.m_side])
                return;

            // If the selected object is the same
            if (hand.m_selectedObject == m_highlightedObjects[hand.m_side == VG_HandSide.LEFT ? VG_HandSide.RIGHT : VG_HandSide.LEFT])
                return;

            m_highlightedObjects[hand.m_side] = hand.m_selectedObject;

            int id = hand.m_side < 0 ? 0 : 1;
            Color color = id == 0 ? m_leftHandColor : m_rightHandColor;
#if HIGHLIGHT_PLUS
        HighlightEffect highlight = hand.m_selectedObject.GetComponent<HighlightEffect>();
        if (highlight == null)
        {
            highlight = hand.m_selectedObject.gameObject.AddComponent<HighlightPlus.HighlightEffect>();
            highlight.highlighted = true;
            highlight.glow = 1.0f;
            highlight.overlayAnimationSpeed = 0.0f;
            highlight.overlay = 0.0f;
            highlight.seeThrough = SeeThroughMode.Never;
            highlight.outlineVisibility = Visibility.AlwaysOnTop;
        }
        else highlight.highlighted = true;
        for (int gp = 0; gp < highlight.glowPasses.Length; gp++)
            highlight.glowPasses[gp].color = color;
        highlight.outlineColor = color;
#else
#if USE_CAKESLICE_OUTLINE
        m_outlines[id].Renderer = hand.m_selectedObject.GetComponent<Renderer>();
        m_outlines[id].Enable();
#else
            Material[] objectMaterials = hand.m_selectedObject.GetComponentInChildren<MeshRenderer>().sharedMaterials;
            m_unhighlightedMaterials[id] = new List<Material>(hand.m_selectedObject.GetComponentInChildren<MeshRenderer>().sharedMaterials);
            for (int i = 0, count = m_unhighlightedMaterials[id].Count; i < count; i++)
                m_unhighlightedMaterials[id][i] = new Material(m_unhighlightedMaterials[id][i]);

            m_highlightedMaterials[id] = new List<Material>();
            for (int i = 0, count = m_unhighlightedMaterials[id].Count; i < count; i++)
            {
                m_highlightedMaterials[id].Add(new Material(m_unhighlightedMaterials[id][i]));
                m_highlightedMaterials[id][i].shader = m_shader;
            }

            MeshRenderer objectRenderer = m_highlightedObjects[hand.m_side].GetComponentInChildren<MeshRenderer>();
            for (int i = 0, count = objectRenderer.materials.Length; i < count; i++)
            {
                objectMaterials[i] = m_highlightedMaterials[id][i];
                objectMaterials[i].SetColor("_RimColor", color);
            }

            objectRenderer.sharedMaterials = objectMaterials;
#endif
#endif
        }

        private void Unhighlight(VG_HandStatus hand)
        {
            int id = hand.m_side < 0 ? 0 : 1;
            if (!m_highlightedObjects.ContainsKey(hand.m_side))
                return;

            // Got no object (or the same object as before), got no unhighlight
            Transform highlightedObject = m_highlightedObjects[hand.m_side];
            if (highlightedObject == null) return;

#if HIGHLIGHT_PLUS
        if (hand.m_formerSelectedObject.GetComponent<HighlightPlus.HighlightEffect>() != null)
            hand.m_formerSelectedObject.GetComponent<HighlightPlus.HighlightEffect>().highlighted = false;
#else
#if USE_CAKESLICE_OUTLINE
        m_outlines[id].Disable();
        m_outlines[id].Renderer = null;
#else
            highlightedObject.GetComponentInChildren<MeshRenderer>().sharedMaterials = m_unhighlightedMaterials[id].ToArray();
#endif
#endif
            m_highlightedObjects[hand.m_side] = null;
        }
    }
}