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

/// <summary>
/// 把Mesh中的面进行排序,用于处理单个Mesh中alpha渲染的问题
/// 这个适用于哪些用几块互相独立的面片制作的,用于2D展示的动画模型.
/// 1.Mesh的不要由SubMesh.
/// 2.Matrial只有一个
/// </summary>
public class AlphaMeshSortScript : MonoBehaviour
{
    //精度值
    private const int CN_PRECISION_VALUE = 1000;
    private Mesh _oldMesh;
    private Mesh _newMesh;
    private SkinnedMeshRenderer _render;
    private SortedDictionary<int, SubMeshInfo> _newTrisData;
    //0:x,1:y,2:z
    [Header("使用模型坐标系的哪个坐标轴进行排序.(0:X,1:Y,2:Z)")]
    [Range(0, 2)]
    public int _useAxis = 1;

    //是否优化
    public bool _useOptimize = true;

    private void OnEnable()
    {
        Restore();
        if (_render != null && _render.sharedMesh != null)
        {
            _oldMesh = _render.sharedMesh;
            CreateNewMesh();
            if (_newMesh != null)
            {
                _render.sharedMesh = _newMesh;
                var mats = new Material[_newMesh.subMeshCount];
                for (int i = 0; i < mats.Length; i++) mats[i] = _render.sharedMaterial;
                _render.sharedMaterials = mats;
            }
        }

    }

    private void OnDisable()
    {
        Restore();
    }

    private void OnDestroy()
    {
        Restore();
    }

    /// <summary>
    /// 把所有处理恢复
    /// </summary>
    private void Restore()
    {
        _render = GetComponent<SkinnedMeshRenderer>();
        if (_oldMesh != null && _render != null)
        {
            _render.sharedMesh = _oldMesh;
            _render.sharedMaterials = new Material[] { _render.sharedMaterial };
            _oldMesh = null;
            _render = null;
        }

        if (_newMesh != null)
        {
            if (Application.isPlaying)
            {
                GameObject.Destroy(_newMesh);
            }
            else
            {
                GameObject.DestroyImmediate(_newMesh);
            }
            _newMesh = null;
        }
    }

    private void CreateNewMesh()
    {
        if (_oldMesh != null)
        {
            var vs = _oldMesh.vertices;
            var tris = _oldMesh.triangles;
            //对使用坐标轴进行排序进行一些矫正
            if (_useAxis < 0) _useAxis = 0;
            else if (_useAxis > 2) _useAxis = 2;

            //根据顶点位置进行分组
            
            _newTrisData = new SortedDictionary<int, SubMeshInfo>(KeyComparer.Default);
            List<int> verKeyList = new List<int>();           

            for (int i = 0; i < vs.Length; i++)
            {
                int k = Mathf.RoundToInt(vs[i][_useAxis] * CN_PRECISION_VALUE);
                verKeyList.Add(k);
                if (!_newTrisData.ContainsKey(k))
                {
                    _newTrisData[k] = new SubMeshInfo(k,vs);
                }
            }

            //对所有的面进行组合
            for (int i = 0; i < tris.Length; i += 3)
            {
                var v1 = verKeyList[tris[i]];
                var v2 = verKeyList[tris[i + 1]];
                var v3 = verKeyList[tris[i + 2]];

                if (v1 == v2 && v2 == v3)
                {
                    _newTrisData[v1].Indexes.Add(tris[i]);
                    _newTrisData[v1].Indexes.Add(tris[i + 1]);
                    _newTrisData[v1].Indexes.Add(tris[i + 2]);
                }
            }


            //Debug.Log("111111111111111111数量:"+ _newTrisData.Count);
            if (_useOptimize)
            {
                //把哪些相邻的不想交的mesh合并起来
                int[] keys = new int[_newTrisData.Count];
                _newTrisData.Keys.CopyTo(keys, 0);
                int lastKey = -9999999;
                for (int i = 0; i < keys.Length; i++)
                {
                    var cuValue = _newTrisData[keys[i]];
                    cuValue.CalcBounds(_useAxis);
                    if (lastKey != -9999999)
                    {
                        if (!cuValue.IsIntersect(_newTrisData[lastKey]))
                        {
                            cuValue.Indexes.AddRange(_newTrisData[lastKey].Indexes);
                            _newTrisData.Remove(lastKey);
                            cuValue.CalcBounds(_useAxis);
                            //Debug.Log("AAAAAAAAAAAAAAAAAAAAAAAAA");
                        }
                    }
                    lastKey = keys[i];
                }
                //Debug.Log("222222222222222222数量:" + _newTrisData.Count);
            }
            else
            {
                var e1 = _newTrisData.GetEnumerator();
                while (e1.MoveNext())
                {
                    e1.Current.Value.CalcBounds(_useAxis);
                }

            }
            if (_newTrisData.Count > 20)
            {
                Debug.LogError("创建新的NewMesh失败,原因是创建的SubMesh太多[" + _newTrisData.Count + "],请检查选用的坐标轴是否正确!");
                return;
            }

            _newMesh = new Mesh();
            _newMesh.hideFlags = HideFlags.DontSave;
            _newMesh.name = _oldMesh.name + "_AlphaSort";
            _newMesh.vertices = vs;
            _newMesh.uv = _oldMesh.uv;
            _newMesh.uv2 = _oldMesh.uv2;
            _newMesh.normals = _oldMesh.normals;
            _newMesh.tangents = _oldMesh.tangents;
            _newMesh.boneWeights = _oldMesh.boneWeights;
            _newMesh.bindposes = _oldMesh.bindposes;
            _newMesh.triangles = new int[0];

            _newMesh.subMeshCount = _newTrisData.Count;
            int idx = 0;
            var e = _newTrisData.GetEnumerator();
            while (e.MoveNext())
            {
                _newMesh.SetTriangles(e.Current.Value.Indexes, idx);
                idx++;
            }
            _newMesh.RecalculateBounds();
        }
    }

