using System;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.Assertions;
using UGCTools.Runtime;
using System.IO.Compression;
using UnityEditor.Animations;
using System.Text;
using System.Collections.Generic;
using UnityEditor.Build;
using UnityEngine.Rendering;
using System.Drawing.Imaging;
using UnityEditor.SceneManagement;
using UnityEngine.SceneManagement;
using UnityEngine.Advertisements;

namespace UGCTools.Editor
{

    public static class StringConst
    {
        public const string logoPath = "Assets/DEETools/Editor/Resources/icon.jpg";
        public const string defaultURPPrefabPath = "Assets/DEETools/Editor/Resources/Models/preview/URP/preview.prefab";
        public const string defaultBuiltInPrefabPath = "Assets/DEETools/Editor/Resources/Models/preview/Builtin/preview.prefab";
        public const string maleURPPrefabPath = "Assets/DEETools/Editor/Resources/Models/Ryan/URP/Ryan.prefab";
        public const string maleBuiltInPrefabPath = "Assets/DEETools/Editor/Resources/Models/Ryan/Builtin/Ryan.prefab";
        public const string femaleURPPrefabPath = "Assets/DEETools/Editor/Resources/Models/Aaliyah/URP/Aaliyah.prefab";
        public const string femaleBuiltInPrefabPath = "Assets/DEETools/Editor/Resources/Models/Aaliyah/Builtin/Aaliyah.prefab";
        public const string controllerResPath = "Assets/DEETools/Editor/Resources/Controller/controller.controller";
        public const string handFistPosePath = "Assets/DEETools/Editor/Resources/Animations/fist_pose.fbx";
        public const string handPalmsPosePath = "Assets/DEETools/Editor/Resources/Animations/palms_pose.fbx";
        public const string previewScenePath = "Assets/DEETools/Editor/Resources/Preview.unity";

        public const string resPath = "Assets/DEETools/Res";
        public const string animationResPath = "Assets/DEETools/Res/Animations";

        public const string builderFolderPath = "Assets/../DEETools/Builder/AB/{0}";
        public const string contextFolderPath = "Assets/../DEETools/Builder/Context/{0}";
        public const string zipFilePath = "Assets/../DEETools/Builder/{0}.zip";
        public const string savepProjectPath = "Assets/DEETools/Settings/{0}.asset";
        public const string projectSettingPath = "Assets/DEETools/Settings/{0}.asset";

        public const string universalRenderKey = "UniversalRenderPipelineAsset";
        public const string HDRenderKey = "HDRenderPipelineAsset";

        public const string sceneName = "Preview";
        public const string npcName = "NPC";
    }

    public static class StringPanel
    {
        public const string modelTab = "Model";
        public const string animationTab = "Animation";
        public const string musicTab = "Music";
        public const string previewTab = "Preview";

        public const string panelName = "DEETools";
        public const string tileName = "  Dance Eden Editor";
        public const string version = "v1.0";
        public const string stringImport = "Import";

        public const string animationType = "Animation Type";
        public const string avatarSetup = "Avatar Setup";
        public const string skinWeights = "Skin Weights";
        public const string importAnimation = "Import Animation";
        public const string animationClip = "Animation Clip";
        public const string apply = "Apply";

        public const string audioSource = "AudioSource";
        public const string stringLength = "Length";
        public const string loopTime = "Loop Time";
        public const string loopPose = "Loop Pose";
        public const string cycleOffset = "Cycle Offset";
        public const string rootTransformRotation = "Root Transform Rotation";
        public const string bakeIntoPose = "Bake Into Pose";
        public const string basedUpon = "Based Upon (at Start)";
        public const string offset = "Offset";
        public const string rootTransformPositionY = "Root Transform Position (Y)";
        public const string centerOfMass = "Center Of Mass";
        public const string feet = "Feet";
        public const string rootTransformPositionXZ = "Root Transform Position (XZ)";
        public const string mirror = "Mirror";
        public const string handAniStyle = "Hand Styling";
        public const string FootIK = "Foot IK";
        public static readonly string[] transformRotationPopups = new string[] { "Original", "Body Orientation" };
        public static readonly string[] transformPositionYPopups = new string[] { "Original", "Center Of Mass", "Feet" };
        public static readonly string[] transformPositionXZPopups = new string[] { "Original", "Center of Mass" };
        public static readonly string[] handPoseStylePopups = new string[] { "Original animation", "Fist", "Unstyled(relaxed hand)" };

        public const string audioClip = "Audio Clip";
        public const string coverImage = "Cover Image";
        public const string musicName = "Music Name";
        public const string singerName = "Singer Name";
        public const string star = "Star";
        public const string exerciseIntensity = "Exercise Intensity";
        public const string bpm = "BPM";
        public const string calBPM = "Calculate BPM";
        public const string animationStartTime = "Animation Start At";
        public const string musicLength = "Music Length";
        public const string danceLength = "Dance Length";
        public const string type = "Type";

        public const string stringScene = "Scene";
        public const string createDefaultScene = "Create Default Scene";
        public const string resetCamera = "Reset Camera";
        public const string npcLable = "Coach";
        public const string startTime = "Start Time";
        public const string endTime = "  End Time";
        public const string startEnterButton = "Enter the start time";
        public const string endEnterButton = "Enter the end time";
        public const string autoPlayAnimation = "Auto Play Animation";
        public static readonly string[] npcName = new string[] { "Ryan", "Aaliyah" };

        public const string stringProject = "Project";
        public const string projectName = "Project Name";
        public const string stringSave = "Save";
        public const string stringBuild = "Build Resource";
    }

    public class UGCTools : EditorWindow
    {
        enum TabsType
        {
            Model,
            //Cover,
            Animation,
            Music,
            Preview,
        }

        enum NPC
        {
            Male,
            Female,
        }

        public enum RenderPipeline
        {
            BuiltIn,
            URP,
            HDRP,
            Unknown
        }

        public static RenderPipeline GetRenderPipeline()
        {
            var renderPipelineAsset = GraphicsSettings.renderPipelineAsset;
            if (renderPipelineAsset == null)
            {
                return RenderPipeline.BuiltIn;
            }
            else if (renderPipelineAsset.GetType().ToString().Contains(StringConst.universalRenderKey))
            {
                return RenderPipeline.URP;
            }
            else if (renderPipelineAsset.GetType().ToString().Contains(StringConst.HDRenderKey))
            {
                return RenderPipeline.HDRP;
            }
            else
            {
                return RenderPipeline.Unknown;
            }
        }

        private Texture2D logo;
        private Vector2 screenPosition;
        private Vector2 scrollPosition;
        private TabsType selectedTab;
        private Rect contentRect;

        //Model
        public GameObject selectedModel;
        private GameObject oldSelectedModel;
        private int selectedClipIndex = 0;
        AnimationClip selectedClip;
        AnimationClip oldSelectedClip;
        AnimationClip outputClip;
        string[] animatinoClipNames;
        private GameObject previewObject;

        //Animation
        //private AnimationClipPreviewEditor animationClipEditor;
        private int handPoseIndex;
        private bool enableFootIK;

        //Audio
        private AudioClip oldSelectedAudioClip;
        private AudioClip selectedAudioClip;
        private Texture2D selectedCoverTexture;
        private string musicName;
        private string singerName;
        private int musicStar;
        private float musicStartTime;
        //private float musicLength;
        private int musicBpm;
        private int musicType;
        private int musicExerciseIntensity;

        //Preview
        private bool foldoutScene = false;
        private bool foldoutAnimation = false;
        private bool foldoutMusic = false;
        private int defaultNpc;
        private GameObject previewNpc;
        private UGCTimeline animationTimeline;

        private UGCTimeline musicTimeline;
        private AudioSource audioSource;
        private bool autoPlayAnimation;
        private float animationPlayDelay = 0;
        private bool animatorIsPlaying = false;
        List<ModelImporterClipAnimation> clipAnimations;

        //Projection
        private int projectIndex = 0;
        private string projectName = "";

        [MenuItem("DEETools/DEEInspector")]
        public static void ShowWindow()
        {
            GetWindow<UGCTools>(StringPanel.panelName);
        }

        private void OnEnable()
        {
            logo = AssetDatabase.LoadAssetAtPath<Texture2D>(StringConst.logoPath);
            selectedTab = TabsType.Model;
            EditorApplication.update += OnEditorUpdate;
            //EditorSceneManager.sceneSaving += OnSceneSaving;
            CreateDefaultScene();

        }
        private static void OnSceneSaving(Scene scene, string path)
        {
            // Ƿضĳ
            if (scene.name == "Preview")
            {
                // ȡ
                Debug.Log("Preventing save for scene: " + scene.name);
                EditorApplication.Beep();
                EditorUtility.DisplayDialog("Save Prevented", "Saving this scene is not allowed.", "OK");
                throw new System.OperationCanceledException("Saving this scene is not allowed.");
            }
        }

        private void OnDisable()
        {
            EditorApplication.update -= OnEditorUpdate;
            //EditorSceneManager.sceneSaving -= OnSceneSaving;
        }

