using KF3.Containers;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UI;

public class KF3FriendContainer : KF3CharacterContainer
{
    public bool
        EyesLocked = false;
    public float
        LookAtStrength;
    public Transform
        LeftEye,
        RightEye,
        SubWeapon,
        SubWeaponBone;
    public UIHandleEyes
        EyeTarget;
    public Quaternion
        LeftEyeRotation = Quaternion.identity,
        RightEyeRotation = Quaternion.identity;
    /// <summary> Original data </summary>
    public CharaModelReferencer
        Ears,
        EarsAlt,
        Tail,
        TailAlt;
    public CharaModelReferencer
        EarsAnimation,
        TailAnimation;
    /// <summary> Custom Animator </summary>
    public SimpleAnimation
        EarsSimpleAnimation,
        TailSimpleAnimation;

    public Dictionary<HumanBodyBones, Transform> HumanBones = new Dictionary<HumanBodyBones, Transform>();
    private Dictionary<Transform, UIHandle>     BoneHandles = new Dictionary<Transform, UIHandle>();
    //private Dictionary<Transform, HumanBodyBones> HumanBonesInverse = new Dictionary<Transform, HumanBodyBones>();

    public UIKF3MorphPanel MorphPanel;
    public UIKF3CharacterPanel CharacterPanel;

    private bool isFakeFriend = false;

    public static IEnumerator CreateAsync(string modelName, string animationSetName, bool fakeFriend = false, System.Action<KF3FriendContainer> callback = null)
    {
        GameObject go = new GameObject(modelName);
        var container = go.AddComponent<KF3FriendContainer>();
        if (fakeFriend) container.DoNotSave = container.isFakeFriend = true;

        yield return container.BuildGeneral(modelName, animationSetName);
        if (!container.isFakeFriend) UIHandleFriend.CreateAsChild(container);
        container.LoadInProgress = false;
        callback?.Invoke(container);
    }

    protected override void Start()
    {
        if (isFakeFriend) return;

        LeftEyeRotation = LeftEye.localRotation;
        RightEyeRotation = RightEye.localRotation;

        base.Start();
    }

    private void Update()
    {
        if (LoadInProgress || isFakeFriend) return;
        if (BodySimpleAnimation && BodySimpleAnimation.clip != null) //check if simple animation component has been set
        {
            SimpleAnimation.State currentClip = BodySimpleAnimation.GetStates().FirstOrDefault(state => state.clip == BodySimpleAnimation.clip);
            if (currentClip.normalizedTime == 1 && currentClip.speed > 0) //check if an animation is playing and if an animation was playing before
            {
                RewindAllAnimations();
            }
            else if (currentClip.normalizedTime <= 0 && currentClip.speed < 0) //check if an animation is playing and if an animation was playing before
            {
                UnwindAllAnimations();
            }
        }
        if ((LeftEye && RightEye && EyeTarget) && !EyesLocked)
        {
            Vector3 target = Vector3.Lerp(EyeTarget.transform.position, KF3ModelViewerMain.Instance.GetCamera().transform.position, LookAtStrength);
            LeftEye.LookAt(target, HumanBones[HumanBodyBones.Head].up);

            RightEye.LookAt(target, HumanBones[HumanBodyBones.Head].up);
        }
    }

    #region Setup
    public override void SetAnimationReferences()
    {
        if (BodyAnimation)
        {
            BodySimpleAnimation = Body.GetComponentInChildren<SimpleAnimation>();
            if (Main.OverrideClip != null)
            {
                foreach (var anim in BodyAnimation.prefabAnimeList)
                {
                    anim.clip = Main.OverrideClip;
                }
            }
            Body.refAnimationObj = BodyAnimation.gameObject;
            Body.JoinReferenceParam();
        }
        else
        {
            Error.Log(Color.blue, "Body Animations Missing");
        }

        if (Ears && EarsAnimation)
        {
            EarsSimpleAnimation = Ears.GetComponentInChildren<SimpleAnimation>();

            Ears.transform.SetParent(Body.boneList.FirstOrDefault(b => b.name == "j_head"));
            Ears.transform.ResetTransform();

            Ears.refAnimationObj = EarsAnimation.gameObject;
            Ears.JoinReferenceParam();

            if (EarsAlt)
            {
                EarsAlt.transform.SetParent(Body.boneList.FirstOrDefault(b => b.name == "j_head"));
                EarsAlt.transform.ResetTransform();
                EarsAlt.GetComponentInChildren<Renderer>().enabled = false;
            }
        }
        else
        {
            Error.Log(Color.blue, "Ears Missing");
        }

        if (Tail && TailAnimation)
        {
            TailSimpleAnimation = Tail.GetComponentInChildren<SimpleAnimation>();

            Tail.transform.SetParent(Body.boneList.FirstOrDefault(b => b.name == (string.IsNullOrEmpty(BodyAnimation.tailNodeName) ? "j_lowerbody" : BodyAnimation.tailNodeName)));
            Tail.transform.ResetTransform();

            Tail.refAnimationObj = TailAnimation.gameObject;
            Tail.JoinReferenceParam();

            if (TailAlt)
            {
                TailAlt.transform.SetParent(Body.boneList.FirstOrDefault(b => b.name == (string.IsNullOrEmpty(BodyAnimation.tailNodeName) ? "j_lowerbody" : BodyAnimation.tailNodeName)));
                TailAlt.transform.ResetTransform();
                TailAlt.GetComponentInChildren<Renderer>().enabled = false;
            }
        }
        else
        {
            Error.Log(Color.blue, "Tail Missing");
        }
    }

