// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts,
// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies.
// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
// Eden.AutoMorpher.EdenAutoMorpherManager
using Eden.AutoMorpher;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;

public class EdenAutoMorpherManager : IDisposable
{
    private string displayTitle;

    private string displayText;

    private float displayProgress;

    private List<ClothInstance> clothInstances;

    public (string, string, float) GetProgressInfo()
    {
        return (this.displayTitle, this.displayText, this.displayProgress);
    }

    private void SetupProgress(string title, string text, float progress)
    {
        this.displayTitle = title;
        this.displayText = text;
        this.displayProgress = progress;
    }

    private IEnumerator SetupClothInstances(GameObject targetClothesObject, Dictionary<HumanBodyBones, HashSet<Transform>> clothesHumanoidMatchedBones, bool duplicateMesh = true)
    {
        this.clothInstances = new List<ClothInstance>();
        SkinnedMeshRenderer[] targetClothesSMRs = targetClothesObject.GetComponentsInChildren<SkinnedMeshRenderer>(includeInactive: false);
        if (targetClothesSMRs.Length == 0)
        {
            throw new AutoMorpherException("There is NO Skinned Mesh Renderer in Clothes", "[EdenAutoMorpherManager] SetupClothInstance\n - There is NO Skinned Mesh Renderer in Clothes");
        }
        EdenAutoMorpher_SetUpUtil setupUtil = new EdenAutoMorpher_SetUpUtil();
        for (int rendereri = 0; rendereri < targetClothesSMRs.Length; rendereri++)
        {
            this.SetupProgress("Set Up", $"Setup Clothes Info [{rendereri}/{targetClothesSMRs.Length}] - ({targetClothesSMRs[rendereri].name})", Mathf.Lerp(0.05f, 0.1f, rendereri / targetClothesSMRs.Length));
            yield return null;
            ClothInstance clothInstance = setupUtil.SetupClothInstance(targetClothesSMRs[rendereri], clothesHumanoidMatchedBones, duplicateMesh);
            if (clothInstance != null)
            {
                this.clothInstances.Add(clothInstance);
            }
        }
        if (this.clothInstances.Count == 0)
        {
            throw new AutoMorpherException(LanguageManager.Get("UI.Exception.title.ClothInstanceAllocateFail"), LanguageManager.Get("UI.Exception.message.ClothInstanceAllocateFail"));
        }
    }

    private IEnumerator SetupClothInstances(GameObject targetClothesObject, Dictionary<HumanBodyBones, HashSet<Transform>> clothesHumanoidMatchedBones, GameObject sourceClothesObject, MeshMatcher meshMatcher)
    {
        this.clothInstances = new List<ClothInstance>();
        SkinnedMeshRenderer[] targetClothesSMRs = targetClothesObject.GetComponentsInChildren<SkinnedMeshRenderer>(includeInactive: false);
        if (targetClothesSMRs.Length == 0)
        {
            throw new AutoMorpherException("There is NO Skinned Mesh Renderer in Clothes", "[EdenAutoMorpherManager] SetupClothInstance\n - There is NO Skinned Mesh Renderer in Clothes");
        }
        SkinnedMeshRenderer[] sourceClothesSMRs = sourceClothesObject.GetComponentsInChildren<SkinnedMeshRenderer>(includeInactive: false);
        if (sourceClothesSMRs.Length == 0 || targetClothesSMRs.Length != sourceClothesSMRs.Length)
        {
            throw new AutoMorpherException("Clothes Skinned Mesh Renderer is Not Matched", "[EdenAutoMorpherManager] SetupClothInstance\n - Skinned Mesh Renderer of Source Clothes and Target Clothes is Not Matched");
        }
        EdenAutoMorpher_SetUpUtil setupUtil = new EdenAutoMorpher_SetUpUtil();
        for (int rendereri = 0; rendereri < targetClothesSMRs.Length; rendereri++)
        {
            this.SetupProgress("Set Up", $"Setup Clothes Info [{rendereri}/{targetClothesSMRs.Length}] - ({targetClothesSMRs[rendereri].name})", Mathf.Lerp(0.07f, 0.1f, rendereri / targetClothesSMRs.Length));
            yield return null;
            ClothInstance clothInstance = setupUtil.SetupClothInstance(targetClothesSMRs[rendereri], sourceClothesSMRs[rendereri], meshMatcher, clothesHumanoidMatchedBones, duplicateMesh: true);
            if (clothInstance != null)
            {
                this.clothInstances.Add(clothInstance);
            }
        }
        if (this.clothInstances.Count == 0)
        {
            throw new AutoMorpherException(LanguageManager.Get("UI.Exception.title.ClothInstanceAllocateFail"), LanguageManager.Get("UI.Exception.message.ClothInstanceAllocateFail"));
        }
    }

