using RuntimeGizmos;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine;
using UnityEngine.UI;

public class KF3ModelBuilder : ModelBuilder
{
    public static KF3ModelBuilder Instance => GetInstance<KF3ModelBuilder>();

    private KF3ModelViewerMain Main => KF3ModelViewerMain.Instance;

    public IEnumerator Init(Action<KF3ModelBuilder> callback)
    {
        callback(this);
        yield break;
    }

    public IEnumerator BuildCharacter(string modelName, string animSetName, Action<GameObject> callback = null)
    {
        yield return KF3FriendContainer.CreateAsync(modelName, animSetName, false, (container)=>
        {
            Main.SelectObject(container);
            callback?.Invoke(container.gameObject);
        });
    }

    public IEnumerator BuildCerulean(string modelName, string animSetName, Action<GameObject> callback = null)
    {
        yield return KF3CeruleanContainer.CreateAsync(modelName, animSetName, (container) =>
        {
            Main.SelectObject(container);
            callback?.Invoke(container.gameObject);
        });
    }

    public override IEnumerator SpawnSerializedCoroutine(ObjectContainerSerializable oc, Action<ObjectContainer> callback = null)
    {
        return SpawnSerialized(oc as KF3ObjectContainerSerializable, callback);
    }

    public IEnumerator SpawnSerialized(KF3ObjectContainerSerializable oc, Action<ObjectContainer> callback = null)
    {
        switch (oc)
        {
            case KF3CharacterContainerSerializable cs:
                if (cs.IsCerulean)
                {
                    var celName = Main.Assets.Ceruleans.FirstOrDefault(c => c.FileName == cs.CostumeName);
                    yield return BuildCerulean(celName.FileName, celName.FileName, (go) =>
                    {
                        var cont = go.GetComponent<KF3CeruleanContainer>();
                        cont.PasteContainer(cs, new PoseLoadOptions(true));
                        cont.DeserializeFrames(cs);
                        TimelineController.SetCurrentFrame(cont);
                        callback?.Invoke(cont);
                    });
                }
                else
                {
                    yield return BuildCharacter(cs.CostumeName, cs.AnimationName, (go) =>
                    {
                        var cont = go.GetComponent<KF3FriendContainer>();
                        cont.PasteContainer(cs, new PoseLoadOptions(true));
                        cont.DeserializeFrames(cs);
                        TimelineController.SetCurrentFrame(cont);
                        callback?.Invoke(cont);
                    });
                }
                break;
            case KF3ObjectContainerSerializable cs:
                yield return SpawnFurniture(cs.Filename);
                break;
        }
    }

    public IEnumerator UpdateCharacter(KF3CharacterContainer container, string modelName, string animSetName, Action<ObjectContainer> callback = null)
    {
        if (container.LoadInProgress) yield break;

        string mIdLong = modelName.Split('_', '.')[1];
        string aIdLong = animSetName.Split('_', '.')[1];

        container.ModelId = int.Parse(mIdLong);
        container.CostumeName = modelName;
        container.AnimationName = animSetName;

        var data = container.Serialize() as KF3CharacterContainerSerializable;
        Destroy(container);
        yield return SpawnSerialized(data, callback);
    }

    public void SpawnReference(Texture2D texture, System.Action<GameObject> callback = null)
    {
        //GameObject go = new GameObject(texture.name);
        //go.AddComponent<UIHandleReference>().Init(go);
        //GameObject go1 = GameObject.CreatePrimitive(PrimitiveType.Plane);
        //go1.transform.SetParent(go.transform);
        //go1.Get<Renderer>().material.shader = Shader.Find("Unlit/Transparent Cutout");
        //go1.Get<Renderer>().material.mainTexture = texture;
        //go1.transform.localScale = new Vector3((float)texture.width / 5000, 1, (float)texture.height / 5000);

        //callback?.Invoke(go);
    }

    public IEnumerator SpawnAsset(string text, System.Action<GameObject> callback)
    {
        KF3AssetBundle assetBundle = Main.Assets.Find(text);

        yield return Main.Assets.LoadAsset(assetBundle, (ab) =>
        {
            var background = Instantiate(ab.LoadAsset(ab.GetAllAssetNames()[0])) as GameObject;
            ab.Unload(false);
            callback?.Invoke(background);
        });
    }

    public IEnumerator SpawnFurniture(string bundleName, System.Action<GameObject> callback = null)
    {
        KF3AssetBundle assetBundle = Main.Assets.GetFurnitureBundle(bundleName);
        if(assetBundle != null)
        {
            yield return Main.Assets.LoadAsset(assetBundle, (ab) =>
            {
                KF3StaticContainer containter = KF3StaticContainer.Create(ab);
                callback?.Invoke(containter.gameObject);
            });
        }
        else
        {
            assetBundle = Main.Assets.GetMiscBundle(bundleName);
            if (assetBundle == null) assetBundle = assetBundle = Main.Assets.GetMiracleBundle(bundleName);
            if (assetBundle != null)
            {
                yield return Spawn(assetBundle);
            }
        }
    }

    public IEnumerator Spawn(KF3AssetBundle assetBundle, System.Action<GameObject> callback = null)
    {
        yield return Main.Assets.LoadAsset(assetBundle, (ab) =>
        {
            GameObject go = Instantiate(ab.LoadAsset(ab.GetAllAssetNames()[0]) as GameObject);
            go.layer = 0;

            KF3StaticContainer containter = go.AddComponent<KF3StaticContainer>();
            //containter.oName = assetBundle.DisplayName;
            UIHandleProp.CreateAsChild(containter, go.transform);

            //containter.PauseAll();
            //containter.BuildHierarchy();

            ab.Unload(false);
            callback?.Invoke(go);
        });
    }

    /// <summary> Clean Up, delete all model remains </summary>
    public void DeleteAnimation(KF3CharacterContainer container)
    {
        List<GameObject> destroyQueue = new List<GameObject>()
        {
            container.BodyAnimation.gameObject
        };

        List<object> nullable = new List<object>()
        {
            container.BodySimpleAnimation
        };

        if(container as KF3FriendContainer != null)
        {
            var container1 = (KF3FriendContainer)container;
            destroyQueue.Add(container1.EarsAnimation.gameObject);
            destroyQueue.Add(container1.TailAnimation.gameObject);
            nullable.Add(container1.EarsSimpleAnimation);
            nullable.Add(container1.TailSimpleAnimation);
        }

        for (int i = destroyQueue.Count - 1; i >= 0; i--)
        {
            Destroy(destroyQueue[i]);
        }

        for (int i = nullable.Count - 1; i >= 0; i--)
        {
            nullable[i] = null;
        }

        Resources.UnloadUnusedAssets();
        System.GC.Collect();
    }
}