    public override void SetBones()
    {
        if (isFakeFriend) return;

        HumanBones = new Dictionary<HumanBodyBones, Transform>();
        var extraBones = Body.boneList.Where(b => b.name.Contains("osg"));
        HumanBones.Add(HumanBodyBones.Spine,            Body.transform.Find("root/pelvis"));
        HumanBones.Add(HumanBodyBones.Hips,             Body.transform.Find("root/pelvis/j_lowerbody"));
        HumanBones.Add(HumanBodyBones.RightUpperLeg,    Body.transform.Find("root/pelvis/j_lowerbody/j_thigh_r"));
        HumanBones.Add(HumanBodyBones.RightLowerLeg,    Body.transform.Find("root/pelvis/j_lowerbody/j_thigh_r/j_leg_r"));
        HumanBones.Add(HumanBodyBones.RightFoot,        Body.transform.Find("root/pelvis/j_lowerbody/j_thigh_r/j_leg_r/j_foot_r"));
        HumanBones.Add(HumanBodyBones.LeftUpperLeg,     Body.transform.Find("root/pelvis/j_lowerbody/j_thigh_l"));
        HumanBones.Add(HumanBodyBones.LeftLowerLeg,     Body.transform.Find("root/pelvis/j_lowerbody/j_thigh_l/j_leg_l"));
        HumanBones.Add(HumanBodyBones.LeftFoot,         Body.transform.Find("root/pelvis/j_lowerbody/j_thigh_l/j_leg_l/j_foot_l"));
        HumanBones.Add(HumanBodyBones.Chest,            Body.transform.Find("root/pelvis/j_upperbody"));
        HumanBones.Add(HumanBodyBones.UpperChest,       Body.transform.Find("root/pelvis/j_upperbody/j_chest"));
        HumanBones.Add(HumanBodyBones.Neck,             Body.transform.Find("root/pelvis/j_upperbody/j_chest/j_neck"));
        HumanBones.Add(HumanBodyBones.Head,             Body.transform.Find("root/pelvis/j_upperbody/j_chest/j_neck/j_head"));
        HumanBones.Add(HumanBodyBones.RightShoulder,            Body.transform.Find("root/pelvis/j_upperbody/j_chest/j_shoulder_r"));
        HumanBones.Add(HumanBodyBones.RightUpperArm,            Body.transform.Find("root/pelvis/j_upperbody/j_chest/j_shoulder_r/j_arm_r"));
        HumanBones.Add(HumanBodyBones.RightLowerArm,            Body.transform.Find("root/pelvis/j_upperbody/j_chest/j_shoulder_r/j_arm_r/j_forearm_r"));
        HumanBones.Add(HumanBodyBones.RightHand,                Body.transform.Find("root/pelvis/j_upperbody/j_chest/j_shoulder_r/j_arm_r/j_forearm_r/j_wrist_r"));
        HumanBones.Add(HumanBodyBones.RightIndexProximal,       Body.transform.Find("root/pelvis/j_upperbody/j_chest/j_shoulder_r/j_arm_r/j_forearm_r/j_wrist_r/j_index_a_r"));
        HumanBones.Add(HumanBodyBones.RightIndexIntermediate,   Body.transform.Find("root/pelvis/j_upperbody/j_chest/j_shoulder_r/j_arm_r/j_forearm_r/j_wrist_r/j_index_a_r/j_index_b_r"));
        HumanBones.Add(HumanBodyBones.RightRingProximal,        Body.transform.Find("root/pelvis/j_upperbody/j_chest/j_shoulder_r/j_arm_r/j_forearm_r/j_wrist_r/j_ring_a_r"));
        HumanBones.Add(HumanBodyBones.RightRingIntermediate,    Body.transform.Find("root/pelvis/j_upperbody/j_chest/j_shoulder_r/j_arm_r/j_forearm_r/j_wrist_r/j_ring_a_r/j_ring_b_r"));
        HumanBones.Add(HumanBodyBones.RightThumbProximal,       Body.transform.Find("root/pelvis/j_upperbody/j_chest/j_shoulder_r/j_arm_r/j_forearm_r/j_wrist_r/j_thumb_a_r"));
        HumanBones.Add(HumanBodyBones.RightThumbIntermediate,   Body.transform.Find("root/pelvis/j_upperbody/j_chest/j_shoulder_r/j_arm_r/j_forearm_r/j_wrist_r/j_thumb_a_r/j_thumb_b_r"));
        HumanBones.Add(HumanBodyBones.LeftShoulder,             Body.transform.Find("root/pelvis/j_upperbody/j_chest/j_shoulder_l"));
        HumanBones.Add(HumanBodyBones.LeftUpperArm,             Body.transform.Find("root/pelvis/j_upperbody/j_chest/j_shoulder_l/j_arm_l"));
        HumanBones.Add(HumanBodyBones.LeftLowerArm,             Body.transform.Find("root/pelvis/j_upperbody/j_chest/j_shoulder_l/j_arm_l/j_forearm_l"));
        HumanBones.Add(HumanBodyBones.LeftHand,                 Body.transform.Find("root/pelvis/j_upperbody/j_chest/j_shoulder_l/j_arm_l/j_forearm_l/j_wrist_l"));
        HumanBones.Add(HumanBodyBones.LeftIndexProximal,        Body.transform.Find("root/pelvis/j_upperbody/j_chest/j_shoulder_l/j_arm_l/j_forearm_l/j_wrist_l/j_index_a_l"));
        HumanBones.Add(HumanBodyBones.LeftIndexIntermediate,    Body.transform.Find("root/pelvis/j_upperbody/j_chest/j_shoulder_l/j_arm_l/j_forearm_l/j_wrist_l/j_index_a_l/j_index_b_l"));
        HumanBones.Add(HumanBodyBones.LeftRingProximal,         Body.transform.Find("root/pelvis/j_upperbody/j_chest/j_shoulder_l/j_arm_l/j_forearm_l/j_wrist_l/j_ring_a_l"));
        HumanBones.Add(HumanBodyBones.LeftRingIntermediate,     Body.transform.Find("root/pelvis/j_upperbody/j_chest/j_shoulder_l/j_arm_l/j_forearm_l/j_wrist_l/j_ring_a_l/j_ring_b_l"));
        HumanBones.Add(HumanBodyBones.LeftThumbProximal,        Body.transform.Find("root/pelvis/j_upperbody/j_chest/j_shoulder_l/j_arm_l/j_forearm_l/j_wrist_l/j_thumb_a_l"));
        HumanBones.Add(HumanBodyBones.LeftThumbIntermediate,    Body.transform.Find("root/pelvis/j_upperbody/j_chest/j_shoulder_l/j_arm_l/j_forearm_l/j_wrist_l/j_thumb_a_l/j_thumb_b_l"));
        //HumanBonesInverse = HumanBones.Where(d=>d.Value != null).ToDictionary((i) => i.Value, (i) => i.Key);

        if (Tail)
        {
            extraBones = extraBones.Union(Tail.boneList.Where(b => b.name.Contains("osg")));
        }
        if (TailAlt)
        {
            extraBones = extraBones.Union(TailAlt.boneList.Where(b => b.name.Contains("osg")));
        }
        if (Ears)
        {
            extraBones = extraBones.Union(Ears.boneList.Where(b => b.name.Contains("osg")));
        }
        if (EarsAlt)
        {
            extraBones = extraBones.Union(EarsAlt.boneList.Where(b => b.name.Contains("osg")));
        }

        var allBones = HumanBones.Where(b => b.Value != null).Select(b => b.Value).Union(Body.transform.Find("root")?.GetComponentsInChildren<Transform>().Where(t => t.name.Contains("weapon")).ToList()).Union(extraBones).Distinct();

        foreach (var bone in allBones)
        {
            var tags = new List<SerializableBone.BoneTags>();
            UIHandle handle;

            if (HumanBones.ContainsValue(bone))
                tags.Add(SerializableBone.BoneTags.Humanoid);
            if (bone.name.EndsWith("_l"))
                tags.Add(SerializableBone.BoneTags.Left);
            else if (bone.name.EndsWith("_r"))
                tags.Add(SerializableBone.BoneTags.Right);

            if (extraBones.Contains(bone))
            {
                if (bone.GetComponent<Osage>() != null)
                {
                    tags.Add(SerializableBone.BoneTags.Dynamic);
                    handle = UIHandlePhysicsBone.CreateAsChild(this, bone, tags).SetColor(Color.cyan);
                }
                else
                {
                    handle = UIHandleBone.CreateAsChild(this, bone, tags).SetColor(Color.cyan);
                }
            }
            else
            {
                handle = UIHandleBone.CreateAsChild(this, bone, tags);
            }

            if (bone == HumanBones[HumanBodyBones.Hips])
                handle.SetOffset(bone.up * -0.02f);
            if (bone == HumanBones[HumanBodyBones.Chest])
                handle.SetOffset(bone.up * -0.02f);

            BoneHandles.Add(bone, handle);
        }
    }

