using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

public class EversoulCharacterContainer : ObjectContainer, IAnimated
{
    public CharacterAsset Data;
    public string AnimationSet;
    public string Animation;

    public MorphPanel MorphPanel;
    public List<AnimationClip> Animations;
    private Animator _animator;
    private AnimatorOverrideController _animatorController;
    private Dictionary<HumanBodyBones, Transform> _humanBones = new Dictionary<HumanBodyBones, Transform>();

    public override void Select()
    {
        if (MorphPanel != null)
        {
            MorphPanel.gameObject.SetActive(true);
        }
    }

    public override void Deselect()
    {
        if (MorphPanel != null)
        {
            MorphPanel.gameObject.SetActive(false);
        }
    }

    public IEnumerator Build(CharacterAsset model)
    {
        Data = model;
        var path = EversoulAssetLibrary.Instance.GetResourcePath(model.ResourceName, out var dependencies);
        var modelPath = EversoulAssetLibrary.Instance.LocalFilesPath + path;
        var dependencyPaths = dependencies.Select(path => EversoulAssetLibrary.Instance.LocalFilesPath + path).ToList();
        Debug.LogError(modelPath);

        yield return EversoulAssetLibrary.Instance.LoadAssets(dependencyPaths);

        var bundle = AssetLibrary.LoadFromFile(modelPath);
        var asset = bundle.LoadAsset<GameObject>(model.ResourceName);
        var go = Instantiate(asset, this.transform);

        EversoulAssetLibrary.Instance.UnloadAssets(dependencyPaths);
        EversoulAssetLibrary.Instance.UnloadAsset(modelPath);

        SetDefaultMaterials(transform);

        Init();
    }

    public void Init()
    {
        _animator = GetComponentInChildren<Animator>();
        _animator.applyRootMotion = false;
        _animator.runtimeAnimatorController = _animatorController = Instantiate(SharedResources.Instance.GenericAnimatorController);
        SetBones();
        CreateMorphPanel();
    }

    public void PlayAnimation(AnimationClip anim)
    {
        if (anim == null)
        {
            Animation = "";
            _animator.enabled = false;
        }
        else
        {
            Animation = anim.name;
            _animator.enabled = true;
            _animatorController["Anim"] = anim;
        }
    }

    public void SetDefaultMaterials(Transform t)
    {
        List<Material> materials = new List<Material>();
        foreach (var renderer in t.GetComponentsInChildren<Renderer>())
        {
            foreach (var material in renderer.sharedMaterials)
            {
                if (materials.Contains(material)) continue;
                materials.Add(material);
                switch (material.shader.name)
                {
                    case "NineArk/Character/PC_Basic_Body":
                        material.shader = Shader.Find("Eversoul/Lit");
                        break;
                    case "NineArk/Character/PC_Basic_Body_Transparent":
                        material.shader = Shader.Find("Eversoul/Lit Transparent");
                        break;
                    case "NineArk/Character/NPC_Glass":
                        material.shader = Shader.Find("Eversoul/Glass");
                        break;
                    case "NineArk/Character/PC_Basic_Face":
                        material.shader = Shader.Find("Eversoul/Unlit");
                        break;
                    case "NineArk/Character/PC_Basic_Eye":
                        material.shader = Shader.Find("Eversoul/BasicEye");
                        break;
                    case "NineArk/Outline/PC_Outline":
                        material.shader = Shader.Find("Eversoul/Outline");
                        break;
                    case "NineArk/Outline/PC_Outline_Transparent":
                        material.shader = Shader.Find("Eversoul/Outline Transparent");
                        break;
                    default:
                        Debug.LogError("Shader not found: " + material.shader.name);
                        //material.shader = Shader.Find("Eversoul/Lit");
                        break;
                }
            }
        }
    }

