// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using HoloToolkit.Unity;
using System.Collections;
using UnityEngine;

namespace HoloToolkit.Unity.Buttons
{
    [ExecuteInEditMode]
    [RequireComponent(typeof(CompoundButton))]
    public class CompoundButtonIcon : ProfileButtonBase<ButtonIconProfile>
    {
        [Header("Icon Settings")]
        [SerializeField]
        [DropDownComponent]
        private MeshRenderer targetIconRenderer = null;

        [Tooltip("Turns off the icon entirely")]
        public bool DisableIcon = false;

        [Tooltip("Disregard the icon in the profile and use Icon Override instead")]
        public bool OverrideIcon = false;

        [SerializeField]
        [HideInMRTKInspector]
        private string iconName;

        [SerializeField]
        [ShowIfBoolValue("OverrideIcon")]
        [Tooltip("Icon to use for override")]
        private Texture2D iconOverride = null;

        [SerializeField]
        [Tooltip("Alpha value for the text mesh component")]
        private float alpha = 1f;

        private Material instantiatedMaterial;
        private Mesh instantiatedMesh;
        private bool updatingAlpha = false;
        private float alphaTarget = 1f;

        private const float AlphaThreshold = 0.01f;

        /// <summary>
        /// Property to use in IconMaterial for alpha control
        /// Useful for animating icon transparency
        /// </summary>
        public float Alpha
        {
            get
            {
                return alphaTarget;
            }
            set
            {
                if (alphaTarget != value)
                {
                    alphaTarget = value;
                    if (Application.isPlaying)
                    {                        
                        if (Mathf.Abs (alpha - alphaTarget) < AlphaThreshold)
                        {
                            return;
                        }

                        if (updatingAlpha)
                        {
                            return;
                        }

                        if (gameObject.activeSelf && gameObject.activeInHierarchy)
                        {
                            // Update over time
                            updatingAlpha = true;
                            StartCoroutine(UpdateAlpha());
                        }
                        else
                        {
                            // If we're not active, just set the alpha immediately
                            alpha = alphaTarget;
                            RefreshAlpha();
                        }
                    }
                    else
                    {
                        alphaTarget = value;
                        alpha = alphaTarget;
                        RefreshAlpha();
                    }
                }
            }
        }

        public MeshFilter IconMeshFilter
        {
            get
            {
                return targetIconRenderer != null ? targetIconRenderer.GetComponent<MeshFilter>() : null;
            }
        }
        
        #if UNITY_EDITOR
        /// <summary>
        /// Called by CompoundButtonSaveInterceptor
        /// Prevents saving a scene with instanced materials / meshes
        /// </summary>
        public void OnWillSaveScene()
        {
            ClearInstancedAssets();

            SetIconName(iconName);
            
        }
        #endif
        
        public string IconName
        {
            get
            {
                return iconName;
            }
            set
            {
                SetIconName(value);
            }
        }

        private void SetIconName(string newName)
        {
            // Avoid exploding if possible
            if (Profile == null) {
                return;
            }

            if (targetIconRenderer == null) {
                return;
            }

            if (DisableIcon) {
                targetIconRenderer.enabled = false;
                return;
            }

            if (Profile.IconMaterial == null || Profile.IconMesh == null) {
                return;
            }

            // Instantiate our local material now, if we don't have one
            if (instantiatedMaterial == null)
            {
                instantiatedMaterial = new Material(Profile.IconMaterial);
                instantiatedMaterial.name = Profile.IconMaterial.name;
            }
            targetIconRenderer.sharedMaterial = instantiatedMaterial;
            
            // Instantiate our local mesh now, if we don't have one
            if (instantiatedMesh == null)
            {
                instantiatedMesh = Instantiate(Profile.IconMesh);
                instantiatedMesh.name = Profile.IconMesh.name;
            }
            IconMeshFilter.sharedMesh = instantiatedMesh;

            if (OverrideIcon)
            {
                // Use the default mesh for override icons
                targetIconRenderer.enabled = true;
                IconMeshFilter.sharedMesh = Profile.IconMesh;
                IconMeshFilter.transform.localScale = Vector3.one;
                instantiatedMaterial.mainTexture = iconOverride;
                return;
            }

            // Disable the renderer if the name is empty
            if (string.IsNullOrEmpty(newName))
            {
                targetIconRenderer.enabled = false;
                iconName = newName;
                return;
            }            

            // Moment of truth - try to get our icon
            if (!Profile.GetIcon(newName, targetIconRenderer, IconMeshFilter, true))
            {
                targetIconRenderer.enabled = false;
                return;
            }

            // If we've made it this far we're golden
            iconName = newName;
            targetIconRenderer.enabled = true;
            RefreshAlpha();
        }