    public override void DoStuff()
    {
        if (isFakeFriend) return;

        UIKF3MorphSlider eyeBase = null;

        foreach (Renderer r in GetComponentsInChildren<Renderer>())
        {
            foreach (Material mat in r.materials)
            {
#if UNITY_ANDROID
                    if(!mat.shader.name.EndsWith("V2"))
                        mat.shader = Shader.Find(mat.shader.name + "V2");
#endif
                if (mat.shader.name == "PRD/Cheek")
                {
                    Texture2D tex = mat.GetTexture("_BaseMap") as Texture2D;
                    mat.shader = Shader.Find("Unlit/Transparent");
                    mat.mainTexture = tex;
                }
            }
            Renderers.Add(r);
        }

        var morphPanel = MorphPanel = UIKF3MorphPanel.Create(this);
        var charaPanel = CharacterPanel = UIKF3CharacterPanel.Create(this);
        InstantiatedObjects.Add(morphPanel.gameObject);
        InstantiatedObjects.Add(charaPanel.gameObject);

        foreach (Transform child in GetComponentsInChildren<Transform>())
        {
            switch (child.name)
            {
                case "md_eye_base":
                    {
                        UIKF3MorphSlider.CreateLookAt(this, morphPanel.EyesPanel);

                        SkinnedMeshRenderer meshRenderer = child.GetComponent<SkinnedMeshRenderer>();
                        if (meshRenderer == null || meshRenderer.sharedMesh == null) break;
                        for (int i = 0; i < meshRenderer.sharedMesh.blendShapeCount; i++)
                        {
                            UIKF3MorphSlider.CreateMorph(this, meshRenderer, i, "BS_eye.eye_", morphPanel.EyesPanel);
                        }

                        if (HumanBones.ContainsKey(HumanBodyBones.Head) && HumanBones[HumanBodyBones.Head] != null && !EyeTarget)
                        {
                            Transform head = HumanBones[HumanBodyBones.Head];
                            EyeTarget = UIHandleEyes.Create(this, head);
                        }
                        eyeBase = UIKF3MorphSlider.CreateEyeToggle(this, meshRenderer, morphPanel.EyesPanel, MorphPanel.EyesToggleGroup);
                        break;
                    }
                case "md_eye_special_a":
                case "md_eye_special_b":
                    {
                        SkinnedMeshRenderer meshRenderer = child.GetComponent<SkinnedMeshRenderer>();
                        var toggle = UIKF3MorphSlider.CreateEyeToggle(this, meshRenderer, morphPanel.EyesPanel, MorphPanel.EyesToggleGroup);
                        break;
                    }
                case "md_mouth_base":
                    {
                        SkinnedMeshRenderer meshRenderer = child.GetComponent<SkinnedMeshRenderer>();
                        if (meshRenderer == null || meshRenderer.sharedMesh == null) break;
                        for (int i = 0; i < meshRenderer.sharedMesh.blendShapeCount; i++)
                        {
                            UIKF3MorphSlider.CreateMorph(this, meshRenderer, i, "BS_mouth.mouth_", morphPanel.MouthPanel);
                        }
                        break;
                    }
                case "md_eye_l": LeftEye = child; break;
                case "md_eye_r": RightEye = child; break;
                case "md_brow_base":
                    {
                        SkinnedMeshRenderer meshRenderer = child.GetComponent<SkinnedMeshRenderer>();
                        if (meshRenderer == null || meshRenderer.sharedMesh == null) break;

                        for (int i = 0; i < meshRenderer.sharedMesh.blendShapeCount; i++)
                        {
                            UIKF3MorphSlider.CreateMorph(this, meshRenderer, i, "BS_brow.brow_", morphPanel.EyebrowsPanel);
                        }
                        break;
                    }
                case "md_subweapon":
                    {
                        SubWeapon = child;
                        if (SubWeaponBone == null) break;
                        SubWeapon.SetParent(SubWeaponBone);
                        SubWeapon.SetPositionAndRotation(Vector3.zero, Quaternion.identity);
                        break;
                    }
                case "subweapon":
                    {
                        SubWeaponBone = child;
                        if (SubWeapon == null) break;
                        SubWeapon.SetParent(SubWeaponBone);
                        SubWeapon.SetPositionAndRotation(Vector3.zero, Quaternion.identity);
                        break;
                    }
            }
        }

        if(eyeBase != null)
        {
            eyeBase.Toggle.isOn = true;
        }

        SetIK(HumanBodyBones.LeftUpperArm);
        SetIK(HumanBodyBones.RightUpperArm);
        SetIK(HumanBodyBones.LeftUpperLeg);
        SetIK(HumanBodyBones.RightUpperLeg);
        ToggleIK(true);
    }