    public void SetBones()
    {
        var humanBones = new Dictionary<HumanBodyBones, Transform>();
        var allBones = new Dictionary<string, Transform>();
        foreach(var bone in _animator.transform.GetComponentsInChildren<Transform>())
        {
            if (!allBones.ContainsKey(bone.name))
            {
                allBones.Add(bone.name, bone);
            }
            else
            {
                Debug.LogWarning("Duplicate bone: " + bone.name);
            }
        }
        humanBones.Add(HumanBodyBones.Hips,                     TryGet(allBones, "Bip001 Pelvis"));
        humanBones.Add(HumanBodyBones.Spine,                    TryGet(allBones, "Bip001 Spine"));
        humanBones.Add(HumanBodyBones.RightUpperLeg,            TryGet(allBones, "Bip001 R Thigh"));
        humanBones.Add(HumanBodyBones.RightLowerLeg,            TryGet(allBones, "Bip001 R Calf"));
        humanBones.Add(HumanBodyBones.RightFoot,                TryGet(allBones, "Bip001 R Foot"));
        humanBones.Add(HumanBodyBones.RightToes,                TryGet(allBones, "Bip001 R Toe0"));
        humanBones.Add(HumanBodyBones.LeftUpperLeg,             TryGet(allBones, "Bip001 L Thigh"));
        humanBones.Add(HumanBodyBones.LeftLowerLeg,             TryGet(allBones, "Bip001 L Calf"));
        humanBones.Add(HumanBodyBones.LeftFoot,                 TryGet(allBones, "Bip001 L Foot"));
        humanBones.Add(HumanBodyBones.LeftToes,                 TryGet(allBones, "Bip001 L Toe0"));
        humanBones.Add(HumanBodyBones.Chest,                    TryGet(allBones, "Bip001 Spine1"));
        humanBones.Add(HumanBodyBones.Neck,                     TryGet(allBones, "Bip001 Neck"));
        humanBones.Add(HumanBodyBones.Head,                     TryGet(allBones, "Bip001 Head"));
        humanBones.Add(HumanBodyBones.RightShoulder,            TryGet(allBones, "Bip001 R Clavicle"));
        humanBones.Add(HumanBodyBones.RightUpperArm,            TryGet(allBones, "Bip001 R UpperArm"));
        humanBones.Add(HumanBodyBones.RightLowerArm,            TryGet(allBones, "Bip001 R Forearm"));
        humanBones.Add(HumanBodyBones.RightHand,                TryGet(allBones, "Bip001 R Hand"));
        humanBones.Add(HumanBodyBones.RightIndexProximal,       TryGet(allBones, "Bip001 R Finger1"));
        humanBones.Add(HumanBodyBones.RightIndexIntermediate,   TryGet(allBones, "Bip001 R Finger11"));
        humanBones.Add(HumanBodyBones.RightMiddleProximal,      TryGet(allBones, "Bip001 R Finger2"));
        humanBones.Add(HumanBodyBones.RightMiddleIntermediate,  TryGet(allBones, "Bip001 R Finger21"));
        humanBones.Add(HumanBodyBones.RightRingProximal,        TryGet(allBones, "Bip001 R Finger3"));
        humanBones.Add(HumanBodyBones.RightRingIntermediate,    TryGet(allBones, "Bip001 R Finger31"));
        humanBones.Add(HumanBodyBones.RightLittleProximal,      TryGet(allBones, "Bip001 R Finger4"));
        humanBones.Add(HumanBodyBones.RightLittleIntermediate,  TryGet(allBones, "Bip001 R Finger41"));
        humanBones.Add(HumanBodyBones.RightThumbProximal,       TryGet(allBones, "Bip001 R Finger0"));
        humanBones.Add(HumanBodyBones.RightThumbIntermediate,   TryGet(allBones, "Bip001 R Finger01"));
        humanBones.Add(HumanBodyBones.LeftShoulder,             TryGet(allBones, "Bip001 L Clavicle"));
        humanBones.Add(HumanBodyBones.LeftUpperArm,             TryGet(allBones, "Bip001 L UpperArm"));
        humanBones.Add(HumanBodyBones.LeftLowerArm,             TryGet(allBones, "Bip001 L Forearm"));
        humanBones.Add(HumanBodyBones.LeftHand,                 TryGet(allBones, "Bip001 L Hand"));
        humanBones.Add(HumanBodyBones.LeftIndexProximal,        TryGet(allBones, "Bip001 L Finger1"));
        humanBones.Add(HumanBodyBones.LeftIndexIntermediate,    TryGet(allBones, "Bip001 L Finger11"));
        humanBones.Add(HumanBodyBones.LeftMiddleProximal,       TryGet(allBones, "Bip001 L Finger2"));
        humanBones.Add(HumanBodyBones.LeftMiddleIntermediate,   TryGet(allBones, "Bip001 L Finger21"));
        humanBones.Add(HumanBodyBones.LeftRingProximal,         TryGet(allBones, "Bip001 L Finger3"));
        humanBones.Add(HumanBodyBones.LeftRingIntermediate,     TryGet(allBones, "Bip001 L Finger31"));
        humanBones.Add(HumanBodyBones.LeftLittleProximal,       TryGet(allBones, "Bip001 L Finger4"));
        humanBones.Add(HumanBodyBones.LeftLittleIntermediate,   TryGet(allBones, "Bip001 L Finger41"));
        humanBones.Add(HumanBodyBones.LeftThumbProximal,        TryGet(allBones, "Bip001 L Finger0"));
        humanBones.Add(HumanBodyBones.LeftThumbIntermediate,    TryGet(allBones, "Bip001 L Finger01"));
        foreach (var bone in humanBones)
        {
            if (bone.Value != null)
            {
                var tags = new List<SerializableBone.BoneTags>() { SerializableBone.BoneTags.Humanoid };
                if (bone.Value.name.Contains(" L "))
                {
                    tags.Add(SerializableBone.BoneTags.Left);
                }
                else if (bone.Value.name.Contains(" R "))
                {
                    tags.Add(SerializableBone.BoneTags.Right);
                }
                if ((bone.Key >= HumanBodyBones.LeftThumbProximal && bone.Key <= HumanBodyBones.LeftLittleDistal)
                    || (bone.Key >= HumanBodyBones.RightThumbProximal && bone.Key <= HumanBodyBones.RightLittleDistal))
                {
                    tags.Add(SerializableBone.BoneTags.Finger);
                }
                UIHandle.CreateAsChild<UIHandleBone>(bone.Value).Init(this, tags).WithLineRenderer(); ;
            }
        }
        foreach (var bone in allBones.Values.Except(humanBones.Values))
        {
            var tags = new List<SerializableBone.BoneTags>();
            UIHandle.CreateAsChild<UIHandleBone>(bone).Init(this, tags).WithLineRenderer(); ;
        }

        _humanBones = humanBones;
    }