        // ƴڵı
        private void DrawHeader(Rect logoRect)
        {
            Assert.IsNotNull(logo, "logo is null");
            Vector2 fontSize = Styles.logoFont.CalcSize(new GUIContent(StringPanel.tileName));
            Vector2 versionSize = Styles.versionFont.CalcSize(new GUIContent(StringPanel.version));
            float totalWidth = logo.width + fontSize.x + versionSize.x;
            float startX = logoRect.x + (logoRect.width - totalWidth) / 2;
            GUI.DrawTexture(new Rect(startX, logoRect.y, logo.width, logo.height), logo);
            startX = startX + logo.width;
            Styles.logoFont.Draw(new Rect(startX, logoRect.y, totalWidth - logo.width - versionSize.x, logo.height), StringPanel.tileName, false, false, false, false);
            startX = startX + fontSize.x;
            float startY = logoRect.y + logo.height - versionSize.y - 5;
            Styles.versionFont.Draw(new Rect(startX, startY, versionSize.x, versionSize.y), StringPanel.version, false, false, false, false);
        }

        public void DrawHeadTitle(string title)
        {
            GUILayout.Space(10);
            Rect rect = EditorGUILayout.GetControlRect(GUILayout.Height(28));
            Color original = GUI.backgroundColor;
            GUI.backgroundColor = Styles.headColor;
            GUI.Box(rect, GUIContent.none);
            //EditorGUILayout.LabelField(title, Styles.head1Font, GUILayout.Height(28));
            Styles.head1Font.Draw(rect, title, false, false, false, false);
            GUI.backgroundColor = original;
        }

        private void ImportAnimation(ModelImporter modelImporter)
        {
            if (selectedModel == null)
            {
                return;
            }
            string assetPath = AssetDatabase.GetAssetPath(selectedModel);
            UnityEngine.Object[] assets = AssetDatabase.LoadAllAssetsAtPath(assetPath);
            AnimationClip[] animations = assets.Select(asset => asset as AnimationClip).Where(clip => clip != null && !clip.name.StartsWith("__preview__")).ToArray();
            clipAnimations = new List<ModelImporterClipAnimation>();
            foreach (AnimationClip clip in animations)
            {
                ModelImporterClipAnimation modelImporterClipAnimation = new ModelImporterClipAnimation()
                {
                    name = clip.name,
                    takeName = clip.name,
                    firstFrame = 0,
                    lastFrame = (int)(clip.length * clip.frameRate),
                    loopTime = false,
                    loopPose = false,
                    cycleOffset = 0,

                    lockRootRotation = true,
                    keepOriginalOrientation = true,
                    rotationOffset = 0,

                    lockRootHeightY = true,
                    keepOriginalPositionY = false,
                    heightFromFeet = true,
                    heightOffset = 0,

                    lockRootPositionXZ = false,
                    keepOriginalPositionXZ = false,

                    mirror = false,
                };
                clipAnimations.Add(modelImporterClipAnimation);
            }
            modelImporter.clipAnimations = clipAnimations.ToArray();
        }

        private void ExtractAndSaveAnimation(string clipName, string animatinClipName, float startTime, float endTime)
        {
            string animationPath = StringConst.animationResPath;
            if (!AssetDatabase.IsValidFolder(animationPath))
            {
                AssetDatabase.CreateFolder(StringConst.resPath, "Animations");
            }
            AnimationClip sourceClip = selectedClip;
            
            if (sourceClip != null)
            {
                AnimationClip newClip = new AnimationClip();
                string newClipPath = $"{animationPath}/{clipName}.anim";
                EditorUtility.CopySerialized(sourceClip, newClip);
                EditorCurveBinding[] curveBindings = AnimationUtility.GetCurveBindings(newClip);
                //ȡ޸Ķ֡
                foreach (var binding in curveBindings)
                {
                    AnimationCurve curve = AnimationUtility.GetEditorCurve(newClip, binding);
                    if (curve == null) continue;
                    List<Keyframe> newKeys = new List<Keyframe>();
                    int keyLength = curve.keys.Length;
                    for (int i = 0; i < keyLength - 1; i++)
                    {
                        Keyframe keyframe = curve.keys[i];
                        Keyframe nextKeyframe = curve.keys[i + 1];

                        if (keyframe.time >= startTime && keyframe.time <= endTime)
                        {
                            newKeys.Add(new Keyframe(keyframe.time - startTime, keyframe.value, keyframe.inTangent, keyframe.outTangent));
                        }
                        else if (keyframe.time < startTime && nextKeyframe.time > startTime)
                        {
                            float t = (startTime - keyframe.time) / (nextKeyframe.time - keyframe.time);
                            float value = Mathf.Lerp(keyframe.value, nextKeyframe.value, t);
                            newKeys.Add(new Keyframe(startTime - startTime, value));
                        }
                        else if (keyframe.time < endTime && nextKeyframe.time > endTime)
                        {
                            float t = (endTime - keyframe.time) / (nextKeyframe.time - keyframe.time);
                            float value = Mathf.Lerp(keyframe.value, nextKeyframe.value, t);
                            newKeys.Add(new Keyframe(endTime - startTime, value));
                        }
                    }
                    //һ֡
                    if (curve.keys[keyLength - 1].time >= startTime && curve.keys[keyLength - 1].time <= endTime)
                    {
                        Keyframe keyframe = curve.keys[keyLength - 1];
                        newKeys.Add(new Keyframe(keyframe.time - startTime, keyframe.value, keyframe.inTangent, keyframe.outTangent));
                    }
                    // µ߲ùؼ֡
                    var newCurve = new AnimationCurve(newKeys.ToArray());
                    AnimationUtility.SetEditorCurve(newClip, binding, newCurve);
                }
                // ¶
                newClip.EnsureQuaternionContinuity();
                //޸newClipĳΪendTime-startTime

                if (handPoseIndex != 0)
                {
                    foreach (EditorCurveBinding curveBinding in curveBindings)
                    {
                        if (curveBinding.path.Contains("LeftHand") || curveBinding.path.Contains("RightHand"))
                        {
                            AnimationUtility.SetEditorCurve(newClip, curveBinding, null);
                        }
                    }
                    if (handPoseIndex == 1)
                    {
                        UnityEngine.Object[] objects = AssetDatabase.LoadAllAssetsAtPath(StringConst.handFistPosePath);
                        AnimationClip fistPosClip = objects.Select(asset => asset as AnimationClip).Where(clip => clip != null && !clip.name.StartsWith("__preview__")).ToArray()[0];
                        curveBindings = AnimationUtility.GetCurveBindings(fistPosClip);
                        foreach (EditorCurveBinding curveBinding in curveBindings)
                        {
                            Debug.Log($"curveBinding: {curveBinding.path}.{curveBinding.propertyName}");
                            if (curveBinding.propertyName.Contains("LeftHand") || curveBinding.propertyName.Contains("RightHand"))
                            {
                                AnimationCurve curve = AnimationUtility.GetEditorCurve(fistPosClip, curveBinding);
                                AnimationUtility.SetEditorCurve(newClip, curveBinding, curve);
                            }
                        }
                    }
                    else
                    {
                        UnityEngine.Object[] objects = AssetDatabase.LoadAllAssetsAtPath(StringConst.handPalmsPosePath);
                        AnimationClip palmsPosClip = objects.Select(asset => asset as AnimationClip).Where(clip => clip != null && !clip.name.StartsWith("__preview__")).ToArray()[0];
                        curveBindings = AnimationUtility.GetCurveBindings(palmsPosClip);
                        foreach (EditorCurveBinding curveBinding in curveBindings)
                        {
                            Debug.Log($"curveBinding: {curveBinding.path}.{curveBinding.propertyName}");
                            if (curveBinding.propertyName.Contains("LeftHand") || curveBinding.propertyName.Contains("RightHand"))
                            {
                                AnimationCurve curve = AnimationUtility.GetEditorCurve(palmsPosClip, curveBinding);
                                AnimationUtility.SetEditorCurve(newClip, curveBinding, curve);
                            }
                        }
                    }
                    
                }
                StringBuilder stringBuilder = new StringBuilder();
                // ־TempĿ¼
                string logPath = $"{Path.GetDirectoryName(Application.dataPath)}/ExtractAnimationLog.txt";
                File.WriteAllText(logPath, stringBuilder.ToString());
                AssetDatabase.CreateAsset(newClip, newClipPath);
                AssetDatabase.SaveAssets();
                AssetDatabase.Refresh();
                outputClip = AssetDatabase.LoadAssetAtPath<AnimationClip>(newClipPath);
                Debug.Log($"Animation clip '{sourceClip.name}' has been extracted and saved to '{newClipPath}'");
            }

        }

        private void UpdateModeSettting()
        {
            if (oldSelectedModel == selectedModel)
            {
                return;
            }
            oldSelectedModel = selectedModel;
            animatinoClipNames = new string[0];
            if (selectedModel != null)
            {
                ModelImporter modelImporter = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(selectedModel)) as ModelImporter;
                if (modelImporter != null)
                {
                    modelImporter.animationType = ModelImporterAnimationType.Human;
                    modelImporter.avatarSetup = ModelImporterAvatarSetup.CreateFromThisModel;
                    ImportAnimation(modelImporter);
                    modelImporter.SaveAndReimport();
                }
                string assetPath = AssetDatabase.GetAssetPath(selectedModel);
                UnityEngine.Object[] assets = AssetDatabase.LoadAllAssetsAtPath(assetPath);
                AnimationClip[] animations = assets.Select(asset => asset as AnimationClip).Where(clip => clip != null && !clip.name.StartsWith("__preview__")).ToArray();
                animatinoClipNames = animations.Select(clip => clip.name).ToArray();
                int index = 0;
                for (int i = 0; i < animatinoClipNames.Length; i++)
                {
                    if (animatinoClipNames[i] == selectedClip?.name)
                    {
                        index = i;
                        break;
                    }
                }
                selectedClipIndex = index;
            }
        }