    public void SetIK(HumanBodyBones bone)
    {
        var cTr = transform;
        UIHandleIK ikHandle = null;
        Color orange = new Color(1.0f, 0.64f, 0.0f);
        switch (bone)
        {
            case HumanBodyBones.LeftUpperArm:
                {
                    if (HumanBones[HumanBodyBones.LeftHand] == null) return;
                    InverseKinematics IK = gameObject.AddComponent<InverseKinematics>();
                    IK.upperArm = HumanBones[HumanBodyBones.LeftUpperArm];
                    IK.forearm = HumanBones[HumanBodyBones.LeftLowerArm];
                    IK.hand = HumanBones[HumanBodyBones.LeftHand];

                    IK.elbow = UIHandleIK.CreateTransform("Elbow_L", transform, IK.forearm.position + -cTr.forward * 0.1f);
                    UIHandleIK.CreateAsChild(this, IK.elbow, orange, IK.name, 0.75f);

                    IK.target = UIHandleIK.CreateTransform("Hand_L", transform, IK.hand.position, IK.hand.eulerAngles);
                    ikHandle = UIHandleIK.CreateAsChild(this, IK.target, Color.green, IK.name, 2f);
                    ikHandle.HiddenHandles = new List<UIHandle> {
                        BoneHandles[IK.upperArm],
                        BoneHandles[IK.forearm],
                        BoneHandles[IK.hand],
                    };

                    IK.uppperArm_OffsetRotation = new Vector3(0, 90, 0);
                    IK.forearm_OffsetRotation = new Vector3(-90, 90, 0);
                    IK.SpawnLines(Color.green);
                    break;
                }
            case HumanBodyBones.RightUpperArm:
                {
                    if (HumanBones[HumanBodyBones.RightHand] == null) return;
                    InverseKinematics IK = gameObject.AddComponent<InverseKinematics>();
                    IK.upperArm = HumanBones[HumanBodyBones.RightUpperArm];
                    IK.forearm = HumanBones[HumanBodyBones.RightLowerArm];
                    IK.hand = HumanBones[HumanBodyBones.RightHand];

                    IK.elbow = UIHandleIK.CreateTransform("Elbow_R", transform, IK.forearm.position + -cTr.forward * 0.1f);
                    UIHandleIK.CreateAsChild(this, IK.elbow, orange, IK.name, 0.75f);

                    IK.target = UIHandleIK.CreateTransform("Hand_R", transform, IK.hand.position, IK.hand.eulerAngles);
                    ikHandle = UIHandleIK.CreateAsChild(this, IK.target, Color.blue, IK.name, 2f);
                    ikHandle.HiddenHandles = new List<UIHandle> {
                        BoneHandles[IK.upperArm],
                        BoneHandles[IK.forearm],
                        BoneHandles[IK.hand],
                    };

                    IK.uppperArm_OffsetRotation = new Vector3(0, 90, 0);
                    IK.forearm_OffsetRotation = new Vector3(-90, 90, 0);
                    IK.SpawnLines(Color.blue);
                    break;
                }
            case HumanBodyBones.LeftUpperLeg:
                {
                    if (HumanBones[HumanBodyBones.LeftFoot] == null) return;
                    InverseKinematics IK = gameObject.AddComponent<InverseKinematics>();
                    IK.upperArm = HumanBones[HumanBodyBones.LeftUpperLeg];
                    IK.forearm = HumanBones[HumanBodyBones.LeftLowerLeg];
                    IK.hand = HumanBones[HumanBodyBones.LeftFoot];

                    IK.elbow = UIHandleIK.CreateTransform("Knee_L", transform, IK.forearm.position + cTr.forward * 0.2f);
                    UIHandleIK.CreateAsChild(this, IK.elbow, orange, IK.name, 0.75f);

                    IK.target = UIHandleIK.CreateTransform("Foot_L", transform, IK.hand.position, IK.hand.eulerAngles + cTr.forward * 45);
                    ikHandle = UIHandleIK.CreateAsChild(this, IK.target, Color.green, IK.name, 2f);
                    ikHandle.HiddenHandles = new List<UIHandle> {
                        BoneHandles[IK.upperArm],
                        BoneHandles[IK.forearm],
                        BoneHandles[IK.hand],
                    };

                    IK.uppperArm_OffsetRotation = new Vector3(0, 90, 0);
                    IK.forearm_OffsetRotation = new Vector3(-90, 90, 0);
                    IK.hand_OffsetRotation = new Vector3(0, 0, -45);
                    IK.SpawnLines(Color.green);
                    break;
                }
            case HumanBodyBones.RightUpperLeg:
                {
                    if (HumanBones[HumanBodyBones.RightFoot] == null) return;
                    InverseKinematics IK = gameObject.AddComponent<InverseKinematics>();
                    IK.upperArm = HumanBones[HumanBodyBones.RightUpperLeg];
                    IK.forearm = HumanBones[HumanBodyBones.RightLowerLeg];
                    IK.hand = HumanBones[HumanBodyBones.RightFoot];

                    IK.elbow = UIHandleIK.CreateTransform("Knee_R", transform, IK.forearm.position + cTr.forward * 0.2f);
                    UIHandleIK.CreateAsChild(this, IK.elbow, orange, IK.name, 0.75f);

                    IK.target = UIHandleIK.CreateTransform("Foot_R", transform, IK.hand.position, IK.hand.eulerAngles + cTr.forward * 45);
                    ikHandle = UIHandleIK.CreateAsChild(this, IK.target, Color.blue, IK.name, 2f);
                    ikHandle.HiddenHandles = new List<UIHandle> {
                        BoneHandles[IK.upperArm],
                        BoneHandles[IK.forearm],
                        BoneHandles[IK.hand],
                    };

                    IK.uppperArm_OffsetRotation = new Vector3(0, 90, 0);
                    IK.forearm_OffsetRotation = new Vector3(-90, 90, 0);
                    IK.hand_OffsetRotation = new Vector3(0, 0, -45);
                    IK.SpawnLines(Color.blue);
                    break;
                }
        }
    }
    #endregion

