using System;
using System.Linq;
using UnityEngine;

namespace TFramework
{
    [DisallowMultipleComponent]
    public class Outline : MonoBehaviour
    {
        public enum Mode
        {
            /// <summary>
            /// 描边所有轮廓
            /// </summary>
            OutlineAll,
            /// <summary>
            /// 仅描边可见部分
            /// </summary>
            OutlineVisible,
            /// <summary>
            /// 仅描边隐藏部分
            /// </summary>
            OutlineHidden,
            /// <summary>
            /// 描边且剪影
            /// </summary>
            OutlineAndSilhouette,
            /// <summary>
            /// 仅描边
            /// </summary>
            SilhouetteOnly
        }

        /// <summary>
        /// 描边模式
        /// </summary>
        public Mode OutlineMode
        {
            get { return outlineMode; }
            set
            {
                outlineMode = value;
                _isChange = true;
            }
        }

        /// <summary>
        /// 描边颜色
        /// </summary>
        public Color OutlineColor
        {
            get { return outlineColor; }
            set
            {
                outlineColor = value;
                _isChange = true;
            }
        }

        /// <summary>
        /// 描边宽度
        /// </summary>
        public float OutlineWidth
        {
            get { return outlineWidth; }
            set
            {
                outlineWidth = value;
                _isChange = true;
            }
        }


        [SerializeField]
        private Mode outlineMode;

        [SerializeField]
        private Color outlineColor;

        [SerializeField, Range(0f, 10f)]
        private float outlineWidth = 2f;

        private Renderer[] renderers;
        private Material outlineMaskMaterial;
        private Material outlineFillMaterial;
        private bool _isChange=false;

        void Awake()
        {
            renderers = GetComponentsInChildren<Renderer>();

            // Instantiate outline materials
            outlineMaskMaterial = Instantiate(Main.GetMagr<EffectManager>().m_outlineMaskMaterial);
            outlineFillMaterial = Instantiate(Main.GetMagr<EffectManager>().m_outlineFillMaterial);

            outlineMaskMaterial.name = "OutlineMask (Instance)";
            outlineFillMaterial.name = "OutlineFill (Instance)";
        }

        void OnEnable()
        {
            foreach (var renderer in renderers)
            {

                var materials = renderer.sharedMaterials.ToList();

                materials.Add(outlineMaskMaterial);
                materials.Add(outlineFillMaterial);

                renderer.materials = materials.ToArray();
            }
            _isChange = true;
        }

        private void Update()
        {
            if(_isChange)
            {
                _isChange = false;
                UpdateMaterialProperties();
            }
        }

        void OnDisable()
        {
            foreach (var renderer in renderers)
            {

                // Remove outline shaders
                var materials = renderer.sharedMaterials.ToList();

                materials.Remove(outlineMaskMaterial);
                materials.Remove(outlineFillMaterial);

                renderer.materials = materials.ToArray();
            }
        }

        void OnDestroy()
        {
            Destroy(outlineMaskMaterial);
            Destroy(outlineFillMaterial);
        }

        void UpdateMaterialProperties()
        {

            // Apply properties according to mode
            outlineFillMaterial.SetColor("_OutlineColor", outlineColor);

            switch (outlineMode)
            {
                case Mode.OutlineAll:
                    outlineMaskMaterial.SetFloat("_ZTest", (float)UnityEngine.Rendering.CompareFunction.Always);
                    outlineFillMaterial.SetFloat("_ZTest", (float)UnityEngine.Rendering.CompareFunction.Always);
                    outlineFillMaterial.SetFloat("_OutlineWidth", outlineWidth);
                    break;

                case Mode.OutlineVisible:
                    outlineMaskMaterial.SetFloat("_ZTest", (float)UnityEngine.Rendering.CompareFunction.Always);
                    outlineFillMaterial.SetFloat("_ZTest", (float)UnityEngine.Rendering.CompareFunction.LessEqual);
                    outlineFillMaterial.SetFloat("_OutlineWidth", outlineWidth);
                    break;

                case Mode.OutlineHidden:
                    outlineMaskMaterial.SetFloat("_ZTest", (float)UnityEngine.Rendering.CompareFunction.Always);
                    outlineFillMaterial.SetFloat("_ZTest", (float)UnityEngine.Rendering.CompareFunction.Greater);
                    outlineFillMaterial.SetFloat("_OutlineWidth", outlineWidth);
                    break;

                case Mode.OutlineAndSilhouette:
                    outlineMaskMaterial.SetFloat("_ZTest", (float)UnityEngine.Rendering.CompareFunction.LessEqual);
                    outlineFillMaterial.SetFloat("_ZTest", (float)UnityEngine.Rendering.CompareFunction.Always);
                    outlineFillMaterial.SetFloat("_OutlineWidth", outlineWidth);
                    break;

                case Mode.SilhouetteOnly:
                    outlineMaskMaterial.SetFloat("_ZTest", (float)UnityEngine.Rendering.CompareFunction.LessEqual);
                    outlineFillMaterial.SetFloat("_ZTest", (float)UnityEngine.Rendering.CompareFunction.Greater);
                    outlineFillMaterial.SetFloat("_OutlineWidth", 0f);
                    break;
            }
        }
    }
}
