using System.Collections.Generic;
using UnityEngine;
using UniHumanoid;
using System.IO;
using System;
using RootMotion.FinalIK;

namespace KindRetargeting
{
    /// <summary>
    /// 이 스크립트는 원본 아바타(Source)의 포즈 손가락 움직임을 대상 아바타(Target)에 리타게팅(Retargeting)합니다.
    /// 또한 IK 타겟을 생성하여 대상 아바타의 관절 움직임을 자연스럽게 조정합니다.
    /// </summary>
    [RequireComponent(typeof(LimbWeightController))]
    [RequireComponent(typeof(ShoulderCorrectionFunction))]
    [RequireComponent(typeof(FullBodyInverseKinematics_RND))]
    [RequireComponent(typeof(PropLocationController))]
    [RequireComponent(typeof(FingerShapedController))]
    public class CustomRetargetingScript : MonoBehaviour
    {
        #region 필드

        [Header("원본 및 대상 아바타 Animator")]
        [SerializeField] public Animator sourceAnimator; // 원본 아바타의 Animator
        [HideInInspector] public Animator targetAnimator; // 대상 아바타의 Animator

        // IK 컴포넌트 참조 변경
        private FullBodyInverseKinematics_RND ikComponent;

        [Header("힙 위치 보정")]
        [SerializeField, Range(-1, 1)]
        private float hipsWeight = 0f; // 힙의 위치를 위아래로 보정하는 가중치

        [HideInInspector] public float HipsWeightOffset = 1f;

        // HumanPoseHandler를 이용하여 원본 및 대상 아바타의 포즈를 관리
        private HumanPoseHandler sourcePoseHandler;
        private HumanPoseHandler targetPoseHandler;
        private HumanPose sourcePose;
        private HumanPose targetPose;

        // 본별 회전 오프셋을 저장하는 딕셔너리
        private Dictionary<HumanBodyBones, Quaternion> rotationOffsets = new Dictionary<HumanBodyBones, Quaternion>();

        // HumanBodyBones.LastBone을 이용한 본 순회 범위
        private int lastBoneIndex = 23;

        [Header("손가락 복제 설정")]
        [SerializeField] private EnumsList.FingerCopyMode fingerCopyMode = EnumsList.FingerCopyMode.Rotation;

        [Header("모션 필터링 설정")]
        [SerializeField] private bool useMotionFilter = false;
        [SerializeField, Range(2, 10)] private int filterBufferSize = 5;

        [Header("러프 모션 설정")]
        [SerializeField] private bool useBodyRoughMotion = false;    // 몸 러프 모션 사용
        [SerializeField] private bool useFingerRoughMotion = false;  // 손가락 러프 모션 사용
        [SerializeField, Range(0f, 1f)] private float bodyRoughness = 0.1f;
        [SerializeField, Range(0f, 1f)] private float fingerRoughness = 0.1f;

        private Dictionary<HumanBodyBones, MotionFilter> boneFilters = new Dictionary<HumanBodyBones, MotionFilter>();
        private Dictionary<HumanBodyBones, RoughMotion> roughMotions = new Dictionary<HumanBodyBones, RoughMotion>();

        // IK 타겟용 러프 모션 필터
        private Dictionary<string, RoughMotion> ikTargetRoughMotions = new Dictionary<string, RoughMotion>();

        // IK 타겟용 모션 필터
        private Dictionary<string, MotionFilter> ikTargetFilters = new Dictionary<string, MotionFilter>();

        [Header("무릎 안/밖 조정")]
        [SerializeField, Range(-1f, 1f)]
        private float kneeInOutWeight = 0f; // 무릎 안/밖 위치 조정 가중치
        [Header("무릎 앞/뒤 조정")]
        [SerializeField, Range(-1f, 1f)]
        private float kneeFrontBackWeight = 0.4f; // 무릎 앞/뒤 위치 조정 가중치

        [Header("바닥 높이 조정")]
        [SerializeField, Range(-1f, 1f)] public float floorHeight = 0f; // 바닥 높이 조정값

        [Header("발목 높이 설정")]
        [SerializeField] public float minimumAnkleHeight = 0.2f;  // 수동 설정용 최소 발목 높이

        [Header("설정 저장/로드")]
        [SerializeField] private string settingsFolderName = "RetargetingSettings";

        private float initialHipsHeight;  // 초기 힙 높이

        [Header("아바타 크기 조정")]
        [SerializeField, Range(0.1f, 3f)] private float avatarScale = 1f;
        private float previousScale = 1f;
        private List<Transform> scalableObjects = new List<Transform>();

        // 필드 추가
        private Dictionary<Transform, Vector3> originalScales = new Dictionary<Transform, Vector3>();
        private Dictionary<Transform, Vector3> originalPositions = new Dictionary<Transform, Vector3>();

        [System.Serializable]
        private class RotationOffsetData
        {
            public int boneIndex;
            public float x, y, z, w;

            public RotationOffsetData(int bone, Quaternion rotation)
            {
                boneIndex = bone;
                x = rotation.x;
                y = rotation.y;
                z = rotation.z;
                w = rotation.w;
            }

            public Quaternion ToQuaternion()
            {
                return new Quaternion(x, y, z, w);
            }
        }