    #region Animation

    public void PlayEarsAnimation(string clipName)
    {
        PlayAnimation(EarsSimpleAnimation, clipName);
    }

    public void PlayTailAnimation(string clipName)
    {
        PlayAnimation(TailSimpleAnimation, clipName);
    }

    #endregion

    #region Control
    public void UpdateLookAtTarget(float value)
    {
        LookAtStrength = value;
    }

    public void UpdateMorph(SkinnedMeshRenderer meshRenderer, int id, float value, Slider slider = null)
    {
        meshRenderer.SetBlendShapeWeight(id, value);
        Morphs[meshRenderer].First(m => m.Name == meshRenderer.sharedMesh.GetBlendShapeName(id)).Strength = value;
        if (slider)
            slider.SetValueWithoutNotify(value);
    }

    #endregion

    #region Toggles
    public void ToggleIK(bool value)
    {
        foreach (var ik in GetComponents<InverseKinematics>())
        {
            ik.SoftEnabled = value;
            ik.linesVisible = value;
        }
        foreach (UIHandleIK ikHandle in Handles.Where(h=>h.GetType() == typeof(UIHandleIK)))
        {
            foreach(var handle in ikHandle.HiddenHandles)
            {
                handle.ForceDisplayOff(value);
            }
        }
        IKEnabled = value;
    }