        private void OnDisable()
        {
            ClearInstancedAssets();
        }

        private void OnEnable()
        {
            if (Application.isPlaying)
            {
                ClearInstancedAssets();
            }

            SetIconName(iconName);
        }

        private void Start()
        {
            SetIconName(iconName);
        }

        private void RefreshAlpha()
        {
            string alphaColorProperty = string.IsNullOrEmpty(Profile.AlphaColorProperty) ? "_Color" : Profile.AlphaColorProperty;
            if (instantiatedMaterial != null)
            {
                Color c = instantiatedMaterial.GetColor(alphaColorProperty);
                c.a = alpha;
                instantiatedMaterial.SetColor(alphaColorProperty, c);
            }
        }

        private void ClearInstancedAssets()
        {
            // Prevent material leaks
            if (instantiatedMaterial != null)
            {
                if (Application.isPlaying)
                {
                    Destroy(instantiatedMaterial);
                }
                else
                {
                    DestroyImmediate(instantiatedMaterial);
                }

                instantiatedMaterial = null;
            }
            if (instantiatedMesh != null)
            {
                if (Application.isPlaying)
                {
                    Destroy(instantiatedMesh);
                }
                else
                {
                    DestroyImmediate(instantiatedMesh);
                }

                instantiatedMesh = null;
            }

            // Reset to default mats and meshes
            if (Profile != null)
            {
                if (targetIconRenderer != null)
                {
                    // Restore the icon material to the renderer
                    targetIconRenderer.sharedMaterial = Profile.IconMaterial;
                }
                if (IconMeshFilter != null)
                {
                    IconMeshFilter.sharedMesh = Profile.IconMesh;
                }
            }
            // Reset our alpha to the alpha target
            alpha = alphaTarget;
        }

        private IEnumerator UpdateAlpha()
        {
            float startTime = Time.time;
            Color color = Color.white;
            string alphaColorProperty = string.IsNullOrEmpty(Profile.AlphaColorProperty) ? "_Color" : Profile.AlphaColorProperty;

            if (instantiatedMaterial != null)
            {
                color = instantiatedMaterial.GetColor(alphaColorProperty);
                color.a = alpha;
            }

            while (Time.time < startTime + Profile.AlphaTransitionSpeed)
            {
                alpha = Mathf.Lerp(alpha, alphaTarget, (Time.time - startTime) / Profile.AlphaTransitionSpeed);
                if (instantiatedMaterial != null && !string.IsNullOrEmpty(alphaColorProperty))
                {
                    color.a = alpha;
                    instantiatedMaterial.SetColor(alphaColorProperty, color);
                }
                yield return null;
            }

            alpha = alphaTarget;
            RefreshAlpha();
            updatingAlpha = false;
        }

#if UNITY_EDITOR
        [UnityEditor.CustomEditor(typeof(CompoundButtonIcon))]
        public class CustomEditor : MRTKEditor {
            protected override void DrawCustomFooter() {
                CompoundButtonIcon iconButton = (CompoundButtonIcon)target;
                if (iconButton != null && iconButton.Profile != null)
                {
                    iconButton.IconName = iconButton.Profile.DrawIconSelectField(iconButton.iconName);
                }

            }
        }
#endif
    }
}
