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

public class GltfCacher
{
    GltfImport gltfImport;
    public GltfCacher()
    {

    }

    public async Task<bool> DownloadAndCache(string gltfUrl,
        NewDownloadProvider downloadProvider = null,
        IDeferAgent deferAgent = null,
        IMaterialGenerator materialGenerator = null,
        ICodeLogger logger = null,
        CancellationToken token = default, UnityAction callback = null)
    {
        string CacheFile = GetCacheFile(gltfUrl);
        if (File.Exists(CacheFile))
        {
            callback?.Invoke();
            return true;
            
        }
        else
        {
            if (downloadProvider == null) downloadProvider = new NewDownloadProvider();


            gltfImport = new GltfImport(downloadProvider, deferAgent, materialGenerator, logger);

            var success = false;
            success = await gltfImport.Load(gltfUrl, null, token);

            if (success)
            {
                if (!CacheFile.Contains(".gltf"))
                {
                    SaveToCache(CacheFile, downloadProvider.glbData);
                    downloadProvider.glbData = null;
                }
                else
                {
                    SaveGltfToCache(CacheFile, downloadProvider);
                }
            }
            callback?.Invoke();
            return success;
        }
    }
    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 SaveToCache(string CacheFile, byte[] data)
    {
        if (File.Exists(CacheFile))
        {
            File.Delete(CacheFile);
        }
        File.WriteAllBytes(CacheFile, data);
    }


    public string GetCacheFile(string url)
    {
        string CachePath = Application.persistentDataPath;

        CachePath = Path.Combine(CachePath, "AnchorCaches");
        if (!Directory.Exists(CachePath))
        {
            Directory.CreateDirectory(CachePath);
        }

        string[] token = url.Split(Defines.hostDomain);
        string[] folderNames;
        if (token.Length != 2)
        {
            Debug.Log("Something wrong with the storage!!!");
            return CachePath;
        }
        else
        {
            folderNames = token[1].Split("/");
            for (int i = 0; i < folderNames.Length - 1; i++)
            {
                CachePath = Path.Combine(CachePath, folderNames[i]);
                if (!Directory.Exists(CachePath))
                {
                    Directory.CreateDirectory(CachePath);
                }
            }
        }
        string fileName = Path.GetFileName(url);
        string CacheFile = Path.Combine(CachePath, fileName);
        return CacheFile;
    }
}
}