    public IEnumerator FittingIteration(EdenAutoMorpherConfig config, MorpherMode morpherMode)
    {
        this.SetupProgress("Set Up", "Setup Target Avatar Info", 0.03f);
        yield return null;
        MeshMatcher meshMatcher = new MeshMatcher();
        BvhTriangleMesh targetBodyBVH = meshMatcher.BuildBvhMulti(config.targetBodyMeshes, config.targetAvatarObject.GetComponent<Animator>());
        if (targetBodyBVH == null)
        {
            throw new AutoMorpherException("targetBodyBVH is Null", "[EdenAutoMorpherManater] FittingIteration\n - targetBodyBVH is null");
        }
        yield return null;
        this.SetupProgress("Set Up", "Setup Source Avatar Info", 0.05f);
        yield return null;
        BvhTriangleMesh sourceBodyBVH = null;
        switch (morpherMode)
        {
            case MorpherMode.AutoMorpher:
            case MorpherMode.ManualMorpher:
                sourceBodyBVH = meshMatcher.BuildBvhMulti(config.sourceBodyMeshes, config.sourceAvatarObject.GetComponent<Animator>());
                break;
            case MorpherMode.ProfileMorpher:
                {
                    ProfileLoader profileLoader = new ProfileLoader();
                    sourceBodyBVH = profileLoader.LoadBvhWithRootTransform(config.sourceClothesObject.transform, config.profileName);
                    break;
                }
            default:
                Debug.LogError("Unknown MorpherMode");
                break;
        }
        yield return null;
        if (sourceBodyBVH == null)
        {
            throw new AutoMorpherException("sourceBodyBVH is Null", "[EdenAutoMorpherManater] FittingIteration\n - sourceBodyBVH is null");
        }
        meshMatcher.bodyBVH = sourceBodyBVH;
        this.SetupProgress("Set Up", "Setup Clothes Info", 0.07f);
        yield return null;
        IEnumerator setupClothEnum = this.SetupClothInstances(config.targetClothesObject, config.clothesHumanoidMatchedBones, config.sourceClothesObject, meshMatcher);
        while (setupClothEnum.MoveNext())
        {
            yield return setupClothEnum.Current;
        }
        yield return null;
        float fittingRadius = config.worldRadius;
        meshMatcher.bodyBVH = targetBodyBVH;
        VertexFittingUtil vertexFittingUtil = new VertexFittingUtil();
        int fittingI;
        for (fittingI = 0; fittingI < config.fittingExpandIteration; fittingI++)
        {
            foreach (ClothInstance clothInstance in this.clothInstances)
            {
                this.SetupProgress("Fitting", $"Phase 1 Expanding [{fittingI + 1}/{config.fittingExpandIteration}] - {clothInstance.smr.transform.name}", Mathf.Lerp(0.1f, 0.4f, (float)fittingI / (float)config.fittingExpandIteration));
                yield return null;
                vertexFittingUtil.ExpandClothes_World(clothInstance, this.clothInstances, meshMatcher, config, fittingRadius);
                yield return null;
            }
            if (fittingI % 2 == 0)
            {
                fittingRadius *= 0.97f;
            }
        }
        fittingRadius = config.worldRadius;
        for (fittingI = 0; fittingI < config.fittingShrinkIteration; fittingI++)
        {
            foreach (ClothInstance clothInstance in this.clothInstances)
            {
                this.SetupProgress("Fitting", $"Phase 2 Fitting [{fittingI + 1}/{config.fittingShrinkIteration}] - {clothInstance.smr.transform.name}", Mathf.Lerp(0.4f, 0.8f, (float)fittingI / (float)config.fittingShrinkIteration));
                yield return null;
                vertexFittingUtil.ShrinkClothes_World(clothInstance, this.clothInstances, meshMatcher, config, fittingRadius);
                yield return null;
                vertexFittingUtil.ExpandClothes_World(clothInstance, this.clothInstances, meshMatcher, config, fittingRadius);
                yield return null;
            }
            if (fittingI % 2 == 0)
            {
                fittingRadius *= 0.97f;
            }
        }
        fittingRadius = config.worldRadius / 2f;
        for (fittingI = 0; fittingI < config.fittingExpandIteration; fittingI++)
        {
            foreach (ClothInstance clothInstance in this.clothInstances)
            {
                this.SetupProgress("Fitting", $"Phase 3 Fine-tuning [{fittingI + 1}/{config.fittingExpandIteration}] - {clothInstance.smr.transform.name}", Mathf.Lerp(0.8f, 0.9f, (float)fittingI / (float)config.fittingExpandIteration));
                yield return null;
                vertexFittingUtil.ExpandClothes_World(clothInstance, this.clothInstances, meshMatcher, config, fittingRadius);
                yield return null;
            }
            if (fittingI % 2 == 0)
            {
                fittingRadius *= 0.97f;
            }
        }
        yield return null;
        this.SetupProgress("Fitting", "Apply Transform Bone", 0.93f);
        yield return null;
        BoneAlignmentUtil boneAlignmentUtil = new BoneAlignmentUtil();
        boneAlignmentUtil.CleanupTempBones(config.targetClothesObject.transform);
        MorpherMode morpherMode2 = morpherMode;
        if (morpherMode2 == MorpherMode.AutoMorpher || morpherMode2 == MorpherMode.ProfileMorpher)
        {
            config.targetClothesObject.transform.localScale = Vector3.one;
            boneAlignmentUtil.AlignClothBonesToAvatar(this.clothInstances[0], config.targetAvatarObject.GetComponent<Animator>());
        }
        yield return null;
        this.SetupProgress("Fitting", "Apply Transform Mesh", 0.99f);
        yield return null;
        fittingI = 0;
        while (fittingI < this.clothInstances.Count)
        {
            ClothInstance clothInstance = this.clothInstances[fittingI];
            this.SetupProgress("Fitting", $"Apply to Mesh [{fittingI + 1}/{this.clothInstances.Count}] - {clothInstance.smr.transform.name}", Mathf.Lerp(0.93f, 0.98f, (float)fittingI / (float)this.clothInstances.Count));
            yield return null;
            clothInstance.ApplyWorldVerticesToMesh();
            clothInstance.smr.localBounds = new Bounds(Vector3.zero, Vector3.one * 2f);
            EditorUtility.SetDirty(clothInstance.smr.sharedMesh);
            EditorUtility.SetDirty(clothInstance.smr);
            yield return null;
            int num = fittingI + 1;
            fittingI = num;
        }
        yield return null;
        this.SetupProgress("Saving", "Saving Mesh Data", 0.99f);
        yield return null;
        this.SaveEditedMeshesToAssets(this.clothInstances, config);
        yield return null;
        this.SetupProgress("Finishing", "Finishing", 1f);
        foreach (ClothInstance clothInstance2 in this.clothInstances)
        {
            clothInstance2.Dispose();
        }
        this.clothInstances = null;
        yield return null;
    }