        [System.Serializable]
        private class RetargetingSettings
        {
            public float hipsWeight;
            public float kneeInOutWeight;
            public float kneeFrontBackWeight;
            public float floorHeight;
            public EnumsList.FingerCopyMode fingerCopyMode;
            public bool useMotionFilter;
            public int filterBufferSize;
            public bool useBodyRoughMotion;
            public bool useFingerRoughMotion;
            public float bodyRoughness;
            public float fingerRoughness;
            public List<RotationOffsetData> rotationOffsetCache;
            public float initialHipsHeight;
            public float avatarScale;
        }

        // 각 손가락 관절별로 필터 버퍼를 관리하는 Dictionary 추가
        private Dictionary<int, Queue<float>> fingerFilterBuffers = new Dictionary<int, Queue<float>>();

        // IK 조인트 싱을 위한 구조체
        private struct IKJoints
        {
            public Transform leftLowerLeg;
            public Transform rightLowerLeg;
            public Transform leftLowerArm;
            public Transform rightLowerArm;
        }
        private IKJoints sourceIKJoints;

        #endregion

        #region 초기화

        /// <summary>
        /// 초기화 메서드. T-포즈 설정, IK 타겟 생성, HumanPoseHandler 초기화 및 회전 오프셋 계산을 수행합니다.
        /// </summary>
        void Start()
        {
            targetAnimator = GetComponent<Animator>();
            // 설정 로드
            LoadSettings();

            // IK 컴포넌트 참조 가져오기 변경
            ikComponent = GetComponent<FullBodyInverseKinematics_RND>();

            // IK 타겟 생성 (무릎 시각화 오브젝트 포함)
            CreateIKTargets();

            // 원본 및 대상 아바타를 T-포즈로 복원
            SetTPose(sourceAnimator);
            SetTPose(targetAnimator);

            // HumanPoseHandler 초기화
            InitializeHumanPoseHandlers();

            // 회전 오프셋 초기화 (캐시 사용 또는 새로 계산)
            InitializeRotationOffsets();

            // 모션 필터 초기화
            if (useMotionFilter || useBodyRoughMotion || useFingerRoughMotion)
            {
                InitializeMotionFilters();
            }

            // 초기 힙 높이 저장
            if (targetAnimator != null)
            {
                Transform hips = targetAnimator.GetBoneTransform(HumanBodyBones.Hips);
                if (hips != null)
                {
                    initialHipsHeight = hips.position.y;
                }
            }

            InitializeIKJoints();

            // 크기 조정 대상 오브젝트 캐싱
            CacheScalableObjects();
            
            previousScale = avatarScale;

            ApplyScale();
        }

        /// <summary>
        /// HumanPoseHandler를 초기화합니다.
        /// </summary>
        private void InitializeHumanPoseHandlers()
        {
            if (sourceAnimator != null && sourceAnimator.avatar != null)
            {
                sourcePoseHandler = new HumanPoseHandler(sourceAnimator.avatar, sourceAnimator.transform);
            }

            if (targetAnimator != null && targetAnimator.avatar != null)
            {
                targetPoseHandler = new HumanPoseHandler(targetAnimator.avatar, targetAnimator.transform);
            }
        }

        /// <summary>
        /// 원본과 대상 아바타의 각 본 간 회전 오프셋을 계산하여 저장합니다.
        /// </summary>
        private void CalculateRotationOffsets(bool isIPose = false)
        {
            if (sourceAnimator == null || targetAnimator == null)
            {
                Debug.LogError("소스 또는 타겟 Animator가 설정되지 않았습니다.");
                return;
            }

            // Dictionary가 null이면 초기화
            if (rotationOffsets == null)
            {
                rotationOffsets = new Dictionary<HumanBodyBones, Quaternion>();
            }

            // 모든 본에 대해 오프셋 계산 (기본 몸체 본 + 손가락 본 + UpperChest)
            for (int i = 0; i <= (isIPose ? 23 : 54); i++)
            {
                HumanBodyBones bone = (HumanBodyBones)i;
                Transform sourceBone = sourceAnimator.GetBoneTransform(bone);
                Transform targetBone = targetAnimator.GetBoneTransform(bone);

                if (sourceBone != null && targetBone != null)
                {
                    if (rotationOffsets.ContainsKey(bone))
                    {
                        rotationOffsets[bone] = Quaternion.Inverse(sourceBone.rotation) * targetBone.rotation;
                    }
                    else
                    {
                        rotationOffsets.Add(bone, Quaternion.Inverse(sourceBone.rotation) * targetBone.rotation);
                    }
                }
            }
        }

