// 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.MeshClassifier
using System;
using System.Collections.Generic;
using Eden.AutoMorpher;
using UnityEngine;

public class MeshClassifier
{
	private HumanBodyBones[] bodyBones = new HumanBodyBones[28]
	{
		HumanBodyBones.Hips,
		HumanBodyBones.Spine,
		HumanBodyBones.Chest,
		HumanBodyBones.Neck,
		HumanBodyBones.LeftShoulder,
		HumanBodyBones.LeftUpperArm,
		HumanBodyBones.LeftLowerArm,
		HumanBodyBones.LeftHand,
		HumanBodyBones.LeftThumbProximal,
		HumanBodyBones.LeftIndexProximal,
		HumanBodyBones.LeftMiddleProximal,
		HumanBodyBones.LeftRingProximal,
		HumanBodyBones.LeftLittleProximal,
		HumanBodyBones.RightShoulder,
		HumanBodyBones.RightUpperArm,
		HumanBodyBones.RightLowerArm,
		HumanBodyBones.RightHand,
		HumanBodyBones.RightThumbProximal,
		HumanBodyBones.RightIndexProximal,
		HumanBodyBones.RightMiddleProximal,
		HumanBodyBones.RightRingProximal,
		HumanBodyBones.RightLittleProximal,
		HumanBodyBones.LeftUpperLeg,
		HumanBodyBones.LeftLowerLeg,
		HumanBodyBones.LeftFoot,
		HumanBodyBones.RightUpperLeg,
		HumanBodyBones.RightLowerLeg,
		HumanBodyBones.RightFoot
	};

	private HumanBodyBones[] headBones = new HumanBodyBones[3]
	{
		HumanBodyBones.Head,
		HumanBodyBones.LeftEye,
		HumanBodyBones.RightEye
	};

	public SkinnedMeshRenderer GetBodyMesh(Transform root, Animator animator)
	{
		List<Transform> list = HumanBodyBonesTrsnforms(bodyBones, animator);
		if (list.Count != bodyBones.Length)
		{
			if (AutoMorpherDev.isDeveloperMode)
			{
				Debug.LogWarning("[Body Mesh] Animator Bone is not enough");
			}
			return null;
		}
		return GetBoneMatchedMesh(root, list);
	}

	public SkinnedMeshRenderer GetHeadMesh(Transform root, Animator animator)
	{
		List<Transform> list = HumanBodyBonesTrsnforms(headBones, animator);
		if (list.Count != headBones.Length)
		{
			return null;
		}
		return GetBoneMatchedMesh(root, list);
	}

	private List<Transform> HumanBodyBonesTrsnforms(HumanBodyBones[] humanBonesList, Animator animator)
	{
		List<Transform> list = new List<Transform>();
		List<HumanBodyBones> list2 = new List<HumanBodyBones>();
		foreach (HumanBodyBones humanBodyBones in humanBonesList)
		{
			Transform boneTransform = animator.GetBoneTransform(humanBodyBones);
			if (boneTransform == null)
			{
				list2.Add(humanBodyBones);
			}
			else
			{
				list.Add(boneTransform);
			}
		}
		if (list2.Count > 0)
		{
			string text = string.Join(", ", list2);
			throw new AutoMorpherException("[Body Mesh Finding] Required Humanoid Bones are Missing", "[BodyMeshUtil] HumanBodyBonesTrsnforms\n - Missing Humanoid Bones: [" + text + "]\n - Animator Humanoid mapping may be broken\n - Please check whether the missing humanoid bones are correctly assigned in [Animator → Avatar → Configure].");
		}
		return list;
	}

	private SkinnedMeshRenderer GetBoneMatchedMesh(Transform root, List<Transform> humanBoneTransforms)
	{
		SkinnedMeshRenderer[] componentsInChildren = root.GetComponentsInChildren<SkinnedMeshRenderer>(includeInactive: false);
		foreach (SkinnedMeshRenderer skinnedMeshRenderer in componentsInChildren)
		{
			bool flag = true;
			HashSet<Transform> activeBones = GetActiveBones(skinnedMeshRenderer);
			if (AutoMorpherDev.isDeveloperMode)
			{
				Debug.Log($"[Body Mesh] {skinnedMeshRenderer.gameObject.name} have bone Set {activeBones.Count}");
			}
			foreach (Transform humanBoneTransform in humanBoneTransforms)
			{
				if (!activeBones.Contains(humanBoneTransform) && !BoneExistsByPosition(humanBoneTransform, activeBones))
				{
					flag = false;
					if (AutoMorpherDev.isDeveloperMode)
					{
						Debug.Log("[Body Mesh] " + skinnedMeshRenderer.gameObject.name + " Doesn't hav bone " + humanBoneTransform.name);
					}
					break;
				}
			}
			if (flag)
			{
				return skinnedMeshRenderer;
			}
		}
		return null;
	}

