using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;
using Unity.Collections;
using UnityEngine;
using UnityEngine.Rendering;

namespace DefaultNamespace
{
    public class JiuJIeTest : MonoBehaviour
    {
        public const string meshXml = @"E:\local_workspace\ogre-1.9.1\Build\bin\debug\Npc_cunzhang1.mesh.xml";

        private void Start()
        {
            LoadV2();
        }

        private void LoadV2()
        {
            OgreMeshData meshData = new OgreMeshData();
            meshData.ReadXml(meshXml);

            string baseDir = Path.GetDirectoryName(meshXml);
            string skeletonXml = Path.Combine(baseDir, meshData.SkeletonLink + ".xml");

            OgreSkeletonData skeletonData = new OgreSkeletonData();
            skeletonData.ReadXml(skeletonXml);

            Transform meshParent = new GameObject("mesh").transform;
            meshParent.SetParent(transform, false);

            Transform skeletonParent = new GameObject("skeleton").transform;
            skeletonParent.SetParent(transform, false);

            Material defaultMaterial = new Material(Shader.Find("Standard"));

            var dic = new Dictionary<string, Transform>();
            Transform[] bones = new Transform[skeletonData.Bones.Length];
            for (int i = 0; i < skeletonData.Bones.Length; i++)
            {
                OgreSkeletonBone bone = skeletonData.Bones[i];
                GameObject go = new GameObject(bone.Name);
                go.transform.SetParent(skeletonParent, false);
                go.transform.position = bone.Pos;
                go.transform.rotation = bone.Rot;
                go.transform.localScale = bone.Scale;
                dic.Add(go.name, go.transform);
                bones[i] = go.transform;
            }

            // 设置层级
            for (int i = 0; i < skeletonData.BoneHierarchies.Length; i++)
            {
                OgreBoneHierarchy h = skeletonData.BoneHierarchies[i];
                dic[h.Bone].SetParent(dic[h.Parent], false);
            }

            Matrix4x4[] bindposes = new Matrix4x4[bones.Length];
            for (int i = 0; i < bones.Length; i++)
            {
                var bone = bones[i];
                bindposes[i] = bone.worldToLocalMatrix * transform.localToWorldMatrix;
            }

#if SubMeshs
            for (int i = 0; i < meshData.SubMeshName.Length; i++)
            {
                OgreSubMeshName subMeshName = meshData.SubMeshName[i];
                OgreSubMesh subMesh = meshData.SubMesh[subMeshName.Index];

                GameObject meshGo = new GameObject(subMeshName.Name);
                meshGo.transform.SetParent(meshParent, false);
                var smr = meshGo.AddComponent<SkinnedMeshRenderer>();
                smr.sharedMaterial = defaultMaterial;

                // 找到这个subMesh最大的索引
                int min = subMesh.Triangles.Min();
                int max = subMesh.Triangles.Max();
                int len = max - min + 1;

                Mesh mesh = new Mesh();
                mesh.SetVertices(meshData.Vertices, min, len);
                mesh.SetNormals(meshData.Normals, min, len);
                for (int j = 0; j < meshData.UVs.Length; j++)
                    mesh.SetUVs(j, meshData.UVs[j], min, len);

                int[] triangles = subMesh.Triangles.ToArray();
                for (int j = 0; j < triangles.Length; j++)
                    triangles[j] -= min;
                mesh.SetTriangles(triangles, 0);

                // 骨骼权重
                List<OgreBoneWeight>[] weights = new List<OgreBoneWeight>[len];

                // 每个顶点的骨骼数量
                NativeArray<byte> bonesPerVertex = new NativeArray<byte>(len, Allocator.Temp);
                for (int k = 0; k < meshData.BoneWeights.Length; k++)
                {
                    OgreBoneWeight bw = meshData.BoneWeights[k];
                    if (bw.VertexIndex >= min && bw.VertexIndex <= max)
                    {
                        int vertexIndex = bw.VertexIndex - min;
                        weights[vertexIndex] ??= new();
                        weights[vertexIndex].Add(bw);
                        bonesPerVertex[vertexIndex] += 1; // 记录骨骼数量
                    }
                }

                NativeArray<BoneWeight1> boneWeights = new NativeArray<BoneWeight1>(weights.Sum(f => f.Count), Allocator.Temp);
                int addIndex = 0;
                foreach (var list in weights)
                {
                    foreach (var w in list.OrderByDescending(f => f.Weight))
                    {
                        boneWeights[addIndex] = new BoneWeight1
                        {
                            boneIndex = w.BoneIndex,
                            weight = w.Weight
                        };
                        addIndex++;
                    }
                }

                mesh.SetBoneWeights(bonesPerVertex, boneWeights);
                mesh.bindposes = bindposes;

                smr.sharedMesh = mesh;
                smr.bones = bones;
                smr.rootBone = dic[skeletonData.BoneHierarchies[0].Parent];
            }
#else

            Mesh mesh = new Mesh();
            mesh.SetVertices(meshData.Vertices);
            mesh.SetNormals(meshData.Normals);
            for (int j = 0; j < meshData.UVs.Length; j++)
                mesh.SetUVs(j, meshData.UVs[j]);

            int indexCount = meshData.SubMesh.Sum(f => f.FaceCount) * 3;
            NativeArray<int> triangles = new NativeArray<int>(indexCount, Allocator.Temp);
            int triangleIdx = 0;
            foreach (var item in meshData.SubMesh)
            {
                foreach (var idx in item.Triangles)
                {
                    triangles[triangleIdx] = idx;
                    triangleIdx += 1;
                }
            }

            mesh.SetIndexBufferParams(indexCount, IndexFormat.UInt32);
            mesh.SetIndexBufferData(triangles, 0, 0, indexCount);
            mesh.subMeshCount = meshData.SubMeshName.Length;
            mesh.bindposes = bindposes;

            int indexStart = 0;
            for (int i = 0; i < meshData.SubMesh.Length; i++)
            {
                OgreSubMesh ogreSubMesh = meshData.SubMesh[i];
                mesh.SetSubMesh(i, new SubMeshDescriptor(indexStart * 3, ogreSubMesh.FaceCount * 3));
                indexStart += ogreSubMesh.FaceCount;
            }

            // 骨骼权重
            int verticesCount = meshData.Vertices.Length;
            var boneWeightArr = meshData.BoneWeights.OrderBy(f => f.VertexIndex).ThenByDescending(f => f.Weight).ToArray();

            // 每个顶点的骨骼数量
            NativeArray<byte> bonesPerVertex = new NativeArray<byte>(verticesCount, Allocator.Temp);
            NativeArray<BoneWeight1> boneWeights = new NativeArray<BoneWeight1>(meshData.BoneWeights.Length, Allocator.Temp);
            int addIndex = 0;
            for (int k = 0; k < boneWeightArr.Length; k++)
            {
                OgreBoneWeight bw = boneWeightArr[k];
                int vertexIndex = bw.VertexIndex;
                bonesPerVertex[vertexIndex] += 1; // 记录骨骼数量
                boneWeights[addIndex++] = new BoneWeight1 { boneIndex = bw.BoneIndex, weight = bw.Weight };
            }

            mesh.SetBoneWeights(bonesPerVertex, boneWeights);

            var smr = meshParent.gameObject.AddComponent<SkinnedMeshRenderer>();
            smr.sharedMesh = mesh;
            smr.sharedMaterial = defaultMaterial;
            smr.bones = bones;
            smr.rootBone = dic[skeletonData.BoneHierarchies[0].Parent];
#endif
            Animation ani = gameObject.GetComponent<Animation>();
            foreach (var clip in skeletonData.Clips)
            {
                clip.legacy = true;
                ani.AddClip(clip, clip.name);
            }
        }