    private void OnDrawGizmos()
    {
        if (_newTrisData != null)
        {
            var e = _newTrisData.GetEnumerator();
            while (e.MoveNext())
            {
                e.Current.Value.Draw(transform);
            }
        }
    }

    #region  //私有类
    //比较的算子类
    private class KeyComparer : IComparer<int>
    {
        public static KeyComparer Default = new KeyComparer();
        public int Compare(int x, int y)
        {
            return y - x;
        }
    }
    //子Mesh的数据
    private class SubMeshInfo
    {
        public int Key = 0;
        public List<int> Indexes;

        private Vector2 _minPoint;
        private Vector2 _maxPoint;
        private Vector3[] _allVs;

        public SubMeshInfo(int k,Vector3[] vs)
        {
            Key = k;
            Indexes = new List<int>();
            _allVs = vs;
        }

        public void CalcBounds(int excludeIdx)
        {
            var arr = excludeIdx == 0 ? new int[] { 1, 2 } : (excludeIdx == 1 ? new int[] { 0, 2 } : new int[] { 0, 1 });
            _minPoint = Vector2.zero;
            _maxPoint = Vector2.zero;
            for (int i = 0; i < Indexes.Count;i++)
            {
                var v = _allVs[Indexes[i]];
                if (_minPoint.x > v[arr[0]]) _minPoint.x = v[arr[0]];
                if (_maxPoint.x < v[arr[0]]) _maxPoint.x = v[arr[0]];
                if (_minPoint.y > v[arr[1]]) _minPoint.y = v[arr[1]];
                if (_maxPoint.y < v[arr[1]]) _maxPoint.y = v[arr[1]]; 
            }
        }

    

        public bool IsIntersect(SubMeshInfo mi)
        {
            //Debug.LogError("self:" + _maxPoint + ":::" + _minPoint + "::mi:" + mi._maxPoint + "::" + mi._minPoint);
            var v1 = _maxPoint - mi._minPoint;
            var v2 = mi._maxPoint - _minPoint;
           // Debug.Log("v1:" + v1 + ";;;v2" + v2);
            var min = Mathf.Min(v1.x, v1.y, v2.x, v2.y);
            //当min小于0的时候,就表示两个面不相交.
            return min > 0;
        }

        public void Draw(Transform trans)
        {
            var y = (float)Key / (float)CN_PRECISION_VALUE;
            var v1 = trans.localToWorldMatrix.MultiplyPoint(new Vector3(_maxPoint.x, y, _maxPoint.y));
            var v2 = trans.localToWorldMatrix.MultiplyPoint(new Vector3(_maxPoint.x, y, _minPoint.y));            
            var v3 = trans.localToWorldMatrix.MultiplyPoint(new Vector3(_minPoint.x, y, _minPoint.y));
            var v4 = trans.localToWorldMatrix.MultiplyPoint(new Vector3(_minPoint.x, y, _maxPoint.y));
            Gizmos.color = new Color(Random.Range(0, 1), Random.Range(0, 1), Random.Range(0, 1));
            Gizmos.DrawLine(v1, v2);
            Gizmos.DrawLine(v2, v3);
            Gizmos.DrawLine(v3, v4);
            Gizmos.DrawLine(v4, v1);
        }

    }
    #endregion

}