        private void DrawModelTabview(Rect containerRect)
        {
            var rect = EditorGUILayout.BeginHorizontal();
            GUI.enabled = false;
            selectedModel = EditorGUILayout.ObjectField(StringPanel.modelTab, selectedModel, typeof(GameObject), true) as GameObject;
            GUI.enabled = true;
            if (GUILayout.Button(StringPanel.stringImport, GUILayout.Width(100)))
            {
                selectedModel = FbxImporter.ImportFbx();
                selectedClipIndex = 0;
                //ԶseletedModelanimationType ΪHumanoid
            }
            EditorGUILayout.EndHorizontal();

            if (selectedModel != oldSelectedModel)
            {
                UpdateModeSettting();
            }

            if (selectedModel != null)
            {
                string assetPath = AssetDatabase.GetAssetPath(selectedModel);
                ModelImporter modelImporter = AssetImporter.GetAtPath(assetPath) as ModelImporter;
                if (modelImporter != null)
                {
                    modelImporter.animationType = (ModelImporterAnimationType)EditorGUILayout.EnumPopup(StringPanel.animationType, modelImporter.animationType);
                    modelImporter.avatarSetup = (ModelImporterAvatarSetup)EditorGUILayout.EnumPopup(StringPanel.avatarSetup, modelImporter.avatarSetup);
                    modelImporter.skinWeights = (ModelImporterSkinWeights)EditorGUILayout.EnumPopup(StringPanel.skinWeights, modelImporter.skinWeights);
                    modelImporter.importAnimation = EditorGUILayout.Toggle(StringPanel.importAnimation, modelImporter.importAnimation);
                    bool hasAnimation = animatinoClipNames.Length > 0;
                    if (hasAnimation)
                    {
                        EditorGUILayout.BeginHorizontal();
                        EditorGUILayout.LabelField(StringPanel.animationClip, GUILayout.Width(100));
                        string[] clipNames = animatinoClipNames;
                        selectedClipIndex = selectedClipIndex < clipNames.Length ? selectedClipIndex : 0;
                        selectedClipIndex = EditorGUILayout.Popup(selectedClipIndex, clipNames);
                        string clipName = clipNames[selectedClipIndex];

                        UnityEngine.Object[] assets = AssetDatabase.LoadAllAssetsAtPath(assetPath);
                        AnimationClip[] animations = assets.Select(asset => asset as AnimationClip).Where(clip => clip != null && !clip.name.StartsWith("__preview__")).ToArray();
                        EditorGUILayout.EndHorizontal();
                        bool containsAnimation = false;
                        foreach (AnimationClip clip in animations)
                        {
                            if (selectedClip == clip)
                            {
                                containsAnimation = true;
                            }
                        }
                        if (!containsAnimation)
                            selectedClip = animations[selectedClipIndex];
                        InitializeAnimator();
                    }
                    else
                    {
                        EditorGUILayout.HelpBox($"No animation clip found, importAnimation:{modelImporter.importAnimation}, clip num:{modelImporter.clipAnimations.Length}", MessageType.Info);
                        selectedClip = null;
                        //musicLength = 0;
                    }
                }
                if (GUILayout.Button(StringPanel.apply))
                {
                    modelImporter.SaveAndReimport();
                }
            }
        }

        private void InitializeAnimator()
        {
            if (previewNpc == null)
                return;
            Animator animator = previewNpc.GetComponent<Animator>();
            if (animator == null)
            {
                animator = previewNpc.AddComponent<Animator>();
            }
            AnimatorController controller = animator.runtimeAnimatorController as AnimatorController;
            if (controller == null)
            {
                controller = AssetDatabase.LoadAssetAtPath<AnimatorController>(StringConst.controllerResPath);
                if (controller == null)
                {
                    controller = AnimatorController.CreateAnimatorControllerAtPath(StringConst.controllerResPath);
                }
                animator.runtimeAnimatorController = controller;
            }
            if (selectedClip == null)
            {
                Debug.LogError("No animation clip found");
                return;
            }

            bool clipExits = false;
            foreach (ChildAnimatorState state in controller.layers[0].stateMachine.states)
            {
                if (state.state.name == selectedClip.name)
                {
                    clipExits = true;
                    state.state.motion = selectedClip;
                    state.state.iKOnFeet = enableFootIK;
                    break;
                }
            }
            if (!clipExits)
            {
                AnimatorState state = controller.layers[0].stateMachine.AddState(selectedClip.name);
                state.motion = selectedClip;
                state.iKOnFeet = enableFootIK;
            }
        }

        private void InitializeAudioSource()
        {
            if (audioSource == null)
            {
                GameObject audioSourceObject = new GameObject(StringPanel.audioSource);
                audioSource = audioSourceObject.AddComponent<AudioSource>();
            }
            if (selectedAudioClip != null && selectedAudioClip != audioSource.clip)
            {
                audioSource.clip = selectedAudioClip;
                audioSource.loop = true;
            }
        }

        public ModelImporterClipAnimation CopyModelImporterClipAnimation(ModelImporterClipAnimation clip)
        {
            ModelImporterClipAnimation newAnimation = new ModelImporterClipAnimation()
            {
                name = clip.name,
                takeName = clip.name,
                firstFrame = clip.firstFrame,
                lastFrame = clip.lastFrame,
                loopTime = clip.loopTime,
                loopPose = clip.loopPose,
                cycleOffset = clip.cycleOffset,

                lockRootRotation = clip.lockRootRotation,
                keepOriginalOrientation = clip.keepOriginalOrientation,
                rotationOffset = clip.rotationOffset,

                lockRootHeightY = clip.lockRootHeightY,
                keepOriginalPositionY = clip.keepOriginalPositionY,
                heightFromFeet = clip.heightFromFeet,
                heightOffset = clip.heightOffset,

                lockRootPositionXZ = clip.lockRootPositionXZ,
                keepOriginalPositionXZ = clip.keepOriginalPositionXZ,

                mirror = clip.mirror,
            };
            return newAnimation;
        }

        public bool CompareModelImporterClipAnimation(ModelImporterClipAnimation clip1, ModelImporterClipAnimation clip2)
        {
            if (clip1 == null || clip2 == null) return false;
            if (clip1.name != clip2.name) return false;
            if (clip1.takeName != clip2.takeName) return false;
            if (clip1.firstFrame != clip2.firstFrame) return false;
            if (clip1.lastFrame != clip2.lastFrame) return false;
            if (clip1.loopTime != clip2.loopTime) return false;
            if (clip1.loopPose != clip2.loopPose) return false;
            if (clip1.cycleOffset != clip2.cycleOffset) return false;
            if (clip1.lockRootRotation != clip2.lockRootRotation) return false;
            if (clip1.keepOriginalOrientation != clip2.keepOriginalOrientation) return false;
            if (clip1.rotationOffset != clip2.rotationOffset) return false;
            if (clip1.lockRootHeightY != clip2.lockRootHeightY) return false;
            if (clip1.keepOriginalPositionY != clip2.keepOriginalPositionY) return false;
            if (clip1.heightFromFeet != clip2.heightFromFeet) return false;
            if (clip1.heightOffset != clip2.heightOffset) return false;
            if (clip1.lockRootPositionXZ != clip2.lockRootPositionXZ) return false;
            if (clip1.keepOriginalPositionXZ != clip2.keepOriginalPositionXZ) return false;
            if (clip1.mirror != clip2.mirror) return false;
            return true;
        }

