using GLTFast;
using GLTFast.Loading;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using UnityEngine;
namespace XenseAR {


public class GltfAnchor : BaseAnchor
{
    bool bNeedEnable = false;
    GltfLoader gltf;

    public CancellationTokenSource cts;
    private bool isDestroying = false;

    [SerializeField] public string selfAnimation;

    IDeferAgent deferAgent;
    protected virtual void Awake()
    {
        //ILoadingTargetAlgorithm algorithm = new DistanceLoadAlgorithm();
        
        deferAgent = GetComponentInParent<StableFrameDeferAgent>();

    }
    protected override void Start()
    {
        cts = new CancellationTokenSource();
        base.Start();
        GltfLoader.OnFinishLoad += OnGltfAgentFinish;
    }
    protected override void OnDestroy()
    {
        isDestroying = true;
        StopAllCoroutines();
        base.OnDestroy();
        cts?.Cancel();
        GltfLoader.OnFinishLoad -= OnGltfAgentFinish;
        gltf.Dispose();
    }
    private void OnGltfAgentFinish()
    {
        StatusUpdate();
    }

    public override void Load(AnchoringTarget target)
    {
        this.target = target;
        gltf = gameObject.AddComponent<GltfLoader>();
        selfAnimation = target.animation;
        base.Load(target);
        algorithm = new DistanceLoadAlgorithm(target);
    }

    public async void Preview(AnchoringTarget target)
    {
        if (gameObject.GetComponent<GltfLoader>() == null)
            gltf = gameObject.AddComponent<GltfLoader>();
        selfAnimation = target.animation;

        base.Load(target);
        await gltf.Load(target.media.url);
        gltf.Url = target.media.url;
        PlaySelfAnimation(true);
    }

    public override void UnLoad()
    {
        base.UnLoad();
    }

    public override AnchoringMediaType GetType()
    {
        return AnchoringMediaType.Gltf;
    }

    protected async override void StatusUpdate()
    {
        if (string.IsNullOrEmpty(target.id)) return;
        //if (State.Instance.GltfLoaderAgent>1) return;
        bool needEnable = algorithm.IsNeedLoad(target, transform);
        if (needEnable)
        {
            HasDispose = false;
            if (HasLoad)
            {
                base.Show();
                return;
            }
          

            HasLoad = true;
            // GameObject loadingPlaceHolder = CreateLoadingPlaceHolder();

            gltf.Url = target.media.url;

            //string CacheFile = GetCacheFile(gltf.Url);

            await gltf.LoadWithCancelationToken(target.media.url, null, deferAgent, null, null, cts.Token);

            PlaySelfAnimation(true);
        }
        else
        {
            UnLoad();
        }

        if (isDestroying) return;
        bool needDispose = algorithm.IsNeedDispose(target, transform);
        if (needDispose)
            Dispose();
        base.StatusUpdate();

    }

    private void PlaySelfAnimation(bool isPlay)
    {
        if (isDestroying) return;
        if (string.IsNullOrEmpty(selfAnimation)) return;
        Animation animation = gameObject.GetComponentInChildren<Animation>();
        if (animation == null) return;
        if (isPlay)
        {
            animation.clip = animation.GetClip(selfAnimation);
            //Play selected Animation
            if (animation.GetClip(selfAnimation))
            {
                animation.Play(selfAnimation, PlayMode.StopAll);
            }
        }
        else
        {
            //Stop Animation
            if (animation.isPlaying)
            {
                animation.Stop();
            }
        }
    }

    private void SaveGltfToCache(string CacheFile, NewDownloadProvider downloadProvider)
    {
        string DirectoryName = Path.GetDirectoryName(CacheFile);
        SaveAndClearGltfCache(DirectoryName, downloadProvider);

    }