        /// <summary>
        /// 모션 필터를 초기화합니다.
        /// </summary>
        private void InitializeMotionFilters()
        {
            // 기존 본들에 대한 모션 필터 초기화
            for (int i = 0; i < lastBoneIndex; i++)
            {
                HumanBodyBones bone = (HumanBodyBones)i;
                boneFilters[bone] = new MotionFilter(filterBufferSize);
            }

            // 손가락 본에 대한 모션 필터 초기화
            for (int i = 24; i <= 53; i++)
            {
                HumanBodyBones bone = (HumanBodyBones)i;
                boneFilters[bone] = new MotionFilter(filterBufferSize);
            }

            // IK 타겟에 대한 모션 필터 초기화
            string[] ikTargetNames = {
                "Left_Arm_Middle", "Right_Arm_Middle",
                "Left_Leg_Middle", "Right_Leg_Middle",
                "Left_Arm_End", "Right_Arm_End",
                "Left_Leg_End", "Right_Leg_End"
            };

            foreach (string targetName in ikTargetNames)
            {
                ikTargetFilters[targetName] = new MotionFilter(filterBufferSize);
            }

            // 러프 모션 초기화 - 몸과 손가락 분리
            // 몸 러프 모션 초기화
            if (useBodyRoughMotion)
            {
                for (int i = 0; i < lastBoneIndex; i++)
                {
                    HumanBodyBones bone = (HumanBodyBones)i;
                    var rough = new RoughMotion();
                    rough.SetSmoothSpeed(50f - (bodyRoughness * 49f));
                    roughMotions[bone] = rough;
                }
            }

            // 손가락 러프 모션 초기화
            if (useFingerRoughMotion)
            {
                for (int i = 24; i <= 53; i++)
                {
                    HumanBodyBones bone = (HumanBodyBones)i;
                    var rough = new RoughMotion();
                    rough.SetSmoothSpeed(50f - (fingerRoughness * 49f));
                    roughMotions[bone] = rough;
                }

                foreach (string targetName in ikTargetNames)
                {
                    var rough = new RoughMotion();
                    rough.SetSmoothSpeed(50f - (bodyRoughness * 49f));
                    ikTargetRoughMotions[targetName] = rough;
                }
            }
        }

        /// <summary>
        /// 현재 설정을 JSON 파일로 저장합니다.
        /// </summary>
        public void SaveSettings()
        {
            if (targetAnimator == null)
            {
                Debug.LogWarning("타겟 아바타가 설정되지 않았습니다.");
                return;
            }

            var offsetCache = new List<RotationOffsetData>();
            foreach (var kvp in rotationOffsets)
            {
                offsetCache.Add(new RotationOffsetData((int)kvp.Key, kvp.Value));
            }

            var settings = new RetargetingSettings
            {
                hipsWeight = hipsWeight,
                kneeInOutWeight = kneeInOutWeight,
                kneeFrontBackWeight = kneeFrontBackWeight,
                floorHeight = floorHeight,
                fingerCopyMode = fingerCopyMode,
                useMotionFilter = useMotionFilter,
                filterBufferSize = filterBufferSize,
                useBodyRoughMotion = useBodyRoughMotion,
                useFingerRoughMotion = useFingerRoughMotion,
                bodyRoughness = bodyRoughness,
                fingerRoughness = fingerRoughness,
                rotationOffsetCache = offsetCache,
                initialHipsHeight = initialHipsHeight,
                avatarScale = avatarScale,
            };

            string json = JsonUtility.ToJson(settings, true);
            string filePath = GetSettingsFilePath();

            try
            {
                File.WriteAllText(filePath, json);
                //너무 자주 출력되어서 주석처리
                //Debug.Log($"설정이 저장되었습니다: {filePath}");
            }
            catch (Exception e)
            {
                Debug.LogError($"설정 저장 중 오류 발생: {e.Message}");
            }
        }

        /// <summary>
        /// JSON 파일에서 설정을 로드합니다.
        /// </summary>
        public void LoadSettings()
        {
            if (targetAnimator == null)
            {
                Debug.LogWarning("타겟 아바타가 설정되지 않았습니다.");
                return;
            }

            string filePath = GetSettingsFilePath();

            if (!File.Exists(filePath))
            {
                Debug.LogWarning($"저장된 설정 파일이 없습니다: {filePath}");
                return;
            }

            try
            {
                string json = File.ReadAllText(filePath);
                var settings = JsonUtility.FromJson<RetargetingSettings>(json);

                // 설정 적용
                hipsWeight = settings.hipsWeight;
                kneeInOutWeight = settings.kneeInOutWeight;
                kneeFrontBackWeight = settings.kneeFrontBackWeight;
                floorHeight = settings.floorHeight;
                fingerCopyMode = settings.fingerCopyMode;
                useMotionFilter = settings.useMotionFilter;
                filterBufferSize = settings.filterBufferSize;
                useBodyRoughMotion = settings.useBodyRoughMotion;
                useFingerRoughMotion = settings.useFingerRoughMotion;
                bodyRoughness = settings.bodyRoughness;
                fingerRoughness = settings.fingerRoughness;
                initialHipsHeight = settings.initialHipsHeight;
                avatarScale = settings.avatarScale;
                previousScale = avatarScale;

                

                //너무 자주 출력되어서 주석처리
                //Debug.Log($"설정을 로드했습니다: {filePath}");
            }
            catch (Exception e)
            {
                Debug.LogError($"설정 로드 중 오류 발생: {e.Message}");
            }
        }

        private string GetSettingsFilePath()
        {
            // Unity AppData 경로 가져오기
            string appDataPath = Path.Combine(
                Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
                "Unity",
                Application.companyName,
                Application.productName,
                settingsFolderName
            );

            // 타겟 아바타 이름으로 파일명 생성
            string fileName = $"{targetAnimator?.gameObject.name}_settings.json";

            // 설정 폴더가 없으면 생성
            if (!Directory.Exists(appDataPath))
            {
                Directory.CreateDirectory(appDataPath);
            }

            return Path.Combine(appDataPath, fileName);
        }