        private void DrawAnimationTabview(Rect containerRect)
        {
            selectedClip = EditorGUILayout.ObjectField(StringPanel.animationClip, selectedClip, typeof(AnimationClip), false) as AnimationClip;
            if (selectedClip != null)
            {
                string clipAssetPath = AssetDatabase.GetAssetPath(selectedClip);
                bool isFbx = false;
                if (Path.GetExtension(clipAssetPath).ToLower().Contains("fbx") && selectedClip != oldSelectedClip)
                {
                    selectedModel = AssetDatabase.LoadAssetAtPath<GameObject>(clipAssetPath);
                    UpdateModeSettting();
                    isFbx = true;
                }
                oldSelectedClip = selectedClip;
                if (!selectedClip.isHumanMotion)
                {
                    EditorGUILayout.HelpBox("The animation clip is not a humanoid animation", MessageType.Info);
                    return;
                }
                EditorGUILayout.BeginHorizontal();
                EditorGUILayout.LabelField(StringPanel.stringLength, GUILayout.Width(100));
                EditorGUILayout.LabelField(selectedClip.length.ToString(), GUILayout.Width(100));
                EditorGUILayout.EndHorizontal();
                if (isFbx)
                {
                    ModelImporterClipAnimation clipAnimation = clipAnimations[selectedClipIndex];
                    ModelImporterClipAnimation copyClipAnimation = CopyModelImporterClipAnimation(clipAnimation);
                    // ȡLoop Time
                    clipAnimation.loopTime = EditorGUILayout.Toggle(StringPanel.loopTime, clipAnimation.loopTime);
                    GUI.enabled = clipAnimation.loopTime;
                    EditorGUI.indentLevel++;
                    clipAnimation.loopPose = EditorGUILayout.Toggle(StringPanel.loopPose, clipAnimation.loopPose);
                    clipAnimation.cycleOffset = EditorGUILayout.FloatField(StringPanel.cycleOffset, clipAnimation.cycleOffset);
                    EditorGUI.indentLevel--;
                    GUI.enabled = true;
                    // ȡRoot Transform Rotation
                    EditorGUILayout.LabelField(StringPanel.rootTransformRotation, EditorStyles.boldLabel);
                    EditorGUI.indentLevel++;
                    clipAnimation.lockRootRotation = EditorGUILayout.Toggle(StringPanel.bakeIntoPose, clipAnimation.lockRootRotation);
                    int rootBakedup = clipAnimation.keepOriginalOrientation ? 0 : 1;
                    rootBakedup = EditorGUILayout.Popup(StringPanel.basedUpon, rootBakedup, StringPanel.transformRotationPopups);
                    clipAnimation.keepOriginalOrientation = rootBakedup == 0;
                    clipAnimation.rotationOffset = EditorGUILayout.FloatField(StringPanel.offset, clipAnimation.rotationOffset);
                    EditorGUI.indentLevel--;

                    // ȡRoot Transform Position (Y)
                    EditorGUILayout.LabelField(StringPanel.rootTransformPositionY, EditorStyles.boldLabel);
                    EditorGUI.indentLevel++;
                    clipAnimation.lockRootHeightY = EditorGUILayout.Toggle(StringPanel.bakeIntoPose, clipAnimation.lockRootHeightY);
                    int rootPositionYBasedUpon = clipAnimation.keepOriginalPositionY ? 0 : 1;
                    rootPositionYBasedUpon = clipAnimation.heightFromFeet ? 2 : rootPositionYBasedUpon;
                    rootPositionYBasedUpon = EditorGUILayout.Popup(StringPanel.bakeIntoPose, rootPositionYBasedUpon, StringPanel.transformPositionYPopups);
                    clipAnimation.keepOriginalPositionY = rootPositionYBasedUpon == 0;
                    clipAnimation.heightFromFeet = rootPositionYBasedUpon == 2;
                    clipAnimation.heightOffset = EditorGUILayout.FloatField(StringPanel.offset, clipAnimation.heightOffset);
                    EditorGUI.indentLevel--;

                    //// ȡRoot Transform Position (XZ)
                    EditorGUILayout.LabelField(StringPanel.rootTransformPositionXZ, EditorStyles.boldLabel);
                    EditorGUI.indentLevel++;
                    clipAnimation.lockRootPositionXZ = EditorGUILayout.Toggle(StringPanel.bakeIntoPose, clipAnimation.lockRootPositionXZ);
                    int rootPositionXBasedUpon = clipAnimation.keepOriginalPositionXZ ? 0 : 1;
                    rootPositionXBasedUpon = EditorGUILayout.Popup(StringPanel.basedUpon, rootPositionXBasedUpon, StringPanel.transformPositionXZPopups);
                    clipAnimation.keepOriginalPositionXZ = rootPositionXBasedUpon == 0;
                    EditorGUI.indentLevel--;

                    //
                    clipAnimation.mirror = EditorGUILayout.Toggle(StringPanel.mirror, clipAnimation.mirror);
                    if (!CompareModelImporterClipAnimation(copyClipAnimation, clipAnimation))
                    {
                        string assetPath = AssetDatabase.GetAssetPath(selectedModel);
                        ModelImporter modelImporter = AssetImporter.GetAtPath(assetPath) as ModelImporter;
                        modelImporter.clipAnimations = clipAnimations.ToArray();
                        modelImporter.SaveAndReimport();
                    }
                }
                else
                {
                    AnimationClipSettings clipSettings = AnimationUtility.GetAnimationClipSettings(selectedClip);
                    clipSettings.loopTime = EditorGUILayout.Toggle(StringPanel.loopTime, clipSettings.loopTime);
                    GUI.enabled = clipSettings.loopTime;
                    EditorGUI.indentLevel++;
                    clipSettings.loopBlend = EditorGUILayout.Toggle(StringPanel.loopPose, clipSettings.loopBlend);
                    clipSettings.cycleOffset = EditorGUILayout.FloatField(StringPanel.cycleOffset, clipSettings.cycleOffset);
                    EditorGUI.indentLevel--;
                    GUI.enabled = true;

                    EditorGUILayout.LabelField(StringPanel.rootTransformRotation, EditorStyles.boldLabel);
                    EditorGUI.indentLevel++;
                    //clipAnimation.lockRootRotation = EditorGUILayout.Toggle(StringPanel.bakeIntoPose, clipAnimation.lockRootRotation);
                    clipSettings.loopBlendOrientation = EditorGUILayout.Toggle(StringPanel.bakeIntoPose, clipSettings.loopBlendOrientation);
                    int rootBakedup = clipSettings.keepOriginalOrientation ? 0 : 1;
                    rootBakedup = EditorGUILayout.Popup(StringPanel.basedUpon, rootBakedup, StringPanel.transformRotationPopups);
                    clipSettings.keepOriginalOrientation = rootBakedup == 0;
                    clipSettings.orientationOffsetY = EditorGUILayout.FloatField(StringPanel.offset, clipSettings.orientationOffsetY);
                    EditorGUI.indentLevel--;
                    // ȡRoot Transform Position (Y)
                    EditorGUILayout.LabelField(StringPanel.rootTransformPositionY, EditorStyles.boldLabel);
                    EditorGUI.indentLevel++;
                    clipSettings.loopBlendPositionY = EditorGUILayout.Toggle(StringPanel.bakeIntoPose, clipSettings.loopBlendPositionY);
                    int rootPositionYBasedUpon = clipSettings.keepOriginalPositionY ? 0 : 1;
                    rootPositionYBasedUpon = clipSettings.heightFromFeet ? 2 : rootPositionYBasedUpon;
                    rootPositionYBasedUpon = EditorGUILayout.Popup(StringPanel.bakeIntoPose, rootPositionYBasedUpon, StringPanel.transformPositionYPopups);
                    clipSettings.keepOriginalPositionY = rootPositionYBasedUpon == 0;
                    clipSettings.heightFromFeet = rootPositionYBasedUpon == 2;
                    clipSettings.level = EditorGUILayout.FloatField(StringPanel.offset, clipSettings.level);
                    EditorGUI.indentLevel--;

                    //// ȡRoot Transform Position (XZ)
                    EditorGUILayout.LabelField(StringPanel.rootTransformPositionXZ, EditorStyles.boldLabel);
                    EditorGUI.indentLevel++;
                    clipSettings.loopBlendPositionXZ = EditorGUILayout.Toggle(StringPanel.bakeIntoPose, clipSettings.loopBlendPositionXZ);
                    int rootPositionXBasedUpon = clipSettings.keepOriginalPositionXZ ? 0 : 1;
                    rootPositionXBasedUpon = EditorGUILayout.Popup(StringPanel.basedUpon, rootPositionXBasedUpon, StringPanel.transformPositionXZPopups);
                    clipSettings.keepOriginalPositionXZ = rootPositionXBasedUpon == 0;
                    EditorGUI.indentLevel--;
                    //
                    clipSettings.mirror = EditorGUILayout.Toggle(StringPanel.mirror, clipSettings.mirror);
                    AnimationUtility.SetAnimationClipSettings(selectedClip, clipSettings);
                }

                handPoseIndex = EditorGUILayout.Popup(StringPanel.handAniStyle, handPoseIndex, StringPanel.handPoseStylePopups);
                bool oldEnableFootIK = enableFootIK;
                enableFootIK = EditorGUILayout.Toggle(StringPanel.FootIK, enableFootIK);
                if (enableFootIK != oldEnableFootIK)
                {
                    InitializeAnimator();
                }
                if (previewNpc != null)
                {
                    Animator animator = previewNpc.GetComponent<Animator>();
                    int handType = animator.GetInteger("handtype");
                    if (handType != handPoseIndex)
                    {
                        animator.SetInteger("handtype", handPoseIndex);
                    }
                }

            }
            else
            {
                EditorGUILayout.HelpBox("No animation clip found", MessageType.Info);
            }
        }

