using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.U2D;
using UnityEngine.UI;

public class Bundle<T>
{
    Dictionary<string, T> dic;
    public T Get(string name)
    {
        if (dic == null)
            dic = new Dictionary<string, T>();

        if (dic.ContainsKey(name))
            return dic[name];

        return default(T);
    }
    public void Add(string name, T value)
    {
        dic.Add(name, value);
    }
    public void Remove(string name)
    {
        dic.Remove(name);
    }
}

[System.Serializable]
public class AssetBundleManager
{
    static string URL = $"ypdnet.com/Popcity/AssetBundles/";
    static string ID = "unity3d";
    static string PW = "unity3d";

    public string nameBundle = "common";

    //public AssetBundleManager(MonoBehaviour mono, string nameBundle)
    //{
    //    this.nameBundle = nameBundle;
    //    mono.StartCoroutine(DownloadAndCache());
    //}

    public AssetBundle bundle = null;
    public string[] assets;
    /// <summary>
    /// load assetbundle manifest, check hash, load actual bundle with hash parameter to use caching
    /// instantiate gameobject
    /// </summary>
    /// <param name="bundleURL">full url to assetbundle file</param>
    /// <param name="assetName">optional parameter to access specific asset from assetbundle</param>
    /// <returns></returns>
    public IEnumerator DownloadAndCache(string nameBundle, Action<bool> OnEndDownload, Action<float> OnProgress)
    {
        if (bundle != null)
        {
            OnProgress(1);
            if (OnEndDownload != null)
                OnEndDownload(true);

            yield break;
        }

        while (!Caching.ready)
        {
            yield return null;
        }

#if UNITY_EDITOR
        string bundleURL = $"ftp://{ID}:{PW}@{URL}Window/{nameBundle}";
#elif UNITY_ANDROID
        string bundleURL = $"ftp://{ID}:{PW}@{URL}Android/{nameBundle}";
#else
        string bundleURL = $"ftp://{ID}:{PW}@{URL}Window/{nameBundle}";
#endif
        UnityWebRequest www = UnityWebRequest.Get($"{bundleURL}.manifest");

        yield return www.SendWebRequest();

        if (www.isNetworkError == true)
        {
            Debug.LogError("www error: " + www.error);
            www.Dispose();
            www = null;
            yield break;
        }

        Hash128 hashString = default(Hash128);// new Hash128(0, 0, 0, 0);
        if (www.downloadHandler.text.Contains("ManifestFileVersion"))
        {
            var hashRow = www.downloadHandler.text.ToString().Split("\n".ToCharArray())[5];
            hashString = Hash128.Parse(hashRow.Split(':')[1].Trim());

            if (hashString.isValid == true)
            {
                if (Caching.IsVersionCached(bundleURL, hashString) == true)
                {
                    Debug.Log($"{nameBundle} 은 캐시 되어 있는 버전입니다.");
                }
                else
                {
                    Debug.Log($"{nameBundle} 은 새로 받아야 하는 버전입니다.");
                }
            }
            else
            {
                // invalid loaded hash, just try loading latest bundle
                Debug.LogError("Invalid hash:" + hashString);
                OnEndDownload(false);
                yield break;
            }
        }
        else
        {
            Debug.LogError("Manifest doesn't contain string 'ManifestFileVersion': " + bundleURL + ".manifest");
            OnEndDownload(false);
            yield break;
        }

        www = UnityWebRequestAssetBundle.GetAssetBundle(bundleURL, hashString, 0);

        www.SendWebRequest();
        while (!www.isDone)
        {
            //Debug.LogWarning($"{www.downloadedBytes} : {www.downloadProgress}");
            OnProgress(www.downloadProgress);
            yield return null;
        }
        OnProgress(1);

        bundle = ((DownloadHandlerAssetBundle)www.downloadHandler).assetBundle;
        assets = bundle.GetAllAssetNames();

        //www.Dispose();
        //www = null;

        OnEndDownload(true);
    }

    Bundle<AudioClip> bundleAudioClip = new Bundle<AudioClip>();
    public AudioClip GetAudioClip(string name)
    {
        AudioClip audioClip = bundleAudioClip.Get(name);
        if (audioClip == null && bundle != null)
        {
            audioClip = bundle.LoadAsset<AudioClip>(name);
            bundleAudioClip.Add(name, audioClip);
        }

        return audioClip;
    }

    Bundle<SpriteAtlas> bundleSpriteAtlas = new Bundle<SpriteAtlas>();
    public SpriteAtlas GetSpriteAtlas(string name)
    {
        if (bundle == null)
            return null;

        SpriteAtlas atlas = bundleSpriteAtlas.Get(name);
        if (atlas == null)
        {
            atlas = bundle.LoadAsset<SpriteAtlas>(name);
            bundleSpriteAtlas.Add(name, atlas);
        }

        return atlas;
    }

    Bundle<Sprite> bundleTexture = new Bundle<Sprite>();
    public Sprite GetTexture(string name)
    {
        if (bundle == null)
            return null;

        Sprite tex = bundleTexture.Get(name);
        if (tex == null)
        {
            tex = bundle.LoadAsset<Sprite>(name);
            bundleTexture.Add(name, tex);
        }

        return tex;
    }

    Bundle<GameObject> bundlePrefab = new Bundle<GameObject>();
    public GameObject GetPrefab(string name)
    {
        if (bundlePrefab == null || bundle == null)
            return null;

        GameObject prefab = bundlePrefab.Get(name);
        if (prefab == null && bundle.Contains(name))
        {
            prefab = bundle.LoadAsset<GameObject>(name);
            bundlePrefab.Add(name, prefab);
        }

        return prefab;
    }
}