        /// <summary>
        /// 캐시된 회전 오프셋을 로드하거나 새로 계산합니다.
        /// </summary>
        private void InitializeRotationOffsets()
        {
            string filePath = GetSettingsFilePath();
            bool useCache = false;

            // 캐시된 데이터가 있는지 확인
            if (File.Exists(filePath))
            {
                try
                {
                    string json = File.ReadAllText(filePath);
                    var settings = JsonUtility.FromJson<RetargetingSettings>(json);

                    if (settings.rotationOffsetCache != null && settings.rotationOffsetCache.Count > 0)
                    {
                        rotationOffsets.Clear();
                        foreach (var offsetData in settings.rotationOffsetCache)
                        {
                            rotationOffsets.Add((HumanBodyBones)offsetData.boneIndex, offsetData.ToQuaternion());
                        }
                        useCache = true;
                        Debug.Log("캐시된 회전 오프셋을 로드했습니다.");
                    }
                }
                catch (Exception e)
                {
                    Debug.LogWarning($"회전 오프셋 캐시 로드 실패: {e.Message}");
                }
            }

            // 캐시된된 데이터가 없거나 로드 실패 시 새로 계산
            if (!useCache)
            {
                CalculateRotationOffsets();

                //너무 자주 출력되어서 주석처리
                //Debug.Log("새로운 회전 오프셋을 계산했습니다.");
            }
        }

        public void I_PoseCalibration()
        {
            if (targetAnimator == null)
            {
                Debug.LogError("타겟 Animator가 설정되지 않았습니다.");
                return;
            }

            try
            {
                SetIPose(targetAnimator);
                CalculateRotationOffsets(true);
                SaveSettings(); // 캘리브레이션 후 설정 저장
                Debug.Log("I-포즈 캘리브레이션이 완료되었습니다.");
            }
            catch (System.Exception e)
            {
                Debug.LogError($"I-포즈 캘리브레이션 중 오류가 발생했습니다: {e.Message}");
            }
        }

        private void InitializeIKJoints()
        {
            // IK 루트 찾기
            Transform sourceIKRoot = sourceAnimator.transform.Find("IK");
            if (sourceIKRoot == null)
            {
                Debug.LogError("소스 아바타에서 IK 루트를 찾을 수 없습니다.");
                return;
            }

            // IK 조인트들 캐싱
            sourceIKJoints = new IKJoints
            {
                leftLowerLeg = sourceIKRoot.Find("LeftLowerLeg"),
                rightLowerLeg = sourceIKRoot.Find("RightLowerLeg"),
                leftLowerArm = sourceIKRoot.Find("LeftLowerArm"),
                rightLowerArm = sourceIKRoot.Find("RightLowerArm")
            };

        }

        #endregion

        #region 업데이트

        /// <summary>
        /// 매 프레임마다 원본 아바타의 포즈와 손가락 움직임을 대상 아바타에 리타게팅하고, IK 타타겟을 업데이트합니다.
        /// </summary>
        void Update()
        {
            // 포즈 복사 및 동기화
            CopyPoseToTarget();

            UpdateIKTargets();
            // IK 중간 타겟 업데이트

            // 손가락 포즈 동기화
            switch (fingerCopyMode)
            {
                case EnumsList.FingerCopyMode.MuscleData:
                    CopyFingerPoseByMuscle();
                    break;
                case EnumsList.FingerCopyMode.Rotation:
                    CopyFingerPoseByRotation();
                    break;
            }

            // 스케일 변경 확인 및 적용
            if (!Mathf.Approximately(previousScale, avatarScale))
            {
                ApplyScale();
                previousScale = avatarScale;
            }
        }
        /// <summary>
        /// 머슬 데이터를 사용하여 손가락 포즈를 복제합니다.
        /// </summary>
        private void CopyFingerPoseByMuscle()
        {
            Vector3 originalPosition = targetAnimator.GetBoneTransform(HumanBodyBones.Hips).position;
            Quaternion originalRotation = targetAnimator.GetBoneTransform(HumanBodyBones.Hips).rotation;

            if (sourcePoseHandler == null || targetPoseHandler == null)
                return;

            sourcePoseHandler.GetHumanPose(ref sourcePose);
            targetPoseHandler.GetHumanPose(ref targetPose);

            for (int i = 0; i < 40; i++)
            {
                int muscleIndex = 55 + i;
                string muscleName = HumanTrait.MuscleName[muscleIndex];

                // "Spread"가 포함된 머슬만 스킵 (손가락 벌리기 동작)
                if (muscleName.Contains("Spread"))
                    continue;

                float targetValue = sourcePose.muscles[muscleIndex];
                float currentValue = targetPose.muscles[muscleIndex];

                if (useMotionFilter)
                {
                    targetValue = ApplyFilter(targetValue, i);
                }

                if (useFingerRoughMotion && roughMotions.TryGetValue(HumanBodyBones.LeftHand, out RoughMotion rough))
                {
                    float smoothSpeed = 50f - (fingerRoughness * 49f);
                    targetValue = Mathf.Lerp(currentValue, targetValue, smoothSpeed * Time.deltaTime);
                }

                targetPose.muscles[muscleIndex] = targetValue;
            }

            targetPoseHandler.SetHumanPose(ref targetPose);

            targetAnimator.GetBoneTransform(HumanBodyBones.Hips).position = originalPosition;
            targetAnimator.GetBoneTransform(HumanBodyBones.Hips).rotation = originalRotation;
        }