        private void ResizeTextureToPowerOfTwo(Texture2D texture)
        {
            string path = AssetDatabase.GetAssetPath(texture);
            TextureImporter importer = AssetImporter.GetAtPath(path) as TextureImporter;

            if (importer != null)
            {
                importer.isReadable = true;
                AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate);

                int newWidth = Mathf.ClosestPowerOfTwo(texture.width);
                int newHeight = Mathf.ClosestPowerOfTwo(texture.height);
                int minSize = Mathf.Min(newWidth, newHeight);

                Texture2D resizedTexture = new Texture2D(minSize, minSize, texture.format, false);
                //Graphics.ConvertTexture(texture, resizedTexture);
                for(int x = 0; x < minSize; x++)
                {
                    for (int y = 0; y < minSize; y++)
                    {
                        resizedTexture.SetPixel(x, y, texture.GetPixelBilinear((float)x / minSize, (float)y / minSize));
                    }
                }
                byte[] bytes;
                if (path.EndsWith(".png"))
                {
                    bytes = resizedTexture.EncodeToPNG();
                }
                else if(path.EndsWith(".tga"))
                {
                    bytes = resizedTexture.EncodeToTGA();
                }
                else
                {
                    bytes = resizedTexture.EncodeToJPG();
                }
                System.IO.File.WriteAllBytes(path, bytes);

                AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate);
                Debug.Log($"Texture resized to {newWidth}x{newHeight}");
            }
            else
            {
                Debug.LogError("Failed to get TextureImporter.");
            }
        }

        public void DrawMusicTabview(Rect containerRect)
        {
            var rect = EditorGUILayout.BeginHorizontal();
            selectedAudioClip = EditorGUILayout.ObjectField(StringPanel.audioClip, selectedAudioClip, typeof(AudioClip), false) as AudioClip;
            if (GUILayout.Button(StringPanel.stringImport, GUILayout.Width(100)))
            {
                selectedAudioClip = AudioImporter.ImportAudo();
            }
            EditorGUILayout.EndHorizontal();
            if (oldSelectedAudioClip != selectedAudioClip)
            {
                oldSelectedAudioClip = selectedAudioClip;
                InitializeAudioSource();
                if (selectedAudioClip == null)
                {
                    musicName = "";
                    musicStar = 0;
                    musicExerciseIntensity = 0;
                    musicBpm = 0;
                    musicStartTime = 0;
                    //musicLength = 0;
                    musicType = 0;
                }
                else
                {
                    musicName = selectedAudioClip.name;
                    musicBpm = UniBpmAnalyzer.AnalyzeBpm(selectedAudioClip);
                }
                if (musicTimeline != null)
                {
                    musicTimeline.EndTime = selectedAudioClip.length;
                    musicTimeline.StartTime = 0;
                    musicTimeline.Duration = selectedAudioClip.length;
                    musicTimeline.Speed = 1;
                }
            }
            if (selectedAudioClip == null)
            {
                EditorGUILayout.HelpBox("No audio clip found", MessageType.Info);
            }
            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.LabelField(StringPanel.coverImage, EditorStyles.label);
            if (GUILayout.Button(StringPanel.stringImport, GUILayout.Width(100)))
            {
                selectedCoverTexture = ImageImporter.ImportImage();
                if (selectedCoverTexture != null)
                {
                    if (!Mathf.IsPowerOfTwo(selectedCoverTexture.width) || !Mathf.IsPowerOfTwo(selectedCoverTexture.height))
                    {
                        ResizeTextureToPowerOfTwo(selectedCoverTexture);
                    }
                }
            }
            EditorGUILayout.EndHorizontal();
            EditorGUILayout.BeginHorizontal();
            GUI.enabled = false;
            selectedCoverTexture = EditorGUILayout.ObjectField(selectedCoverTexture, typeof(Texture2D), false, GUILayout.Width(64), GUILayout.Height(64)) as Texture2D;
            GUI.enabled = true;
            EditorGUILayout.HelpBox("Suggested size 512 x 512", MessageType.Info);
            //Vector2 size = selectedCoverTexture.siz
            //ͼƬߴǷ2ݣǣʾһťͼƬΪӽ2

            EditorGUILayout.EndHorizontal();
            musicName = EditorGUILayout.TextField(StringPanel.musicName, musicName);
            singerName = EditorGUILayout.TextField(StringPanel.singerName, singerName);
            musicStar = EditorGUILayout.IntSlider(StringPanel.star, musicStar, 1, 4);
            musicExerciseIntensity = EditorGUILayout.IntSlider(StringPanel.exerciseIntensity, musicExerciseIntensity, 1, 3);
            EditorGUILayout.BeginHorizontal();
            musicBpm = EditorGUILayout.IntField(StringPanel.bpm, musicBpm);
            if(GUILayout.Button(StringPanel.calBPM, GUILayout.Width(200)) && selectedAudioClip != null)
            {
                float calBpm = UniBpmAnalyzer.AnalyzeBpm(selectedAudioClip);
                musicBpm = (int)calBpm;
            }
            EditorGUILayout.EndHorizontal();
            musicStartTime = EditorGUILayout.FloatField(StringPanel.animationStartTime, musicStartTime);
            GUI.enabled = false;
            //musicLength = EditorGUILayout.FloatField("Music Length", animationTimeline.Duration - animationTimeline.StartTime + musicStartTime);
            float danceLength = animationTimeline != null ? (animationTimeline.Duration - animationTimeline.StartTime) : 0;
            EditorGUILayout.FloatField(StringPanel.danceLength, danceLength);
            GUI.enabled = true;
            string[] typeNames = Enum.GetNames(typeof(MusicType));
            //бѡ
            musicType = EditorGUILayout.MaskField(StringPanel.type, musicType, typeNames);
        }

        private void CreateDefaultScene()
        {
            var newScene = EditorSceneManager.NewScene(UnityEditor.SceneManagement.NewSceneSetup.DefaultGameObjects, UnityEditor.SceneManagement.NewSceneMode.Single);
            newScene.name = StringConst.sceneName;
            //泡StringConst.previewScenePath
            ResetCamera();
            CreateNPC();
            InitializeAudioSource();
            EditorSceneManager.SaveScene(newScene, StringConst.previewScenePath);
            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();
        }

        private void CreateNPC()
        {
            //ĬNPC
            string prefabPath = GetRenderPipeline() == RenderPipeline.URP ? StringConst.maleURPPrefabPath : StringConst.maleBuiltInPrefabPath;
            if (defaultNpc == 1)
            {
                prefabPath = GetRenderPipeline() == RenderPipeline.URP ? StringConst.femaleURPPrefabPath : StringConst.femaleBuiltInPrefabPath;
            }
            var asset = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
            previewNpc = Instantiate(asset, Vector3.zero, Quaternion.identity);
            previewNpc.name = StringConst.npcName;
            if (selectedClip != null)
            {
                InitializeAnimator();
            }
        }

        public void ResetCamera()
        {
            SceneView sceneView = SceneView.lastActiveSceneView;
            if (sceneView != null)
            {
                //  SceneView λúת
                sceneView.pivot = new Vector3(0, 1, 0);
                sceneView.rotation = Quaternion.Euler(10, 180, 0);
                sceneView.size = 1;
                sceneView.orthographic = false; // ȷ͸ģʽ
                sceneView.Repaint();
                Debug.Log("Scene View camera has been reset to perspective mode.");
            }
        }

        public void DrawPreview(Rect containerRect)
        {
            foldoutScene = EditorGUILayout.Foldout(foldoutScene, StringPanel.stringScene);
            if (foldoutScene)
            {
                EditorGUILayout.BeginHorizontal();
                if (GUILayout.Button(StringPanel.createDefaultScene))
                {
                    CreateDefaultScene();
                }
                if (GUILayout.Button(StringPanel.resetCamera))
                {
                    ResetCamera();
                }
                EditorGUILayout.EndHorizontal();

                EditorGUILayout.BeginHorizontal();
                defaultNpc = EditorGUILayout.Popup(StringPanel.npcLable, defaultNpc, StringPanel.npcName);
                if (GUILayout.Button("Fresh"))
                {
                    if (previewNpc != null)
                    {
                        DestroyImmediate(previewNpc);
                    }
                    //ĬNPC
                    CreateNPC();
                }
                EditorGUILayout.EndHorizontal();
            }

            string foldAnimation = StringPanel.animationTab;
            if (selectedClip != null && animationTimeline != null && foldoutAnimation)
            {
                foldAnimation += $"({(animationTimeline.CurTime - animationTimeline.StartTime).ToString("0.00")}s)";
            }
            EditorGUILayout.BeginHorizontal();
            foldoutAnimation = EditorGUILayout.Foldout(foldoutAnimation, foldAnimation);
            if (foldoutAnimation && animationTimeline != null)
            {
                // Slider
                animationTimeline.Speed = GUILayout.HorizontalSlider(animationTimeline.Speed, 0.1f, 2.0f);
                GUILayout.Label(animationTimeline.Speed.ToString("0.00") + "x");
            }
            EditorGUILayout.EndHorizontal();
            if (foldoutAnimation)
            {
                if (selectedClip != null)
                {
                    if (animationTimeline == null)
                    {
                        animationTimeline = new UGCTimeline();
                    }
                    Rect rect = EditorGUILayout.GetControlRect(GUILayout.Height(30));

                    animationTimeline.PositionX = rect.x;
                    animationTimeline.PositionY = rect.y;
                    animationTimeline.ScreenPosition = contentRect.position;
                    animationTimeline.Width = rect.width;
                    animationTimeline.EndTime = selectedClip.length;
                    animationTimeline.Framerate = selectedClip.frameRate;
                    animationTimeline.Update();
                    if (animationTimeline.Duration < 0 || animationTimeline.Duration > selectedClip.length)
                    {
                        animationTimeline.Duration = selectedClip.length;
                    }
                    EditorGUILayout.BeginHorizontal();
                    EditorGUIUtility.labelWidth = 100;
                    animationTimeline.StartTime = EditorGUILayout.FloatField(StringPanel.startTime, animationTimeline.StartTime, GUILayout.Width(200));
                    if (GUILayout.Button(StringPanel.startEnterButton, GUILayout.Width(200)))
                    {
                        animationTimeline.StartTime = animationTimeline.CurTime;
                        GUI.FocusControl(null);
                    }
                    animationTimeline.Duration = EditorGUILayout.FloatField(StringPanel.endTime, animationTimeline.Duration, GUILayout.Width(200));
                    if (GUILayout.Button(StringPanel.endEnterButton, GUILayout.Width(200)))
                    {
                        animationTimeline.Duration = animationTimeline.CurTime;
                        GUI.FocusControl(null);
                    }
                    EditorGUIUtility.labelWidth = 0;
                    EditorGUILayout.EndHorizontal();
                    float minTime = animationTimeline.StartTime;
                    float maxTime = animationTimeline.Duration;
                    EditorGUILayout.MinMaxSlider(ref minTime, ref maxTime, 0, selectedClip.length);
                    animationTimeline.StartTime = minTime;
                    animationTimeline.Duration = maxTime;
                    float danceStartTime = musicTimeline != null ? musicTimeline.StartTime : 0;
                    //musicLength = maxTime - minTime + musicStartTime;
                }
                else
                {
                    EditorGUILayout.HelpBox("No animation clip found", MessageType.Info);
                }
            }

            string foldMusic = StringPanel.musicTab;
            if (selectedAudioClip != null && musicTimeline != null && foldoutMusic)
            {
                foldMusic += $"({(musicTimeline.CurTime - musicTimeline.StartTime).ToString("0.00")}s)";
            }
            EditorGUILayout.BeginHorizontal();
            foldoutMusic = EditorGUILayout.Foldout(foldoutMusic, foldMusic);
            if (foldoutMusic && musicTimeline != null)
            {
                // Slider
                musicTimeline.Speed = GUILayout.HorizontalSlider(musicTimeline.Speed, 0.1f, 2.0f);
                GUILayout.Label(musicTimeline.Speed.ToString("0.00") + "x");
            }
            EditorGUILayout.EndHorizontal();
            if (foldoutMusic)
            {
                EditorGUILayout.BeginHorizontal();
                autoPlayAnimation = EditorGUILayout.Toggle(StringPanel.autoPlayAnimation, autoPlayAnimation);
                musicStartTime = EditorGUILayout.FloatField(StringPanel.animationStartTime, musicStartTime);
                EditorGUILayout.EndHorizontal();
                if (selectedAudioClip != null)
                {
                    if (musicTimeline == null)
                    {
                        musicTimeline = new UGCTimeline();
                    }
                    Rect rect = EditorGUILayout.GetControlRect(GUILayout.Height(30));
                    //Debug.Log($"Draw Music rect:{rect}, contentRect:{contentRect}");
                    musicTimeline.PositionX = rect.x;
                    musicTimeline.PositionY = rect.y;
                    musicTimeline.ScreenPosition = contentRect.position;
                    musicTimeline.Width = rect.width;
                    musicTimeline.EndTime = selectedAudioClip.length;
                    musicTimeline.Framerate = 1;
                    musicTimeline.Update();
                    if (musicTimeline.Duration < 0 || musicTimeline.Duration > selectedAudioClip.length)
                    {
                        musicTimeline.Duration = selectedAudioClip.length;
                    }
                    GUI.enabled = false;
                    EditorGUILayout.BeginHorizontal();
                    EditorGUIUtility.labelWidth = 100;
                    musicTimeline.StartTime = EditorGUILayout.FloatField(StringPanel.startTime, 0, GUILayout.Width(200));
                    musicTimeline.Duration = EditorGUILayout.FloatField(StringPanel.endTime, selectedAudioClip ? selectedAudioClip.length : 0, GUILayout.Width(200));
                    EditorGUIUtility.labelWidth = 0;
                    EditorGUILayout.EndHorizontal();
                    float minTime = musicTimeline.StartTime;
                    float maxTime = musicTimeline.Duration;
                    EditorGUILayout.MinMaxSlider(ref minTime, ref maxTime, 0, selectedAudioClip.length);
                    musicTimeline.StartTime = minTime;
                    musicTimeline.Duration = maxTime;
                    if (autoPlayAnimation && animationTimeline != null)
                    {
                        animationTimeline.Playing = musicTimeline.Playing;
                    }
                    GUI.enabled = true;
                }
                else
                {
                    EditorGUILayout.HelpBox("No audio clip found", MessageType.Info);
                }
            }
        }

        private void DrawTabsView()
        {
            //ñɫ
            Color originalColor = GUI.backgroundColor;
            //ǳɫ
            string[] tabs = Enum.GetNames(typeof(TabsType));
            string selectedTabName = Enum.GetName(typeof(TabsType), this.selectedTab);
            Rect rect = EditorGUILayout.BeginHorizontal();
            for (int i = 0; i < tabs.Length; i++)
            {
                GUIStyle buttonStyle = Styles.unSelectedTabView;
                if (selectedTabName == tabs[i])
                {
                    buttonStyle = Styles.selectedTabView;
                }
                if (GUILayout.Button(tabs[i], buttonStyle))
                {
                    this.selectedTab = (TabsType)Enum.Parse(typeof(TabsType), tabs[i]);
                }
            }
            EditorGUILayout.EndHorizontal();
            contentRect.y = rect.y;

            // ƶ̬ȵGroupView
            rect = EditorGUILayout.BeginVertical(GUI.skin.box, GUILayout.Height(400));
            contentRect.y = rect.y;
            scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition, GUILayout.Height(400));
            switch (selectedTab)
            {
                case TabsType.Model:
                    DrawModelTabview(rect);
                    break;
                case TabsType.Animation:
                    DrawAnimationTabview(rect);
                    break;
                case TabsType.Music:
                    DrawMusicTabview(rect);
                    break;
                case TabsType.Preview:
                    DrawPreview(rect);
                    break;
                default: break;
            }
            EditorGUILayout.EndScrollView();

            EditorGUILayout.EndVertical();
        }

        private void ImportProject(string[] guids)
        {
            string path = AssetDatabase.GUIDToAssetPath(guids[projectIndex]);
            ProjectSetting setting = AssetDatabase.LoadAssetAtPath<ProjectSetting>(path);
            //model
            string modelPath = AssetDatabase.GUIDToAssetPath(setting.gameObjectGuid);
            selectedModel = AssetDatabase.LoadAssetAtPath<GameObject>(modelPath);
            //audio
            string audioPath = AssetDatabase.GUIDToAssetPath(setting.audioClipGuid);
            selectedAudioClip = AssetDatabase.LoadAssetAtPath<AudioClip>(audioPath);
            AudioConfig audioConfig = setting.audioConfig;
            if (audioConfig != null)
            {
                musicName = audioConfig.musicName;
                singerName = audioConfig.singerName;
                musicStar = audioConfig.musicStarLevel;
                musicBpm = audioConfig.musicBpm;
                musicStartTime = audioConfig.musicStartTime;
                //musicLength = audioConfig.musicLength;
                musicType = audioConfig.musicType;
                musicExerciseIntensity = audioConfig.exerciseIntensity;
            }
            musicTimeline = new UGCTimeline();
            if (audioConfig != null && audioConfig.endTime > 0)
            {
                musicTimeline.EndTime = audioConfig.musicLength;
                musicTimeline.StartTime = audioConfig.startTime;
                musicTimeline.Duration = audioConfig.endTime;
                musicTimeline.Speed = audioConfig.speed;
            }
            else if (selectedAudioClip != null)
            {
                musicTimeline.EndTime = selectedAudioClip.length;
                musicTimeline.StartTime = 0;
                musicTimeline.Duration = selectedAudioClip.length;
                musicTimeline.Speed = 1;
            }

            string coverPath = AssetDatabase.GUIDToAssetPath(audioConfig.coverImageGuid);
            selectedCoverTexture = AssetDatabase.LoadAssetAtPath<Texture2D>(coverPath);
            //animation
            AnimatinConfig animationConfig = setting.animatinConfig;
            if (selectedModel != null)
            {
                ModelImporter importer = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(selectedModel)) as ModelImporter;
                clipAnimations = new List<ModelImporterClipAnimation>();
                foreach (ModelImporterClipAnimation clip in importer.clipAnimations)
                {
                    if (!clip.name.Contains("__preview__"))
                    {
                        clipAnimations.Add(CopyModelImporterClipAnimation(clip));
                    }
                }
                UnityEngine.Object[] assets = AssetDatabase.LoadAllAssetsAtPath(AssetDatabase.GetAssetPath(selectedModel));
                AnimationClip[] animations = assets.Select(asset => asset as AnimationClip).Where(clip => (clip != null && !clip.name.Contains("__preview__"))).ToArray();
                selectedClip = animations[animationConfig.animationIndex];
                animationTimeline = new UGCTimeline();
                if (animationConfig != null && animationConfig.endTime > 0)
                {
                    animationTimeline.EndTime = animationConfig.length;
                    animationTimeline.StartTime = animationConfig.startTime;
                    animationTimeline.Duration = animationConfig.endTime;
                    animationTimeline.Speed = animationConfig.speed;
                    handPoseIndex = animationConfig.handPoseIndex;
                    enableFootIK = animationConfig.enableFootIK;
                }
                else if (selectedClip != null)
                {
                    animationTimeline.EndTime = selectedClip.length;
                    animationTimeline.StartTime = 0;
                    animationTimeline.Duration = selectedClip.length;
                    animationTimeline.Speed = 1;
                    handPoseIndex = 0;
                    enableFootIK = true;
                }
            }
            else
            {

            }
        }

        private bool SaveProject()
        {
            if (projectName == "")
            {
                Debug.LogError("Please select a project");
                return false;
            }
            ProjectSetting setting = ScriptableObject.CreateInstance<ProjectSetting>();
            setting.gameObjectGuid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(selectedModel));
            setting.animationClipGuid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(selectedClip));
            setting.audioClipGuid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(selectedAudioClip));
            if (outputClip != null)
                setting.outputClipGuid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(outputClip));
            AudioConfig audioConfig = new AudioConfig();
            audioConfig.coverImageGuid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(selectedCoverTexture));
            audioConfig.musicName = musicName;
            audioConfig.singerName = singerName;
            audioConfig.musicStarLevel = musicStar;
            audioConfig.musicBpm = (int)musicBpm;
            audioConfig.musicStartTime = musicStartTime;
            audioConfig.musicLength = musicStartTime + animationTimeline.Duration - animationTimeline.StartTime;
            audioConfig.musicType = musicType;
            audioConfig.exerciseIntensity = musicExerciseIntensity;
            if (musicTimeline != null)
            {
                audioConfig.startTime = musicTimeline.StartTime;
                audioConfig.endTime = musicTimeline.Duration;
                audioConfig.speed = musicTimeline.Speed;
                //audioConfig.musicLength = musicTimeline.EndTime;
            }
            setting.audioConfig = audioConfig;
            AnimatinConfig animatinConfig = new AnimatinConfig();
            if (animationTimeline != null)
            {
                animatinConfig.startTime = animationTimeline.StartTime;
                animatinConfig.endTime = animationTimeline.Duration;
                animatinConfig.speed = animationTimeline.Speed;
                animatinConfig.length = selectedClip != null ? selectedClip.length : 0;
                animatinConfig.animationIndex = selectedClipIndex;
                animatinConfig.handPoseIndex = handPoseIndex;
                animatinConfig.enableFootIK = enableFootIK;
            }
            setting.animatinConfig = animatinConfig;
            string savePath = string.Format(StringConst.savepProjectPath, projectName);

            if (!Directory.Exists(Path.GetDirectoryName(savePath)))
            {
                Directory.CreateDirectory(Path.GetDirectoryName(savePath));
            }
            AssetDatabase.CreateAsset(setting, savePath);
            return true;
        }

        public void DrawProject()
        {
            EditorGUILayout.BeginHorizontal();
            string[] guids = AssetDatabase.FindAssets("t:ProjectSetting");
            string[] projectNames = new string[guids.Length];
            for (int i = 0; i < guids.Length; i++)
            {
                string path = AssetDatabase.GUIDToAssetPath(guids[i]);
                projectNames[i] = Path.GetFileNameWithoutExtension(path);
            }
            projectIndex = projectIndex > projectNames.Length ? 0 : projectIndex;
            projectIndex = EditorGUILayout.Popup(StringPanel.stringProject, projectIndex, projectNames, GUILayout.Width(position.width - 110));
            if (GUILayout.Button(StringPanel.stringImport, GUILayout.Width(100)))
            {
                ImportProject(guids);
                if (projectName != projectNames[projectIndex])
                {
                    projectName = projectNames[projectIndex];
                }
            }
            EditorGUILayout.EndHorizontal();
            EditorGUILayout.BeginHorizontal();
            if (projectName == "" && selectedModel != null)
            {
                projectName = selectedModel.name;
            }
            if (projectName == "" && selectedAudioClip != null)
            {
                projectName = selectedAudioClip.name;
            }
            if (projectName == "" && selectedClip != null)
            {
                projectName = selectedClip.name;
            }
            projectName = EditorGUILayout.TextField(StringPanel.projectName, projectName, GUILayout.Width(position.width - 110));
            if (GUILayout.Button(StringPanel.stringSave, GUILayout.Width(100)))
            {
                SaveProject();
            }
            EditorGUILayout.EndHorizontal();
            bool canBuild = CheckCanBuild(out string msg);
            if (!canBuild)
            {
                EditorGUILayout.HelpBox(msg, MessageType.Warning);
            }
            GUI.enabled = canBuild;
            if (GUILayout.Button(StringPanel.stringBuild))
            {
                string clipPath = AssetDatabase.GetAssetPath(selectedClip);
                Debug.Log($"clipPath:{clipPath}");
                ExtractAndSaveAnimation(projectName, selectedClip.name, animationTimeline.StartTime, animationTimeline.Duration);
                if (!SaveProject())
                {
                    Debug.LogError("Please select a project");
                    return;
                }
                ProjectSetting setting = AssetDatabase.LoadAssetAtPath<ProjectSetting>(string.Format(StringConst.projectSettingPath, projectName));
                //Assets/UGCTools/Builder/{projectName}ļµļAssets/DEETools/Res/Context/{projectName}ļµļһzipѹ浽Assets/../DEETools/Builder/{projectName}.zip
                try
                {
                    AssetBundleBuilder.ExtraAudioConfig(setting, out string animationHashCode, out string audioHashCode, out string coverHashCode, out long timeStamp);
                    AssetDatabase.Refresh();
                    AssetBundleBuilder.UGCBuildAllAssetBundle(setting, ref animationHashCode, ref audioHashCode, ref coverHashCode, ref timeStamp);
                    AssetDatabase.Refresh();
                    CreateZipFromFolders(projectName);
                }
                catch (Exception ex)
                {
                    Debug.LogError(ex.Message);
                }
            }
            GUI.enabled = true;
        }

        // е
        private void OnGUI()
        {
            contentRect = new Rect(0, 0, position.width, position.height);
            //Debug.Log($"ContentRect1:{contentRect}");
            try
            {
                screenPosition = EditorGUILayout.BeginScrollView(screenPosition);
                contentRect = EditorGUILayout.BeginVertical();
                Rect logoRect = EditorGUILayout.GetControlRect(GUILayout.Height(64));
                if (Event.current.type == EventType.Repaint)
                {
                    DrawHeader(logoRect);
                }
                contentRect.y += logoRect.y + logoRect.height;
                //Debug.Log($"ContentRect2:{contentRect}");
                DrawTabsView();
                DrawProject();
            }
            catch (Exception ex)
            {
                Debug.LogError(ex.Message);
            }
            finally
            {
                EditorGUILayout.EndVertical();
                EditorGUILayout.EndScrollView();
            }

            if (Event.current.type != EventType.Layout)
            {
                HandleEvents();
            }
        }

        private void OnEditorUpdate()
        {
            bool forceRepaint = false;
            if (musicTimeline != null && (musicTimeline.Playing || musicTimeline.StartTranslating))
            {
                forceRepaint = true;
                if (selectedAudioClip != null)
                {
                    if (musicTimeline.StartTranslating)
                    {
                        float musicTime = musicTimeline.CurTime;
                        audioSource.time = musicTime;
                    }
                    if (!audioSource.isPlaying)
                    {
                        float musicTime = musicTimeline.CurTime;
                        audioSource.time = musicTime;
                        audioSource.Play();
                    }
                }

                if (autoPlayAnimation && animationTimeline != null)
                {
                    float animationTime = 0;
                    if ((musicTimeline.CurTime - musicTimeline.StartTime) < musicStartTime)
                    {
                        animationTime = animationTimeline.StartTime;
                    }
                    else
                    {
                        animationTime = (musicTimeline.CurTime - musicTimeline.StartTime) - musicStartTime;
                        animationTime = animationTime % (animationTimeline.Duration - animationTimeline.StartTime) + animationTimeline.StartTime;
                    }
                    animationTimeline.CurTime = animationTime;
                }
            }
            else if (selectedAudioClip != null && audioSource != null)
            {
                audioSource.Pause();
            }

            bool controlByMusic = autoPlayAnimation && musicTimeline != null && (musicTimeline.Playing || musicTimeline.StartTranslating);
            if (animationTimeline != null && (animationTimeline.Playing || animationTimeline.StartTranslating || controlByMusic))
            {
                forceRepaint = true;
                //Unityʧȥ㣬¶
                bool isFocus = UnityEditorInternal.InternalEditorUtility.isApplicationActive;
                if (previewNpc != null && selectedClip != null && isFocus)
                {
                    // ȡǰʱ䲢¶
                    Animator animator = previewNpc.GetComponent<Animator>();
                    if (animator == null)
                    {
                        InitializeAnimator();
                        animator = previewNpc.GetComponent<Animator>();
                    }
                    if (animatorIsPlaying)
                    {
                        float deltaTime = Time.deltaTime;
                        if (controlByMusic && (musicTimeline.CurTime - musicTimeline.StartTime) < musicStartTime)
                        {
                            deltaTime = 0;
                        }
                        
                        if (deltaTime > 0)
                        {
                            animationPlayDelay += deltaTime * animationTimeline.Speed;
                            if (animationPlayDelay < animationTimeline.StartTime || animationPlayDelay > animationTimeline.Duration)
                            {
                                animator.Play(selectedClip.name, 0, animationTimeline.CurTime / selectedClip.length);
                                animationPlayDelay = animationTimeline.CurTime;
                            }
                            else
                            {
                                if (animationTimeline.StartTranslating)
                                {
                                    animator.Play(selectedClip.name, 0, animationTimeline.CurTime / selectedClip.length);
                                    deltaTime = 0;
                                }
                                animator.Update(deltaTime * animationTimeline.Speed);
                            }
                        }
                    }
                    if (!animatorIsPlaying)
                    {
                        animationPlayDelay = animationTimeline.CurTime;
                        animator.Play(selectedClip.name, 0, animationTimeline.CurTime / selectedClip.length);
                        animatorIsPlaying = true;
                    }
                    SceneView.RepaintAll();
                }
            }
            else
            {
                animatorIsPlaying = false;
            }
            //else
            //{
            //    if (previewNpc != null)
            //    {
            //        Animator animator = previewNpc.GetComponent<Animator>();
            //        if (animator != null)
            //        {
            //            animator.StopPlayback();
            //        }
            //    }
            //}
            if (forceRepaint)
            {
                Repaint();
            }
        }

        private void HandleEvents()
        {
            if (animationTimeline != null)
            {
                animationTimeline.HandleEvents();
            }
            if (musicTimeline != null)
            {
                musicTimeline.HandleEvents();
            }
        }

        private void CreateZipFromFolders(string projectName)
        {
            string builderFolderPath = string.Format(StringConst.builderFolderPath, projectName);
            string contextFolderPath = string.Format(StringConst.contextFolderPath, projectName);
            string zipFilePath = string.Format(StringConst.zipFilePath, projectName);

            // ʱļļ
            string tempFolderPath = Path.Combine(Path.GetTempPath(), projectName);
            if (Directory.Exists(tempFolderPath))
            {
                Directory.Delete(tempFolderPath, true);
            }
            Directory.CreateDirectory(tempFolderPath);

            // Builderļеļʱļ
            CopyFilesRecursively(new DirectoryInfo(builderFolderPath), new DirectoryInfo(tempFolderPath));

            // Contextļеļʱļ
            CopyFilesRecursively(new DirectoryInfo(contextFolderPath), new DirectoryInfo(tempFolderPath));

            if (selectedCoverTexture != null)
            {
                string coverPath = AssetDatabase.GetAssetPath(selectedCoverTexture);
                string coverFileName = Path.GetFileName(coverPath);
                coverFileName = "image" + Path.GetExtension(coverFileName);
                string coverDestinationPath = Path.Combine(tempFolderPath, coverFileName);
                File.Copy(coverPath, coverDestinationPath, true);
            }

            // ɾʱļе.metaļ
            string[] metaFiles = Directory.GetFiles(tempFolderPath, "*.meta", SearchOption.AllDirectories);
            foreach (string metaFile in metaFiles)
            {
                if (File.Exists(metaFile))
                { File.Delete(metaFile); }
            }

            // ɾʱļе.manifestļ
            string[] manifestFiles = Directory.GetFiles(tempFolderPath, "*.manifest", SearchOption.AllDirectories);
            foreach (string manifestFile in manifestFiles)
            {
                if (File.Exists(manifestFile))
                { File.Delete(manifestFile); }
            }

            // ɾļΪAndroid, StandaloneWindows, WebGLļ
            string[] platformFiles = Directory.GetFiles(tempFolderPath, "*", SearchOption.AllDirectories)
                .Where(f => Path.GetFileNameWithoutExtension(f).Equals("Android", StringComparison.OrdinalIgnoreCase) ||
                            Path.GetFileNameWithoutExtension(f).Equals("StandaloneWindows", StringComparison.OrdinalIgnoreCase) ||
                            Path.GetFileNameWithoutExtension(f).Equals("WebGL", StringComparison.OrdinalIgnoreCase))
                .ToArray();
            foreach (string platformFile in platformFiles)
            {
                if (File.Exists(platformFile))
                {
                    Debug.Log($"Deleting file: {platformFile}");
                    File.Delete(platformFile);
                }
            }

            //zipļ
            if (!Directory.Exists(Path.GetDirectoryName(zipFilePath)))
            {
                Directory.CreateDirectory(Path.GetDirectoryName(zipFilePath));
            }

            // ZIPļ
            if (File.Exists(zipFilePath))
            {
                File.Delete(zipFilePath);
            }
            ZipFile.CreateFromDirectory(tempFolderPath, zipFilePath, System.IO.Compression.CompressionLevel.Optimal, false);

            // ɾʱļ
            Directory.Delete(tempFolderPath, true);

            Debug.Log($"ZIP file created at: {zipFilePath}");
        }

        public bool CheckCanBuild(out string warningInfos)
        {
            warningInfos = "";
            StringBuilder stringBuilder = new StringBuilder();
            //if (selectedModel == null)
            //{
            //    stringBuilder.AppendLine("Please select a model");
            //}
            if (selectedAudioClip == null)
            {
                stringBuilder.AppendLine("Please select a audio clip");
            }
            else
            {
                //musicStar0
                if (musicStar <= 0)
                {
                    stringBuilder.AppendLine("Please set the music star level");
                }

                if (musicBpm <= 0)
                {
                    stringBuilder.AppendLine("Please set the music bpm");
                }

                //ƵСС10M
                string audioPath = AssetDatabase.GetAssetPath(selectedAudioClip);
                FileInfo audioFileInfo = new FileInfo(audioPath);
                if (audioFileInfo.Exists)
                {
                    if (audioFileInfo.Length > 10 * 1024 * 1024)
                    {
                        stringBuilder.AppendLine($"The audio clip file size is too large, please reduce the file size to less than 10M, current size is {audioFileInfo.Length / 1024 / 1024}M");
                    }
                }
            }

            if (selectedCoverTexture == null)
            {
                stringBuilder.AppendLine("Please select a cover image");
            }
            else
            {
                //ͼƬСС2M
                string coverPath = AssetDatabase.GetAssetPath(selectedCoverTexture);
                FileInfo coverFileInfo = new FileInfo(coverPath);
                if (coverFileInfo.Exists)
                {
                    if (coverFileInfo.Length > 1 * 1024 * 1024)
                    {
                        stringBuilder.AppendLine($"The cover image file size is too large, please reduce the file size to less than 1M, current size is {coverFileInfo.Length / 1024 / 1024}M");
                    }
                }
            }

            if (selectedClip == null)
            {
                stringBuilder.AppendLine("Please select a animation clip");
            }
            else
            {
                //AnimationClip humonoidͣʾ
                if (!selectedClip.isHumanMotion)
                    stringBuilder.AppendLine("Please set the model's animation type to Humanoid");
                //AnimationClipļС30Mʾ
                string clipPath = AssetDatabase.GetAssetPath(selectedClip);
                FileInfo fileInfo = new FileInfo(clipPath);
                if (fileInfo.Exists)
                {
                    bool isFbx = Path.GetExtension(clipPath).ToLower().Contains("fbx");
                    float maxSize = isFbx ? 30 * 1024 * 1024 : 300 * 1024 * 1024;
                    if (fileInfo.Length > maxSize)
                    {
                        stringBuilder.AppendLine($"The animation clip file size is too large, please reduce the file size to less than {maxSize}M, current size is {fileInfo.Length / 1024 / 1024}M");
                    }
                }
            }
            //AnimationClip humonoidͣʾ
            ModelImporter modelImporter = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(selectedModel)) as ModelImporter;
            if (modelImporter != null)
            {
                if (modelImporter.animationType != ModelImporterAnimationType.Human)
                {
                    stringBuilder.AppendLine("Please set the model's animation type to Humanoid");
                }
            }
            // animationTimelinemusicTimelineʱǷϷ
            if (animationTimeline == null)
            {
                stringBuilder.AppendLine("Please set the animation timeline");
            }
            else
            {
                if (animationTimeline.StartTime < 0 || animationTimeline.Duration <= 0 || animationTimeline.StartTime > animationTimeline.Duration)
                {
                    stringBuilder.AppendLine("Please set the animation timeline correctly");
                }
            }

            if (musicTimeline == null)
            {
                stringBuilder.AppendLine("Please set the music timeline");
            }
            else
            {
                if (musicTimeline.StartTime < 0 || musicTimeline.Duration <= 0 || musicTimeline.StartTime > musicTimeline.Duration)
                {
                    stringBuilder.AppendLine("Please set the music timeline correctly");
                }
            }
            warningInfos = stringBuilder.ToString();
            if (!string.IsNullOrEmpty(warningInfos))
            {
                return false;
            }
            return true;
        }

        private void CopyFilesRecursively(DirectoryInfo source, DirectoryInfo target)
        {
            foreach (DirectoryInfo dir in source.GetDirectories())
            {
                CopyFilesRecursively(dir, target.CreateSubdirectory(dir.Name));
            }
            foreach (FileInfo file in source.GetFiles())
            {
                file.CopyTo(Path.Combine(target.FullName, file.Name), true);
            }
        }
    }
}