    public override void ToggleBonesVisible(bool value)
    {
        base.ToggleBonesVisible(value);
        if (EyeTarget != null)
        {
            EyeTarget.ToggleActive(value);
        }
    }
    #endregion

    public override void Select()
    {
        MorphPanel.gameObject.SetActive(true);
        CharacterPanel.gameObject.SetActive(true);
    }

    public override void Deselect()
    {
        MorphPanel.gameObject.SetActive(false);
        CharacterPanel.gameObject.SetActive(false);
    }

    public override void PastePose(KeyframeData keyframe1, PoseLoadOptions pasteParams)
    {
        var keyframe = keyframe1 as CharacterKeyframeData;

        this.CurrentAnimation = keyframe.CurrentAnimation;
        if (keyframe.CurrentAnimation == "")
        {
            ToggleIK(keyframe.IKEnabled);
            this.PlayAnimation(this.BodySimpleAnimation, "");
        }
        else
        {
            ToggleIK(false);
            int currentFrame = TimelineController.Instance.CurrentFrame;
            FrameContent startFrame = null;
            foreach (var frame in Frames)
            {
                if (frame.FrameNum <= currentFrame && (frame.ObjectData as CharacterKeyframeData).CurrentAnimation == keyframe.CurrentAnimation)
                {
                    if (startFrame == null || frame.FrameNum > startFrame.FrameNum)
                    {
                        startFrame = frame;
                    }
                }
            }
            float diff = currentFrame - startFrame.FrameNum;
            this.PlayAnimation(this.BodySimpleAnimation, keyframe.CurrentAnimation, diff * (TimelineController.Instance.FrameDelayMS/1000));
        }

        var body    = keyframe.Bones.FirstOrDefault(b => b.Name == "Body") ?? new BoneList();
        var ears    = keyframe.Bones.FirstOrDefault(b => b.Name == "Ears") ?? new BoneList();
        var tail    = keyframe.Bones.FirstOrDefault(b => b.Name == "Tail") ?? new BoneList();
        var earsAlt = keyframe.Bones.FirstOrDefault(b => b.Name == "EarsAlt") ?? new BoneList();
        var tailAlt = keyframe.Bones.FirstOrDefault(b => b.Name == "TailAlt") ?? new BoneList();

        if (pasteParams.Root)
        {
            keyframe.Root.ApplyTo(transform);
        }

        if (pasteParams.Body)
        {
            foreach (var bone in this.Body.boneList)
            {
                if (pasteParams.Other || bone.GetComponent<Osage>() == null)
                    body.Bones.FirstOrDefault(b => b.Name == bone.name)?.Transform.ApplyTo(bone, pasteParams);
            }
        }
        if (pasteParams.Tail)
        {
            if (this.Tail != null)
            {
                foreach (var bone in this.Tail.boneList)
                {
                    tail.Bones.FirstOrDefault(b => b.Name == bone.name)?.Transform.ApplyTo(bone, pasteParams);
                }
            }
            if (this.TailAlt != null)
            {
                foreach (var bone in this.TailAlt.boneList)
                {
                    tailAlt.Bones.FirstOrDefault(b => b.Name == bone.name)?.Transform.ApplyTo(bone, pasteParams);
                }
            }
        }
        if (pasteParams.Ears)
        {
            if (this.Ears != null)
            {
                foreach (var bone in this.Ears.boneList)
                {
                    ears.Bones.FirstOrDefault(b => b.Name == bone.name)?.Transform.ApplyTo(bone, pasteParams);
                }
            }
            if (this.EarsAlt != null && earsAlt != null)
            {
                foreach (var bone in this.EarsAlt.boneList)
                {
                    earsAlt.Bones.FirstOrDefault(b => b.Name == bone.name)?.Transform.ApplyTo(bone, pasteParams);
                }
            }
        }
        foreach (var ik in GetComponentsInChildren<InverseKinematics>())
        {
            var bones1 = keyframe.Bones.FirstOrDefault(b => b.Name == "Extra");
            if (bones1 != null)
            {
                bones1.Bones.FirstOrDefault(b => b.Name == ik.target.name)?.Transform.ApplyTo(ik.target);
                bones1.Bones.FirstOrDefault(b => b.Name == ik.elbow.name)?.Transform.ApplyTo(ik.elbow);
            }
        }
        if (pasteParams.Morphs)
        {
            //MorphPanel.PasteValues(keyframe.Renderers);
            foreach (var morph in keyframe.Morphs)
            {
                var rend = this.Morphs.Keys.FirstOrDefault(k => k.name == morph.ParentName);
                if (rend == null) continue;

                MorphHelper h = this.Morphs[rend].FirstOrDefault(m => m.Name == morph.Name);
                if (h != null) this.UpdateMorph(rend, rend.sharedMesh.GetBlendShapeIndex(morph.Name), morph.Strength, h.Slider);
            }
        }
        if (keyframe.Renderers != null)
        {
            foreach (var renderer in keyframe.Renderers)
            {
                var rend = this.Renderers.FirstOrDefault(r => KF3CharacterContainerSerializable.GetRendererName(this, r) == renderer.Name);
                if (rend != null)
                {
                    rend.enabled = renderer.Value;
                }
            }
        }
        this.TogglePhysics(keyframe.PhysicsEnabled);
        this.EyesLocked = keyframe.EyesLocked;
        this.LeftEyeRotation = Quaternion.Euler(keyframe.LeftEyeRotation);
        this.RightEyeRotation = Quaternion.Euler(keyframe.RightEyeRotation);
        this.EyeTarget.transform.localPosition = keyframe.EyeTarget;
        this.LookAtStrength = keyframe.LookAtStrength;
        this.CharacterPanel.UpdateSelection();
    }

