using System.Linq;
using UnityEngine;
using System.Collections;
using Newtonsoft.Json.Linq;
using UnityEngine.AddressableAssets;
using UnityEngine.AddressableAssets.ResourceLocators;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceProviders;
using System.Collections.Generic;
using UnityEngine.ResourceManagement.ResourceLocations;
using System.IO;
using UnityEngine.UI;

public class AssetLibrary : MonoBehaviour
{
    protected static AssetLibrary _mainInstance;
    public ResourceLocationMap AddressableResourceMap;
    public List<Shader> Shaders;
    public string LocalFilesPath => ModelViewerSettings.Get("AssetsPath");
    private List<string> _addressableKeys;

    public Dictionary<string, AssetBundle> LoadedAssets = new Dictionary<string, AssetBundle>();

    protected virtual void Awake()
    {
        _mainInstance = this;
    }

    public static T GetInstance<T>() where T : AssetLibrary
    {
        return _mainInstance as T;
    }

    public List<string> GetAddressableKeys()
    {
        if (_addressableKeys == null)
        {
            _addressableKeys = AddressableResourceMap.Keys.Select(k=>k.ToString()).ToList();
        }
        return _addressableKeys.ToList();
    }

    /// <summary>
    /// Load catalog.json from games that use addressables
    /// </summary>
    /// <param name="catalogPath"></param>
    /// <returns></returns>
    public IEnumerator LoadAddressableCatalog(string catalogPath)
    {
        var opHandle = Addressables.LoadContentCatalogAsync(catalogPath);
        yield return opHandle;

        if (opHandle.Status == AsyncOperationStatus.Succeeded)
        {
            Debug.Log(opHandle.Result.GetType());
            AddressableResourceMap = opHandle.Result as ResourceLocationMap;
        }
        else
        {
            throw opHandle.OperationException;
        }
    }

    public string GetResourcePath(string resourceName, out List<string> dependencies)
    {
        dependencies = new List<string>();
        var locator = AddressableResourceMap.Locations[resourceName][0]; // AddressableResourceMap.Locations[resourceName][0].Dependencies[0].Data as AssetBundleRequestOptions;
        if (locator.HasDependencies)
        {
            GatherDependencies(locator, dependencies);
            var data = locator.Dependencies[0].Data as AssetBundleRequestOptions;
            var filePath = data.BundleName + "/" + data.Hash + "/" + "__data";
            dependencies.Remove(filePath);
            return filePath;
        }
        else
        {
            var data = locator.Data as AssetBundleRequestOptions;
            var filePath = data.BundleName + "/" + data.Hash + "/" + "__data";
            return filePath;
        }
    }

    private void GatherDependencies(IResourceLocation resource, List<string> dependencies)
    {
        if (!resource.HasDependencies)
        {
            var data = resource.Data as AssetBundleRequestOptions;
            var filePath = data.BundleName + "/" + data.Hash + "/" + "__data";
            if (!dependencies.Contains(filePath))
            {
                dependencies.Add(filePath);
            }
        }
        else
        {
            foreach (var dep in resource.Dependencies)
            {
                GatherDependencies(dep, dependencies);
            }
        }
    }

    public IEnumerator LoadAssets(List<string> paths)
    {
        var progressBar = ModelViewerInterface.GetInstance().ProgressBar;

        for (int i = paths.Count - 1; i >= 0; i--)
        {
            if (!File.Exists(paths[i]))
            {
                paths.RemoveAt(i);
            }
        }

        List<AssetBundleCreateRequest> routines = new List<AssetBundleCreateRequest>();
        foreach(var path in paths)
        {
            routines.Add(AssetBundle.LoadFromFileAsync(path));
        }

        while (routines.Any(r => !r.isDone))
        {
            int finished = routines.Count(r => r.isDone);
            progressBar?.SetProgress($"Reading files ({finished}/{routines.Count})", (float)finished / routines.Count);
            yield return 0;
        }

        for (int i = 0; i < routines.Count; i++)
        {
            LoadedAssets.Add(paths[i], routines[i].assetBundle);
        }

        progressBar?.SetProgress("", 1);
    }

    public static AssetBundle LoadFromFile(string path)
    {
        var bundle = AssetBundle.LoadFromFile(path);
        _mainInstance.LoadedAssets.Add(path, bundle);
        return bundle;
    }

    public void UnloadAssets(List<string> paths)
    {
        for (int i = 0; i < paths.Count; i++)
        {
            UnloadAsset(paths[i]);
        }
    }

    public void UnloadAsset(string path)
    {
        if (LoadedAssets.ContainsKey(path))
        {
            LoadedAssets[path].Unload(false);
            LoadedAssets.Remove(path);
        }
    }
}