        private void LoadV1()
        {
            XmlDocument meshDoc = new XmlDocument();
            meshDoc.Load(meshXml);

            XmlElement rootNode = meshDoc.DocumentElement;
            XmlNode sharedGeometryNode = rootNode.GetElementsByTagName("sharedgeometry")[0];
            XmlNode submeshesNode = rootNode.GetElementsByTagName("submeshes")[0];
            XmlNode submeshnamesNode = rootNode.GetElementsByTagName("submeshnames")[0];

            int vertexCount = int.Parse(sharedGeometryNode.Attributes["vertexcount"].Value);

            Vector3[] vertices = new Vector3[vertexCount];
            Vector3[] normals = new Vector3[vertexCount];

            // vertex
            XmlNode vertexBufferNode1 = sharedGeometryNode.ChildNodes[0];
            bool isPositions = bool.Parse(vertexBufferNode1.Attributes["positions"].Value);
            bool isNormals = bool.Parse(vertexBufferNode1.Attributes["normals"].Value);
            for (int i = 0; i < vertexBufferNode1.ChildNodes.Count; i++)
            {
                XmlNode vertexNode = vertexBufferNode1.ChildNodes[i];
                if (isPositions)
                {
                    XmlNode posNode = vertexNode["position"];
                    vertices[i] = posNode.ReadVector3();
                }

                if (isNormals)
                {
                    XmlNode normalNode = vertexNode["normal"];
                    normals[i] = normalNode.ReadVector3();
                }
            }

            // uv
            XmlNode vertexBufferNode2 = sharedGeometryNode.ChildNodes[1];
            int coordsNum = int.Parse(vertexBufferNode2.Attributes["texture_coords"].Value);
            Vector2[][] uvs = new Vector2[coordsNum][];
            for (int i = 0; i < coordsNum; i++)
                uvs[i] = new Vector2[vertexCount];

            for (int i = 0; i < vertexBufferNode2.ChildNodes.Count; i++)
            {
                XmlNode vertexNode = vertexBufferNode2.ChildNodes[i];
                for (int j = 0; j < vertexNode.ChildNodes.Count; j++)
                {
                    XmlNode texcoordNode = vertexNode.ChildNodes[j];
                    uvs[j][i] = texcoordNode.ReadUV2();
                }
            }

            Mesh mesh = new Mesh();
            mesh.vertices = vertices;
            mesh.normals = normals;
            for (int i = 0; i < coordsNum; i++)
                mesh.SetUVs(i, uvs[i]);

            XmlNode boneassignments = rootNode.SelectSingleNode("boneassignments");
            BoneWeight[] boneWeights = new BoneWeight[vertices.Length];

            for (int i = 0; i < boneassignments.ChildNodes.Count; i++)
            {
                XmlElement item = boneassignments.ChildNodes[i] as XmlElement;
                int vIdx = int.Parse(item.GetAttribute("vertexindex"));
                int boneIdx = int.Parse(item.GetAttribute("boneindex"));
                float weight = float.Parse(item.GetAttribute("weight"));

                BoneWeight boneWeight = boneWeights[vIdx];
                if (boneWeight.boneIndex0 == 0)
                {
                    boneWeight.boneIndex0 = boneIdx;
                    boneWeight.weight0 = weight;
                }
                else if (boneWeight.boneIndex1 == 0)
                {
                    boneWeight.boneIndex1 = boneIdx;
                    boneWeight.weight1 = weight;
                }
                else if (boneWeight.boneIndex2 == 0)
                {
                    boneWeight.boneIndex2 = boneIdx;
                    boneWeight.weight2 = weight;
                }
                else if (boneWeight.boneIndex3 == 0)
                {
                    boneWeight.boneIndex3 = boneIdx;
                    boneWeight.weight3 = weight;
                }
                else
                {
                    Debug.LogError($"超过4个顶点: vertexindex={vIdx}");
                }

                boneWeights[vIdx] = boneWeight;
            }

            // 总共数量
            int totalCount = 0;
            int[] indexRange = new int[submeshesNode.ChildNodes.Count];
            for (int i = 0; i < submeshesNode.ChildNodes.Count; i++)
            {
                XmlNode node = submeshesNode.ChildNodes[i];
                XmlNode facesNode = node.FirstChild;
                int count = int.Parse(facesNode.Attributes["count"].Value);
                if (i == 0)
                {
                    indexRange[i] = count;
                }
                else
                {
                    indexRange[i] = indexRange[i - 1] + count;
                }

                totalCount += count;
            }

            int[] triangles = new int[totalCount * 3];
            int triangleIdx = 0;
            for (int i = 0; i < submeshesNode.ChildNodes.Count; i++)
            {
                XmlNode node = submeshesNode.ChildNodes[i];
                XmlNode facesNode = node.FirstChild;
                int count = int.Parse(facesNode.Attributes["count"].Value);

                for (int j = 0; j < facesNode.ChildNodes.Count; j++, triangleIdx += 3)
                {
                    XmlNode faceNode = facesNode.ChildNodes[j];
                    int v1 = int.Parse(faceNode.Attributes["v1"].Value);
                    int v2 = int.Parse(faceNode.Attributes["v2"].Value);
                    int v3 = int.Parse(faceNode.Attributes["v3"].Value);
                    triangles[triangleIdx] = v1;
                    triangles[triangleIdx + 1] = v2;
                    triangles[triangleIdx + 2] = v3;
                }
            }

            mesh.SetIndexBufferParams(triangles.Length, IndexFormat.UInt32);
            mesh.SetIndexBufferData(triangles, 0, 0, triangles.Length);
            mesh.subMeshCount = indexRange.Length;
            mesh.boneWeights = boneWeights;

            for (int i = 0; i < submeshesNode.ChildNodes.Count; i++)
            {
                XmlNode node = submeshesNode.ChildNodes[i];
                XmlNode facesNode = node.FirstChild;
                int count = int.Parse(facesNode.Attributes["count"].Value);
                mesh.SetSubMesh(i, new SubMeshDescriptor((indexRange[i] - count) * 3, count * 3));
            }


            OgreSkeletonData ogreSkeletonData = new OgreSkeletonData();
            // ogreSkeletonData.ReadXml(skeletonXml);

            // 创建骨骼
            Transform skeleton = new GameObject("skeleton").transform;
            skeleton.SetParent(transform, false);

            var dic = new Dictionary<string, Transform>();
            List<Transform> bones = new List<Transform>();
            for (int i = 0; i < ogreSkeletonData.Bones.Length; i++)
            {
                OgreSkeletonBone bone = ogreSkeletonData.Bones[i];
                GameObject go = new GameObject(bone.Name);
                go.transform.SetParent(skeleton, false);
                go.transform.position = bone.Pos;
                go.transform.rotation = bone.Rot;
                go.transform.localScale = bone.Scale;
                dic.Add(go.name, go.transform);
                bones.Add(go.transform);
            }

            // 设置层级
            for (int i = 0; i < ogreSkeletonData.BoneHierarchies.Length; i++)
            {
                OgreBoneHierarchy h = ogreSkeletonData.BoneHierarchies[i];
                dic[h.Bone].SetParent(dic[h.Parent], false);
            }

            Matrix4x4[] bindposes = new Matrix4x4[bones.Count];
            for (int i = 0; i < bones.Count; i++)
            {
                var bone = bones[i];
                bindposes[i] = bone.worldToLocalMatrix * skeleton.localToWorldMatrix;
            }

            mesh.bindposes = bindposes;
            mesh.RecalculateBounds();

            SkinnedMeshRenderer skinnedMeshRenderer = gameObject.AddComponent<SkinnedMeshRenderer>();
            skinnedMeshRenderer.bones = bones.ToArray();
            skinnedMeshRenderer.sharedMesh = mesh;
            // skinnedMeshRenderer.sharedMaterial = 
            skinnedMeshRenderer.bounds = mesh.bounds;
            skinnedMeshRenderer.rootBone = dic[ogreSkeletonData.BoneHierarchies[0].Parent];

            Animation animation = gameObject.GetComponent<Animation>();
            foreach (var clip in ogreSkeletonData.Clips)
            {
                clip.legacy = true;
                // clip.wrapMode = WrapMode.Loop;
                animation.AddClip(clip, clip.name);
            }

            // animation.Play("Stand");
        }

        private bool isPlayLoop = false;

        private void OnGUI()
        {
            isPlayLoop = GUILayout.Toggle(isPlayLoop, "循环播放");
            Animation animation = gameObject.GetComponent<Animation>();
            foreach (AnimationState state in animation)
            {
                if (GUILayout.Button(state.name))
                {
                    state.clip.wrapMode = isPlayLoop ? WrapMode.Loop : WrapMode.Default;
                    animation.Play(state.name);
                }
            }
        }
    }
}