    public void CreateMorphPanel()
    {
        if (MorphPanel != null)
        {
            Destroy(MorphPanel.gameObject);
        }

        var morphRenderers = new Dictionary<SkinnedMeshRenderer, List<MorphHelper>>();
        foreach (var rend in gameObject.GetComponentsInChildren<SkinnedMeshRenderer>())
        {
            if (rend.sharedMesh.blendShapeCount > 0)
            {
                morphRenderers.Add(rend, new List<MorphHelper>());
            }
        }

        if (morphRenderers.Count == 0) return;

        MorphPanel = MorphPanel.Create(morphRenderers.Select(mr => mr.Key.sharedMesh.name).ToList());
        MorphPanel.name = $"{Data.Name} Morphs";

        foreach (var rend in morphRenderers)
        {
            var mesh = rend.Key.sharedMesh;
            for (int i = 0; i < mesh.blendShapeCount; i++)
            {
                MorphPanel.CreateMorph(mesh.name, this, rend.Key, i, mesh.GetBlendShapeName(i));
            }
        }

        Morphs = morphRenderers;
        EversoulInterface.Instance.SelectedObjectToolbar.AttachToThis(MorphPanel.GetComponent<UIElementDragger>());
    }


    public T TryGet<T>(Dictionary<string, T> dict, string key)
    {
        if (dict.ContainsKey(key))
        {
            return dict[key];
        }
        return default(T);
    }

    public override Vector3 GetCenter()
    {
        if (_humanBones.ContainsKey(HumanBodyBones.Hips))
        {
            return _humanBones[HumanBodyBones.Hips].position;
        }
        return base.GetCenter();
    }

    protected override void OnDestroy()
    {
        if (_applicationQuitting) return;
        if (MorphPanel != null)
        {
            Destroy(MorphPanel.gameObject);
        }
        base.OnDestroy();
    }

    public Animator GetAnimator()
    {
        return _animator;
    }
}