using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

namespace Coffee.UIExtensions
{
    /// <summary>
    /// Abstract effect base for UI.
    /// </summary>
    [DisallowMultipleComponent]
    public abstract class UIEffectBase : BaseMeshEffect, IParameterTexture
#if UNITY_EDITOR
    , ISerializationCallbackReceiver
#endif
    {
        protected static readonly Vector2[] splitedCharacterPosition = { Vector2.up, Vector2.one, Vector2.right, Vector2.zero };
        protected static readonly List<UIVertex> tempVerts = new List<UIVertex>();

        [HideInInspector]
        [SerializeField] int m_Version;
        [SerializeField] protected Material m_EffectMaterial;

        /// <summary>
        /// Gets or sets the parameter index.
        /// </summary>
        public int parameterIndex { get; set; }

        /// <summary>
        /// Gets the parameter texture.
        /// </summary>
        public virtual ParameterTexture ptex { get { return null; } }

        /// <summary>
        /// Gets target graphic for effect.
        /// </summary>
        public Graphic targetGraphic { get { return graphic; } }

        /// <summary>
        /// Gets material for effect.
        /// </summary>
        public Material effectMaterial { get { return m_EffectMaterial; } }

#if UNITY_EDITOR
        protected override void Reset()
        {
            m_Version = 300;
            OnValidate();
        }

        /// <summary>
        /// Raises the validate event.
        /// </summary>
        protected override void OnValidate()
        {
            base.OnValidate();

            var mat = GetMaterial();
            if (m_EffectMaterial != mat)
            {
                m_EffectMaterial = mat;
                UnityEditor.EditorUtility.SetDirty(this);
            }

            ModifyMaterial();
            SetVerticesDirty();
            SetDirty();
        }

        public void OnBeforeSerialize()
        {
        }

        public void OnAfterDeserialize()
        {
            UnityEditor.EditorApplication.delayCall += UpgradeIfNeeded;
        }

        protected bool IsShouldUpgrade(int expectedVersion)
        {
            if (m_Version < expectedVersion)
            {
                Debug.LogFormat(gameObject, "<b>{0}({1})</b> has been upgraded: <i>version {2} -> {3}</i>", name, GetType().Name, m_Version, expectedVersion);
                m_Version = expectedVersion;

                //UnityEditor.EditorApplication.delayCall += () =>
                {
                    UnityEditor.EditorUtility.SetDirty(this);
                    if (!Application.isPlaying && gameObject && gameObject.scene.IsValid())
                    {
                        UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(gameObject.scene);
                    }
                }
                ;
                return true;
            }
            return false;
        }

        protected virtual void UpgradeIfNeeded()
        {
        }

        /// <summary>
        /// Gets the material.
        /// </summary>
        /// <returns>The material.</returns>
        protected virtual Material GetMaterial()
        {
            return null;
        }
#endif

        /// <summary>
        /// Modifies the material.
        /// </summary>
        public virtual void ModifyMaterial()
        {
            if (targetGraphic == null)
                return;
            targetGraphic.material = isActiveAndEnabled ? m_EffectMaterial : null;
        }

        /// <summary>
        /// This function is called when the object becomes enabled and active.
        /// </summary>
        protected override void OnEnable()
        {
            base.OnEnable();

            if (ptex != null)
            {
                ptex.Register(this);
            }
            ModifyMaterial();
            SetVerticesDirty();
            SetDirty();
        }

        /// <summary>
        /// This function is called when the behaviour becomes disabled () or inactive.
        /// </summary>
        protected override void OnDisable()
        {
            base.OnDisable();

            ModifyMaterial();
            SetVerticesDirty();
            if (ptex != null)
            {
                ptex.Unregister(this);
            }
        }

        /// <summary>
        /// Mark the UIEffect as dirty.
        /// </summary>
        protected virtual void SetDirty()
        {
            SetVerticesDirty();
        }

        protected override void OnDidApplyAnimationProperties()
        {
            SetDirty();
        }
    }
}