    public IEnumerator WeightingEnumerator(EdenAutoMorpherConfig config)
    {
        this.SetupProgress("Set Up", "Setup Target Avatar Info", 0f);
        yield return null;
        if (new MeshMatcher().BuildBvhMulti(config.targetBodyMeshes, config.targetAvatarObject.GetComponent<Animator>()) == null)
        {
            throw new AutoMorpherException("targetBodyBVH is Null", "[EdenAutoMorpherManater] FittingIteration\n - targetBodyBVH is null");
        }
        yield return null;
        List<Mesh> bakedTargetBodyMeshes = new List<Mesh>();
        foreach (SkinnedMeshRenderer bodyMesh in config.targetBodyMeshes)
        {
            this.SetupProgress("Set Up", "Setup Target Avatar BodyMesh Info", Mathf.Lerp(0.02f, 0.05f, bakedTargetBodyMeshes.Count / config.targetBodyMeshes.Count));
            yield return null;
            Mesh mesh = new Mesh();
            bodyMesh.BakeMesh(mesh);
            mesh.triangles = bodyMesh.sharedMesh.triangles;
            mesh.boneWeights = bodyMesh.sharedMesh.boneWeights;
            bakedTargetBodyMeshes.Add(mesh);
        }
        yield return null;
        IEnumerator setupClothEnum = this.SetupClothInstances(config.targetClothesObject, config.clothesHumanoidMatchedBones, duplicateMesh: false);
        while (setupClothEnum.MoveNext())
        {
            yield return setupClothEnum.Current;
        }
        yield return null;
        Dictionary<Transform, Transform> clonedBoneMap = null;
        if (!config.transferWeightToAvatar)
        {
            new EdenAutoMorpher_SetUpUtil().CreateClothesArmature(config, out clonedBoneMap);
        }
        yield return null;
        WeightTransferUtil weightTransferUtil = new WeightTransferUtil();
        WeightTransferUtil.Settings wtSettings = new WeightTransferUtil.Settings
        {
            maxDistance = 0.07f,
            maxNormalAngleDeg = 35f,
            allowFlippedNormal = true,
            enableSmoothing = true,
            smoothingIterations = 4,
            smoothingAlpha = 0.25f,
            enforceFourBoneLimit = true,
            weightInClothes = !config.transferWeightToAvatar
        };
        yield return null;
        int weightingI = 0;
        while (weightingI < this.clothInstances.Count)
        {
            ClothInstance clothInstance = this.clothInstances[weightingI];
            float num = 0.1f;
            float num2 = (0.95f - num) / (float)Mathf.Max(1, this.clothInstances.Count);
            float num3 = num + num2 * (float)weightingI;
            float num4 = 0.8f;
            float weightingStart = num3;
            float weightingEnd = num3 + num2 * num4;
            float applyStart = num3 + num2 * num4;
            int step = 1;
            this.SetupProgress("Weighting", $"Weighting | Step {step} [{weightingI + 1}/{this.clothInstances.Count}] - {clothInstance.smr.transform.name}", weightingStart);
            yield return null;
            IEnumerator weightingEnum = weightTransferUtil.RetargetAndTransfer(clothInstance, config.targetAvatarObject.GetComponent<Animator>(), config.targetBodyMeshes, bakedTargetBodyMeshes, 0, wtSettings, config.clothBoneTypeMap, clonedBoneMap);
            while (weightingEnum.MoveNext())
            {
                yield return null;
                step++;
                float t = 1f - 1f / ((float)step + 1f);
                float progress = Mathf.Lerp(weightingStart, weightingEnd, t);
                this.SetupProgress("Weighting", $"Weighting | Step {step} [{weightingI + 1}/{this.clothInstances.Count}] - {clothInstance.smr.transform.name}", progress);
                yield return weightingEnum.Current;
            }
            yield return null;
            step++;
            this.SetupProgress("Weighting", $"Weighting | Step {step} - Apply to Mesh [{weightingI + 1}/{this.clothInstances.Count}] - {clothInstance.smr.transform.name}", applyStart);
            yield return null;
            clothInstance.ApplyWorldVerticesToMesh();
            clothInstance.smr.localBounds = new Bounds(Vector3.zero, Vector3.one * 2f);
            EditorUtility.SetDirty(clothInstance.smr.sharedMesh);
            EditorUtility.SetDirty(clothInstance.smr);
            yield return null;
            int num5 = weightingI + 1;
            weightingI = num5;
        }
        this.SetupProgress("Weighting", "Allocate Bones", 0.95f);
        yield return null;
        if (config.transferWeightToAvatar && config.isReparentAccessoryBonesToTargetAvatar)
        {
            new BoneAlignmentUtil().ReparentAccessoryBonesToAvatar(config);
        }
        this.SetupProgress("Finishing", "Finishing", 1f);
        yield return null;
        foreach (ClothInstance clothInstance2 in this.clothInstances)
        {
            clothInstance2.Dispose();
        }
        this.clothInstances = null;
        yield return null;
    }