        private float ApplyFilter(float value, int fingerIndex)
        {
            // 해당 손가락 관절의 필터 버퍼가 없없으면 생성
            if (!fingerFilterBuffers.ContainsKey(fingerIndex))
            {
                fingerFilterBuffers[fingerIndex] = new Queue<float>();
                // 초기값으로 버퍼를 채움
                for (int i = 0; i < filterBufferSize; i++)
                {
                    fingerFilterBuffers[fingerIndex].Enqueue(value);
                }
            }

            var buffer = fingerFilterBuffers[fingerIndex];

            // 가장 오래된 값 제거
            if (buffer.Count >= filterBufferSize)
            {
                buffer.Dequeue();
            }

            // 새 값 추가
            buffer.Enqueue(value);

            // 가중 평균 계산
            float sum = 0f;
            float weight = 1f;
            float totalWeight = 0f;
            float[] values = buffer.ToArray();

            for (int i = 0; i < values.Length; i++)
            {
                sum += values[i] * weight;
                totalWeight += weight;
                weight *= 1.5f; // 최근 값에 더 높은 가중치 부여
            }

            return sum / totalWeight;
        }

        /// <summary>
        /// 힙전값을 사용하여 손가락 포즈를 복제합니다.
        /// </summary>
        private void CopyFingerPoseByRotation()
        {
            for (int i = 24; i <= 53; i++)
            {
                HumanBodyBones bone = (HumanBodyBones)i;
                Transform sourceBone = sourceAnimator.GetBoneTransform(bone);
                Transform targetBone = targetAnimator.GetBoneTransform(bone);

                if (sourceBone != null && targetBone != null)
                {
                    Quaternion targetRotation;
                    if (rotationOffsets.TryGetValue(bone, out Quaternion offset))
                    {
                        targetRotation = sourceBone.rotation * offset;
                    }
                    else
                    {
                        targetRotation = sourceBone.rotation;
                    }

                    // 모션 필터 적용
                    if (useMotionFilter && boneFilters.TryGetValue(bone, out MotionFilter filter))
                    {
                        targetRotation = filter.FilterRotation(targetRotation);
                    }

                    // 러프 모션을 Lerp로 적용
                    if (useFingerRoughMotion && roughMotions.TryGetValue(bone, out RoughMotion rough))
                    {
                        float smoothSpeed = 50f - (fingerRoughness * 49f); // 1~10 범위의 속도
                        targetRotation = Quaternion.Lerp(targetBone.rotation, targetRotation, smoothSpeed * Time.deltaTime);
                    }

                    targetBone.rotation = targetRotation;
                }
            }
        }

        #endregion

        #region 포즈 동기화

        /// <summary>
        /// 원본 아바타의 포즈를 대상 아바타에 오프셋을 적용하여 복사합니다.
        /// 힙 위치와 회전을 동기화하고, 나머지 본의 회전을 오프셋을 적용하여 동기화합니다.
        /// </summary>
        private void CopyPoseToTarget()
        {
            // 힙(루트 본) 동기화
            Transform sourceHips = sourceAnimator.GetBoneTransform(HumanBodyBones.Hips);
            Transform targetHips = targetAnimator.GetBoneTransform(HumanBodyBones.Hips);

            if (sourceHips != null && targetHips != null)
            {
                // 힙 위치 동기화 + 힙 위치 보정 적용 + 바닥 높이 적용용
                Vector3 adjustedPosition = sourceHips.position;
                adjustedPosition.y += hipsWeight * HipsWeightOffset; // 기존 힙 높이 조정
                adjustedPosition.y += floorHeight; // 바닥 높이 조정 추가
                targetHips.position = adjustedPosition;

                // 힙 회전 동기화 (회전 오프셋 적용)
                if (rotationOffsets.TryGetValue(HumanBodyBones.Hips, out Quaternion hipsOffset))
                {
                    targetHips.rotation = sourceHips.rotation * hipsOffset;

                    ikComponent.solver.spine.pelvisTarget.position = targetHips.position;
                    ikComponent.solver.spine.pelvisTarget.rotation = targetHips.rotation;
                }
            }

            // 힙을 제외한 본들의 회전 동기화
            SyncBoneRotations(skipBone: HumanBodyBones.Hips);
        }