    private string SaveAndClearGltfCache(string DirectoryFolder, NewDownloadProvider downloadProvider)
    {
        string archivePath = DirectoryFolder;

        if (!Directory.Exists(archivePath))
        {
            Directory.CreateDirectory(archivePath);
        }

        Dictionary<string, byte[]> Datas = downloadProvider.Datas;
        Dictionary<string, string> Texts = downloadProvider.Texts;

        foreach (var text in Texts)
        {
            if (text.Key.Contains(".gltf"))
            {
                string gltfPath = Path.Combine(archivePath, text.Key);

                JObject root = JObject.Parse(text.Value);

                JArray images = (JArray)root["images"];
                JArray buffers = (JArray)root["buffers"];

                string textureFilePath = "";
                string binFilePath = "";

                if (images != null)
                {
                    foreach (var image in images)
                    {
                        string uri = image.Value<string>("uri");

                        if (!string.IsNullOrEmpty(uri))
                        {
                            //if url is url, save path as file name
                            if (uri.StartsWith("http"))
                            {
                                string fileName = Path.GetFileName(uri.Split("?")[0]);
                                textureFilePath = fileName;
                                image["uri"] = fileName;
                            }
                            else
                            {
                                textureFilePath = uri;
                            }
                        }

                        string texturePath = Path.Combine(archivePath, textureFilePath);

                        //create directory chain if not exist
                        string directoryPath = Path.GetDirectoryName(texturePath);
                        if (!string.IsNullOrEmpty(directoryPath))
                        {
                            Directory.CreateDirectory(directoryPath);
                        }

                        //write byte file
                        if (Datas.ContainsKey(Path.GetFileName(texturePath)))
                        {
                            File.WriteAllBytes(texturePath, Datas[Path.GetFileName(texturePath)]);
                        }
                    }
                }

                if (buffers != null)
                {
                    foreach (var buffer in buffers)
                    {
                        string uri = buffer.Value<string>("uri");
                        if (!string.IsNullOrEmpty(uri))
                        {
                            if (uri.StartsWith("http"))
                            {
                                string fileName = Path.GetFileName(uri.Split("?")[0]);
                                binFilePath = fileName;
                                buffer["uri"] = fileName;
                            }
                            else
                            {
                                binFilePath = uri;
                            }
                        }

                        string binPath = Path.Combine(archivePath, binFilePath);

                        //create directory chain if not exist
                        string directoryPath = Path.GetDirectoryName(binPath);
                        if (string.IsNullOrEmpty(directoryPath))
                        {
                            Directory.CreateDirectory(directoryPath);
                        }

                        //write byte file
                        if (Datas.ContainsKey(Path.GetFileName(binPath)))
                        {
                            File.WriteAllBytes(binPath, Datas[Path.GetFileName(binPath)]);
                        }
                    }
                }

                string gltfData = root.ToString();

                File.WriteAllText(gltfPath, gltfData);
            }
        }
        downloadProvider.Datas.Clear();
        downloadProvider.Texts.Clear();
        return archivePath;
    }

    protected void DestroyLoadingPlaceHolder(GameObject loadingPlaceHolder)
    {
        GameObject.Destroy(loadingPlaceHolder);
    }

    protected GameObject CreateLoadingPlaceHolder()
    {
        GameObject resource = Resources.Load<GameObject>(Defines.PrefabResourceURL.LoadingPlaceHolderPrefab);
        GameObject mgameObject = Instantiate<GameObject>(resource, transform);
        mgameObject.transform.localPosition = new Vector3(0, 0, 0);
        mgameObject.transform.localRotation = Quaternion.identity;
        mgameObject.transform.localScale = new Vector3(1 / transform.localScale.x, 1 / transform.localScale.y, 1 / transform.localScale.z);
        return mgameObject;
    }

    public override void Dispose()
    {
        if (HasDispose) return;
        gltf.Dispose();
        Animation animation = GetComponent<Animation>();
        if (animation != null)
            Destroy(animation);
        foreach (Transform child in transform)
        {
            Destroy(child.gameObject);
        }
        base.Dispose();
    }

}
}