    public override IEnumerator GetModel(string bodyIdLong, string costumeId)
    {
        KF3AssetLibrary lib = Main.Assets;
        List<KF3AssetBundle> abList = lib.AssetBundles.Where(ab => ab.FileName.StartsWith($"ch_{bodyIdLong}_")).ToList();
        KF3AssetBundle bundle;

        //load body
        bundle = abList.FirstOrDefault(ab => ab.FileName == $"ch_{bodyIdLong}_{costumeId}.prefab");
        yield return lib.LoadAsset(bundle, (ab) =>
        {
            Body = spawnAndGetReferencer(ab);
        });

        //load ears
        bundle = abList.FirstOrDefault(ab => ab.FileName == $"ch_{bodyIdLong}_{costumeId}_ear.prefab");
        if (bundle == null)
        {
            bundle = abList.FirstOrDefault(ab => ab.FileName == $"ch_{bodyIdLong}_a_ear.prefab");
        }
        yield return lib.LoadAsset(bundle, (ab) =>
        {
            Ears = spawnAndGetReferencer(ab);
        });

        bundle = abList.FirstOrDefault(ab => ab.FileName == $"ch_{bodyIdLong}_z_ear.prefab");
        yield return lib.LoadAsset(bundle, (ab) =>
        {
            EarsAlt = spawnAndGetReferencer(ab);
        });

        //load tail
        bundle = abList.FirstOrDefault(ab => ab.FileName == $"ch_{bodyIdLong}_{costumeId}_tail.prefab");
        if (bundle == null)
        {
            bundle = abList.FirstOrDefault(ab => ab.FileName == $"ch_{bodyIdLong}_a_tail.prefab");
        }
        yield return lib.LoadAsset(bundle, (ab) =>
        {
            Tail = spawnAndGetReferencer(ab);
        });

        bundle = abList.FirstOrDefault(ab => ab.FileName == $"ch_{bodyIdLong}_z_tail.prefab");
        yield return lib.LoadAsset(bundle, (ab) =>
        {
            TailAlt = spawnAndGetReferencer(ab);
        });
    }

    public override IEnumerator GetAnimations(string idLong)
    {
        KF3AssetLibrary lib = Main.Assets;
        KF3AssetBundle bundle;
        List<KF3AssetBundle> abList = lib.AssetBundles.Where(ab => ab.FileName.StartsWith($"ch_{idLong}_")).ToList();

        //load body motion
        bundle = abList.FirstOrDefault(ab => ab.FileName == $"ch_{idLong}_a_mot.prefab");
        yield return lib.LoadAsset(bundle, (ab) =>
        {
            BodyAnimation = spawnAndGetReferencer(ab);
        });

        //load ears motion
        bundle = abList.FirstOrDefault(ab => ab.FileName == $"ch_{idLong}_a_ear_mot.prefab");
        yield return lib.LoadAsset(bundle, (ab) =>
        {
            EarsAnimation = spawnAndGetReferencer(ab);
        });

        //load tail motion
        bundle = abList.FirstOrDefault(ab => ab.FileName == $"ch_{idLong}_a_tail_mot.prefab");
        yield return lib.LoadAsset(bundle, (ab) =>
        {
            TailAnimation = spawnAndGetReferencer(ab);
        });
    }
}