        /// <summary>
        /// 힙을 제외한 모든 본의 회전을 오프셋을 적용하여 동기화합니다.
        /// </summary>
        /// <param name="skipBone">동기화에서 제외할 본</param>
        private void SyncBoneRotations(HumanBodyBones skipBone)
        {
            // 기본 몸체 본만 동기화 (손가락 제외)
            for (int i = 0; i < lastBoneIndex; i++)
            {
                HumanBodyBones bone = (HumanBodyBones)i;

                if (bone == skipBone)
                    continue;

                Transform sourceBone = sourceAnimator.GetBoneTransform(bone);
                Transform targetBone = targetAnimator.GetBoneTransform(bone);

                if (sourceBone != null && targetBone != null)
                {
                    Quaternion targetRotation;
                    if (rotationOffsets.TryGetValue(bone, out Quaternion offset))
                    {
                        targetRotation = sourceBone.rotation * offset;
                    }
                    else
                    {
                        targetRotation = sourceBone.rotation;
                    }

                    // 모션 필터 적용
                    if (useMotionFilter && boneFilters.TryGetValue(bone, out MotionFilter filter))
                    {
                        targetRotation = filter.FilterRotation(targetRotation);
                    }

                    // 러 러프 모션 적용
                    if (useBodyRoughMotion)
                    {
                        float smoothSpeed = 50f - (bodyRoughness * 49f);
                        targetRotation = Quaternion.Lerp(targetBone.rotation, targetRotation, smoothSpeed * Time.deltaTime);
                    }

                    targetBone.rotation = targetRotation;
                }
            }
        }

        #endregion

        #region IK 타겟 생성 및 관관리

        /// <summary>
        /// IK 타겟(끝과 중간)을 생성하고 FullBodyInverseKinematics 컴포넌트를 설정합니다.
        /// </summary>
        private void CreateIKTargets()
        {
            // IK 컴포넌트 가져오기 또는 새로 추가
            ikComponent = GetComponent<FullBodyInverseKinematics_RND>();
            if (ikComponent == null)
                ikComponent = gameObject.AddComponent<FullBodyInverseKinematics_RND>();


            // IK 타겟들을 담을 부모 오브젝트 생성
            GameObject ikTargetsParent = new GameObject("IK Targets");
            ikTargetsParent.transform.parent = targetAnimator.transform;
            ikTargetsParent.transform.localPosition = Vector3.zero;
            ikTargetsParent.transform.localRotation = Quaternion.identity;

            GameObject hips = CreateIKTargetObject("Hips", targetAnimator.GetBoneTransform(HumanBodyBones.Hips), ikTargetsParent);
            ikComponent.solver.spine.pelvisTarget = hips.transform;

            GameObject leftHand = CreateIKTargetObject("LeftHand", targetAnimator.GetBoneTransform(HumanBodyBones.LeftHand), ikTargetsParent);
            ikComponent.solver.leftArm.target = leftHand.transform;
            GameObject rightHand = CreateIKTargetObject("RightHand", targetAnimator.GetBoneTransform(HumanBodyBones.RightHand), ikTargetsParent);
            ikComponent.solver.rightArm.target = rightHand.transform;
            GameObject leftToes = CreateIKTargetObject("LeftToes", targetAnimator.GetBoneTransform(HumanBodyBones.LeftToes), ikTargetsParent);
            ikComponent.solver.leftLeg.target = leftToes.transform;
            GameObject rightToes = CreateIKTargetObject("RightToes", targetAnimator.GetBoneTransform(HumanBodyBones.RightToes), ikTargetsParent);
            ikComponent.solver.rightLeg.target = rightToes.transform;

            GameObject leftArmGoal = CreateIKTargetObject("LeftArmGoal", targetAnimator.GetBoneTransform(HumanBodyBones.LeftLowerArm), ikTargetsParent);
            ikComponent.solver.leftArm.bendGoal = leftArmGoal.transform;
            GameObject rightArmGoal = CreateIKTargetObject("RightArmGoal", targetAnimator.GetBoneTransform(HumanBodyBones.RightLowerArm), ikTargetsParent);
            ikComponent.solver.rightArm.bendGoal = rightArmGoal.transform;
            GameObject leftLegGoal = CreateIKTargetObject("LeftLegGoal", targetAnimator.GetBoneTransform(HumanBodyBones.LeftLowerLeg), ikTargetsParent);
            ikComponent.solver.leftLeg.bendGoal = leftLegGoal.transform;
            GameObject rightLegGoal = CreateIKTargetObject("RightLegGoal", targetAnimator.GetBoneTransform(HumanBodyBones.RightLowerLeg), ikTargetsParent);
            ikComponent.solver.rightLeg.bendGoal = rightLegGoal.transform;
        }

        /// <summary>
        /// IK 타겟 오브젝트를 생성하고 기본 Gizmo 설정을 적용합니다.
        /// </summary>
        /// <param name="name">타겟 오브젝트의 이름</param>
        /// <param name="bone">타겟 오브젝트를 부모로 설정할 본</param>
        /// <returns>생성된 타겟 게임 오브젝트</returns>
        private GameObject CreateIKTargetObject(string name, Transform bone, GameObject parent)
        {
            GameObject target = new GameObject(name);
            target.transform.position = bone.position;
            target.transform.rotation = bone.rotation;

            // Gizmo 시각화를 위한 컴포넌트 추가
            IKTargetGizmo gizmo = target.AddComponent<IKTargetGizmo>();
            gizmo.gizmoSize = 0.05f;
            gizmo.gizmoColor = UnityEngine.Color.yellow;

            target.transform.parent = parent.transform;

            return target;
        }