	private bool BoneExistsByPosition(Transform boneToCheck, HashSet<Transform> smrBoneSet, float posTolerance = 0.0001f)
	{
		foreach (Transform item in smrBoneSet)
		{
			if ((item.position - boneToCheck.position).sqrMagnitude <= posTolerance * posTolerance)
			{
				return true;
			}
		}
		return false;
	}

	public HashSet<Transform> GetActiveBones(SkinnedMeshRenderer smr, float weightThreshold = 0.0001f)
	{
		Mesh sharedMesh = smr.sharedMesh;
		if (sharedMesh == null)
		{
			Debug.LogWarning("SkinnedMeshRenderer에 연결된 Mesh가 없습니다.");
			return new HashSet<Transform>();
		}
		Transform[] bones = smr.bones;
		BoneWeight[] boneWeights = sharedMesh.boneWeights;
		HashSet<int> hashSet = new HashSet<int>();
		BoneWeight[] array = boneWeights;
		for (int i = 0; i < array.Length; i++)
		{
			BoneWeight boneWeight = array[i];
			if (boneWeight.weight0 > weightThreshold)
			{
				hashSet.Add(boneWeight.boneIndex0);
			}
			if (boneWeight.weight1 > weightThreshold)
			{
				hashSet.Add(boneWeight.boneIndex1);
			}
			if (boneWeight.weight2 > weightThreshold)
			{
				hashSet.Add(boneWeight.boneIndex2);
			}
			if (boneWeight.weight3 > weightThreshold)
			{
				hashSet.Add(boneWeight.boneIndex3);
			}
		}
		HashSet<Transform> hashSet2 = new HashSet<Transform>();
		foreach (int item in hashSet)
		{
			if (item >= 0 && item < bones.Length)
			{
				hashSet2.Add(bones[item]);
			}
		}
		return hashSet2;
	}

	public Dictionary<HumanBodyBones, HashSet<Transform>> MeshHumanoidBoneMatcher(Animator animator, IReadOnlyList<SkinnedMeshRenderer> bodyMeshes, float posTolerance = 0.0001f, float weightThreshold = 0.0001f)
	{
		Dictionary<HumanBodyBones, HashSet<Transform>> dictionary = new Dictionary<HumanBodyBones, HashSet<Transform>>();
		if (animator == null)
		{
			throw new AutoMorpherException("Animator is Missing", "[MeshHumanoidBoneMatcher] MeshHumanoidBoneMatcher\n - animator is null");
		}
		HashSet<Transform> hashSet = new HashSet<Transform>();
		if (bodyMeshes != null)
		{
			foreach (SkinnedMeshRenderer bodyMesh in bodyMeshes)
			{
				if (bodyMesh == null)
				{
					continue;
				}
				foreach (Transform activeBone in GetActiveBones(bodyMesh, weightThreshold))
				{
					if (activeBone != null)
					{
						hashSet.Add(activeBone);
					}
				}
			}
		}
		for (int i = 0; i < 55; i++)
		{
			HumanBodyBones humanBodyBones = (HumanBodyBones)i;
			Transform boneTransform = animator.GetBoneTransform(humanBodyBones);
			if (boneTransform == null)
			{
				continue;
			}
			HashSet<Transform> hashSet2 = new HashSet<Transform>();
			hashSet2.Add(boneTransform);
			foreach (Transform item in FindBonesByPosition(boneTransform, hashSet, posTolerance))
			{
				hashSet2.Add(item);
			}
			dictionary[humanBodyBones] = hashSet2;
		}
		return dictionary;
	}

	private List<Transform> FindBonesByPosition(Transform boneToCheck, HashSet<Transform> smrBoneSet, float posTolerance = 0.0001f)
	{
		List<Transform> list = new List<Transform>();
		if (boneToCheck == null)
		{
			return list;
		}
		float num = posTolerance * posTolerance;
		Vector3 position = boneToCheck.position;
		foreach (Transform item in smrBoneSet)
		{
			if (!(item == null) && !(item == boneToCheck) && NameMatches(item.gameObject.name, boneToCheck.gameObject.name) && (item.position - position).sqrMagnitude <= num)
			{
				list.Add(item);
			}
		}
		return list;
	}

	private string[] TokenizeBoneName(string name)
	{
		if (string.IsNullOrWhiteSpace(name))
		{
			return Array.Empty<string>();
		}
		char[] separator = new char[5] { '-', '_', ':', '.', '|' };
		name = name.Trim();
		return name.Split(separator, StringSplitOptions.RemoveEmptyEntries);
	}

	private bool NameMatches(string boneToCheckName, string candidateName)
	{
		string[] array = TokenizeBoneName(boneToCheckName);
		string[] array2 = TokenizeBoneName(candidateName);
		if (array.Length == 0 || array2.Length == 0)
		{
			return false;
		}
		if (!array[0].Equals(array2[0], StringComparison.OrdinalIgnoreCase))
		{
			return false;
		}
		if (array.Length > 1 && array2.Length > 1 && !array[1].Equals(array2[1], StringComparison.OrdinalIgnoreCase))
		{
			return false;
		}
		return true;
	}
}