    private void SaveEditedMeshesToAssets(List<ClothInstance> clothInstances, EdenAutoMorpherConfig config)
    {
        if (clothInstances == null || clothInstances.Count == 0)
        {
            Debug.LogWarning("[EdenAutoMorpher] 저장할 ClothInstance가 없습니다.");
            throw new AutoMorpherException("Can't Save Mesh", "Therer is no ClothInstance to save mesh");
        }
        if (config.targetClothesObject == null || config.targetAvatarObject == null)
        {
            Debug.LogWarning("[EdenAutoMorpher] targetClothesObject 또는 targetAvatarObject 가 null 입니다. 메쉬 저장 경로를 만들 수 없습니다.");
            throw new AutoMorpherException("Can't Save Mesh", "Target Clothes Object or Target Avatar Object is null.\nCan't Create Mesh Folder");
        }
        string text = "Assets/@Eden_Mesh";
        if (!AssetDatabase.IsValidFolder(text))
        {
            AssetDatabase.CreateFolder("Assets", "@Eden_Mesh");
        }
        string text2 = SanitizeForAssetName(config.targetAvatarObject.name);
        string text3 = text + "/" + text2;
        if (!AssetDatabase.IsValidFolder(text3))
        {
            AssetDatabase.CreateFolder(text, text2);
        }
        string text4 = SanitizeForAssetName(config.targetClothesObject.name);
        string text5 = text3 + "/" + text4;
        if (!AssetDatabase.IsValidFolder(text5))
        {
            AssetDatabase.CreateFolder(text3, text4);
        }
        string text6 = AssetDatabase.GenerateUniqueAssetPath(text5 + "/" + text4 + "_Meshes.asset");
        int num = 0;
        AssetDatabase.CreateAsset(new Mesh
        {
            name = text4 + "_RootMesh"
        }, text6);
        foreach (ClothInstance clothInstance in clothInstances)
        {
            if (clothInstance != null && !(clothInstance.smr == null))
            {
                Mesh sharedMesh = clothInstance.smr.sharedMesh;
                if (!(sharedMesh == null))
                {
                    Mesh mesh = UnityEngine.Object.Instantiate(sharedMesh);
                    mesh.name = SanitizeForAssetName(clothInstance.smr.gameObject.name + "_EditedMesh");
                    AssetDatabase.AddObjectToAsset(mesh, text6);
                    clothInstance.smr.sharedMesh = mesh;
                    clothInstance.editableMesh = clothInstance.smr.sharedMesh;
                    EditorUtility.SetDirty(clothInstance.smr);
                    EditorUtility.SetDirty(clothInstance.smr.sharedMesh);
                    EditorUtility.SetDirty(clothInstance.smr.gameObject);
                    num++;
                }
            }
        }
        AssetDatabase.SaveAssets();
        AssetDatabase.Refresh();
        Debug.Log($"[EdenAutoMorpher] 편집된 메쉬 {num}개를 " + "'" + text6 + "' 에 서브 에셋으로 저장했습니다. (원본 프리팹은 변경하지 않음)");
        static string SanitizeForAssetName(string rawName)
        {
            char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
            foreach (char oldChar in invalidFileNameChars)
            {
                rawName = rawName.Replace(oldChar, '_');
            }
            return rawName.Trim();
        }
    }

    public void Dispose()
    {
    }
}