        #endregion

        #region IK 중간 타겟 업데이트

        /// <summary>
        /// 매 프레임마다 IK 중간 타겟의 위치를 대상 아바타의 본 위치로 업데이트합니다.
        /// </summary>
        private void UpdateIKTargets()
        {
            if (targetAnimator == null) return;

            // 손과 발끝 타겟 업데이트
            UpdateEndTarget(ikComponent.solver.leftArm.target, HumanBodyBones.LeftHand);
            UpdateEndTarget(ikComponent.solver.rightArm.target, HumanBodyBones.RightHand);
            UpdateEndTarget(ikComponent.solver.leftLeg.target, HumanBodyBones.LeftToes);
            UpdateEndTarget(ikComponent.solver.rightLeg.target, HumanBodyBones.RightToes);

            updatejointTarget(ikComponent.solver.leftArm.bendGoal, HumanBodyBones.LeftLowerArm);
            updatejointTarget(ikComponent.solver.rightArm.bendGoal, HumanBodyBones.RightLowerArm);
            updatejointTarget(ikComponent.solver.leftLeg.bendGoal, HumanBodyBones.LeftLowerLeg);
            updatejointTarget(ikComponent.solver.rightLeg.bendGoal, HumanBodyBones.RightLowerLeg);
        }

        /// <summary>
        /// 손과 발끝 타겟의 위치와 회전을 업업데이트합니다.
        /// </summary>
        /// <param name="endTarget">업데이트할 끝 타겟의 Transform</param>
        /// <param name="endBone">대상 본</param>
        private void UpdateEndTarget(Transform endTarget, HumanBodyBones endBone)
        {
            if (endTarget == null) return;

            Transform sourceBone = sourceAnimator.GetBoneTransform(endBone);
            Transform targetBone = targetAnimator.GetBoneTransform(endBone);

            if (sourceBone != null && targetBone != null)
            {
                // 기본 위치와 회전 계산
                Vector3 targetPosition = sourceBone.position;
                Quaternion targetRotation = targetBone.rotation;

                // 발/발가락 본인 경우에만 바닥 높이 적용
                if (endBone == HumanBodyBones.LeftToes || endBone == HumanBodyBones.RightToes)
                {
                    targetPosition.y += floorHeight;
                }

                // 최종 위치와 회전 적용
                endTarget.position = targetPosition;
                endTarget.rotation = targetRotation;
            }
        }

        private void updatejointTarget(Transform target, HumanBodyBones bone)
        {
            Vector3 targetBone = targetAnimator.GetBoneTransform(bone).position;
            Vector3 sourceIKpoint;
            float zOffset = 0f;
            float xOffset = 0f;
            float yOffset = 0f;

            // bone의 이름에 따라 적절한 IK 조인트 오프셋 선택
            switch (bone)
            {
                case HumanBodyBones.LeftLowerLeg:
                case HumanBodyBones.RightLowerLeg:
                    sourceIKpoint = bone == HumanBodyBones.LeftLowerLeg ?
                        sourceIKJoints.leftLowerLeg.position :
                        sourceIKJoints.rightLowerLeg.position;
                    zOffset = kneeFrontBackWeight;  // 무릎 앞/뒤 조정
                    yOffset = floorHeight;
                    xOffset = kneeInOutWeight * (bone == HumanBodyBones.LeftLowerLeg ? -1f : 1f);  // 무릎 안/밖 조정
                    break;
                case HumanBodyBones.LeftLowerArm:
                case HumanBodyBones.RightLowerArm:
                    sourceIKpoint = bone == HumanBodyBones.LeftLowerArm ?
                        sourceIKJoints.leftLowerArm.position :
                        sourceIKJoints.rightLowerArm.position;
                    zOffset = 0.1f;  // 팔꿈치는 뒤로 고정
                    break;
                default:
                    Debug.LogError($"Unsupported bone type: {bone}");
                    return;
            }

            Quaternion LookatIK = Quaternion.LookRotation(sourceIKpoint - targetBone, Vector3.up);

            target.position = targetBone;
            target.rotation = LookatIK;

            // LookatIK 기준으로 프셋 적용
            Vector3 offset = LookatIK * new Vector3(xOffset, 0, zOffset);
            Vector3 offset2 = new Vector3(0, yOffset, 0);
            target.position += offset;
            target.position += offset2;
        }

        #endregion

        #region T-포즈 설정

        /// <summary>
        /// 지정된 Animator의 포즈를 T-포즈로 설정합니다.
        /// </summary>
        /// <param name="animator">T-포즈를 설정할 Animator</param>
        public static void SetTPose(Animator animator)
        {
            if (animator == null || animator.avatar == null)
                return;

            Avatar avatar = animator.avatar;
            Transform transform = animator.transform;

            // HumanPoseClip에 저장된 T-포즈 데이터를 로드하여 적용
            var humanPoseClip = Resources.Load<HumanPoseClip>(HumanPoseClip.TPoseResourcePath);
            if (humanPoseClip != null)
            {
                var pose = humanPoseClip.GetPose();
                HumanPoseTransfer.SetPose(avatar, transform, pose);
            }
            else
            {
                Debug.LogWarning("T-Pose 데이터가 존재하지 않습니다.");
            }
        }

        public static void SetIPose(Animator animator)
        {
            if (animator == null || animator.avatar == null)
                return;

            Avatar avatar = animator.avatar;
            Transform transform = animator.transform;

            // HumanPoseClip에 저장된 T-포즈 데이터를 로드하여 적용
            var humanPoseClip = Resources.Load<HumanPoseClip>(HumanPoseClip.IPoseResourcePath);
            if (humanPoseClip != null)
            {
                var pose = humanPoseClip.GetPose();
                HumanPoseTransfer.SetPose(avatar, transform, pose);
            }
            else
            {
                Debug.LogWarning("I-Pose 데이터가 존재하지 않습니다.");
            }
        }

        #endregion

        #region 파괴 시 처리

        /// <summary>
        /// 오브브젝트가 파괴될 때 HumanPoseHandler를 정리합니다.
        /// </summary>
        void OnDestroy()
        {
            sourcePoseHandler?.Dispose();
            targetPoseHandler?.Dispose();
        }

        /// <summary>
        /// Unity가 종료될 때 호출되되는 메서드
        /// </summary>
        private void OnApplicationQuit()
        {
            SaveSettings();
        }

        #endregion

        /// <summary>
        /// 캐시된 설정 파일이 존재하는지 확인합니다.
        /// </summary>
        public bool HasCachedSettings()
        {
            if (targetAnimator == null) return false;
            string filePath = GetSettingsFilePath();
            return File.Exists(filePath);
        }

        // 무릎 앞/뒤뒤 위치 조정을 위한 public 메서드 추가
        public void SetKneeFrontBackOffset(float offset)
        {
            kneeFrontBackWeight = offset;
        }

        // 무릎 조정을 위한 public 메서드들
        public void SetKneeInOutOffset(float offset)
        {
            kneeInOutWeight = offset;
        }

        public void ResetPoseAndCache()
        {
            // 캐시 파일 삭제
            string filePath = GetSettingsFilePath();
            if (File.Exists(filePath))
            {
                try
                {
                    File.Delete(filePath);
                    Debug.Log("캐시 파일이 삭제되었습니다.");
                }
                catch (Exception e)
                {
                    Debug.LogError($"캐시 파일 삭제 중 오류 발생: {e.Message}");
                    return;
                }
            }

            if (!Application.isPlaying)
            {
                Debug.LogWarning("이 기능은 실행 중에만 사용할 수 있습니다.");
                return;
            }

            sourceAnimator.transform.localRotation = new Quaternion(0, 0, 0, 0);

            // T-포즈로 복
            SetTPose(sourceAnimator);
            SetTPose(targetAnimator);

            // HumanPoseHandler 초기화
            InitializeHumanPoseHandlers();

            // 회전 오프셋 로 계산
            CalculateRotationOffsets();

            Debug.Log("포즈와 회전 오프셋이 재설정되었습니다.");
        }

        // 크기 조정 대상 오브젝트 캐싱 메서드
        private void CacheScalableObjects()
        {
            scalableObjects.Clear();
            originalScales.Clear();
            originalPositions.Clear();

            Transform parentTransform = sourceAnimator.transform.parent;

            if (parentTransform != null)
            {
                for (int i = 0; i < parentTransform.childCount; i++)
                {
                    Transform child = parentTransform.GetChild(i);
                    // sourceAnimator를 제외한 모든 자식 오브젝트 추가
                    if (child != sourceAnimator.transform)
                    {
                        scalableObjects.Add(child);
                        // 초기 스케일과 위치 저장
                        originalScales[child] = child.localScale;
                        originalPositions[child] = child.position - parentTransform.position;
                    }
                }
            }
        }

        // 스케일 적용 메서드
        private void ApplyScale()
        {
            foreach (Transform obj in scalableObjects)
            {
                if (obj != null && obj.parent != null)
                {
                    // 원본 스케일을 기준으로 새로운 스케일 계산
                    if (originalScales.TryGetValue(obj, out Vector3 originalScale))
                    {
                        obj.localScale = originalScale * avatarScale;
                    }

                    // 원본 위치를 기준으로 새로운 위치 계산
                    if (originalPositions.TryGetValue(obj, out Vector3 originalOffset))
                    {
                        obj.position = obj.parent.position + (originalOffset * avatarScale);
                    }
                }
            }
        }

        // 리셋 기능 추가
        public void ResetScale()
        {
            avatarScale = 1f;
            previousScale = 1f;

            foreach (Transform obj in scalableObjects)
            {
                if (obj != null && originalScales.TryGetValue(obj, out Vector3 originalScale))
                {
                    obj.localScale = originalScale;

                    if (obj.parent != null && originalPositions.TryGetValue(obj, out Vector3 originalOffset))
                    {
                        obj.position = obj.parent.position + originalOffset;
                    }
                }
            }
        }

        // 외부에서 스케일을 설정할 수 있는 public 메서드
        public void SetAvatarScale(float scale)
        {
            avatarScale = Mathf.Clamp(scale, 0.1f, 3f);
        }

        // 현재 스케일을 가져오는 메서드
        public float GetAvatarScale()
        {
            return avatarScale;
        }
    }

}
