﻿using AsmodeeDigital.Common.Plugin.Network;
using AsmodeeDigital.Common.Plugin.Utils;
using AsmodeeDigital.CrossPromo.Plugin.Domain;
using AsmodeeDigital.CrossPromo.Plugin.Utils;
using System;
using System.Collections;
using System.IO;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
using UnityEngine.UI;

namespace AsmodeeDigital.CrossPromo.Plugin.Manager
{
    public static class CrossPromoCacheManager
    {
        private static float _countImages = 0;
        private static float _countImagesDownloaded = 0;

        public static void SaveGroupProductInCache(GroupProduct groupProduct, bool cacheInResourcesFolder)
        {
            string pathGroupProductJson = string.Empty;

            if (groupProduct.Filters == MoreGamesFilters.None)
            {
                pathGroupProductJson = Path.Combine(Application.persistentDataPath, string.Format("AsmodeeDigital/CrossPromo/Cache/interstitial.json"));
                if (cacheInResourcesFolder)
                    pathGroupProductJson = Path.Combine(Application.dataPath, string.Format("Resources/AsmodeeDigital/CrossPromo/Cache/interstitial.json"));
            }
            else
            {
                pathGroupProductJson = Path.Combine(Application.persistentDataPath, string.Format("AsmodeeDigital/CrossPromo/Cache/GroupProducts/{0}.json", groupProduct.Filters.ToString()));
                if (cacheInResourcesFolder)
                    pathGroupProductJson = Path.Combine(Application.dataPath, string.Format("Resources/AsmodeeDigital/CrossPromo/Cache/GroupProducts/{0}.json", groupProduct.Filters.ToString()));
            }

            Directory.CreateDirectory(Path.GetDirectoryName(pathGroupProductJson));

            _countImages = 0;
            _countImagesDownloaded = 0;

            foreach (Product product in groupProduct.products)
            {
                // icon_url + tile_url + images + awards
                _countImages += 2 + (cacheInResourcesFolder ? product.images.Count + product.awards.Count : 0);
            }

            foreach (Product product in groupProduct.products)
            {
                SaveProductInCache(product, false, cacheInResourcesFolder);
            }

            //--- Save GroupProduct to json
            string jsonGroupProduct = JsonUtility.ToJson(groupProduct);
            File.WriteAllText(pathGroupProductJson, jsonGroupProduct);
            //---
        }

        public static void SaveBannerInCache(Product product, bool downloadBanner, bool cacheInResourcesFolder)
        {
            string pathProductJson = Path.Combine(Application.persistentDataPath, string.Format("AsmodeeDigital/CrossPromo/Cache/banner.json"));
            if (cacheInResourcesFolder)
                pathProductJson = Path.Combine(Application.dataPath, string.Format("Resources/AsmodeeDigital/CrossPromo/Cache/banner.json"));

            SaveProductInCache(product, downloadBanner, pathProductJson, cacheInResourcesFolder);
        }

        public static void SaveProductInCache(Product product, bool downloadBanner, bool cacheInResourcesFolder)
        {
            string pathProductId = Path.Combine(Application.persistentDataPath, string.Format("AsmodeeDigital/CrossPromo/Cache/Product/{0}/", product.id));
            if (cacheInResourcesFolder)
                pathProductId = Path.Combine(Application.dataPath, string.Format("Resources/AsmodeeDigital/CrossPromo/Cache/Product/{0}/", product.id));

            string pathProductJson = Path.Combine(pathProductId, string.Format("product_{0}.json", product.id));

            SaveProductInCache(product, downloadBanner, pathProductJson, cacheInResourcesFolder);
        }

        private static void SaveProductInCache(Product product, bool downloadBanner, string pathProductJson, bool cacheInResourcesFolder)
        {
            string pathProductId = Path.Combine(Application.persistentDataPath, string.Format("AsmodeeDigital/CrossPromo/Cache/Product/{0}/", product.id));
            if (cacheInResourcesFolder)
                pathProductId = Path.Combine(Application.dataPath, string.Format("Resources/AsmodeeDigital/CrossPromo/Cache/Product/{0}/", product.id));

            string pathIconUrl = Path.Combine(pathProductId, String.Format("icon_url{0}", Path.GetExtension(product.icon_url)));
            string pathTileUrl = Path.Combine(pathProductId, String.Format("tile_url_{0}x{1}{2}", product.tile.width, product.tile.height, Path.GetExtension(product.tile.image_url)));
            string pathBannerUrl = Path.Combine(pathProductId, String.Format("banner_url{0}", Path.GetExtension(product.banner_url)));
            string pathImages = Path.Combine(pathProductId, "Images");
            string pathAwards = Path.Combine(pathProductId, "Awards");

            if (!FreshResourceInCache(pathProductJson) || cacheInResourcesFolder)
            {
                //--- Create Product/Id directory
                Directory.CreateDirectory(pathProductId);
                Directory.CreateDirectory(Path.GetDirectoryName(pathProductJson));
                //---

                //--- Save Product to json
                string jsonProduct = JsonUtility.ToJson(product);
                File.WriteAllText(pathProductJson, jsonProduct);
                //---

                if (cacheInResourcesFolder)
                {
                    //--- Create Product/Id/Images directory
                    if (product.images.Count > 0)
                        Directory.CreateDirectory(pathImages);
                    //---

                    //--- Create Product/Id/Awards directory
                    if (product.awards.Count > 0)
                        Directory.CreateDirectory(pathAwards);
                    //---

                    //--- Download icon
                    DownloadImage(product.icon_url, pathIconUrl, product.name + " - Icon");
                    //---

                    //--- Download tile image
                    DownloadImage(product.tile.image_url, pathTileUrl, product.name + " - Tile");
                    //---

                    //--- Download banner image
                    if (downloadBanner)
                    {
                        DownloadImage(product.banner_url, pathBannerUrl, product.name + " - Banner");
                    }
                    //---

                    //---> Download images
                    for (int i = 0; i < product.images.Count; i++)
                    {
                        string pathImage = Path.Combine(pathImages, Path.GetFileName(product.images[i]));
                        DownloadImage(product.images[i], pathImage, String.Format("{0} - Images ({1}/{2})", product.name, i + 1, product.images.Count));
                    }

                    //---> Download awards
                    for (int i = 0; i < product.awards.Count; i++)
                    {
                        Award award = product.awards[i];
                        string pathAward = Path.Combine(pathAwards, Path.GetFileName(award.image_url));

                        DownloadImage(award.image_url, pathAward, String.Format("{0} - Awards ({1}/{2})", product.name, i + 1, product.awards.Count));
                    }
                }
            }
        }

        private static void DownloadImage(string pathWebImage, string pathLocalCache, string text)
        {
            AsmoLogger.Debug("CrossPromoCacheManager", "Downloading image", new Hashtable() {
                {"url", pathWebImage},
                {"local_path", pathLocalCache},
                {"text", text}
            });

#if UNITY_EDITOR
            //---> In editor mode, show the progress bar
            if (!Application.isPlaying)
            {
                EditorUtility.DisplayProgressBar("Downloading Cross Promo cache", text, _countImagesDownloaded / _countImages);
            }
#endif
            SaveTextureToPNG(pathWebImage, pathLocalCache);
            _countImagesDownloaded++;
        }

        /// <summary>
        /// Save a web image to a local path. Freeze the current thread. For editor script purpose
        /// </summary>
        /// <param name="url"></param>
        /// <param name="savePath"></param>
        /// <param name="afterLoading"></param>
        public static void SaveTextureToPNG(string url, string savePath)
        {
            WWW www = new WWW(url);
            while (!www.isDone)
            {
            }

            if (string.IsNullOrEmpty(www.error))
            {
                Texture2D texture = new Texture2D(2, 2, TextureFormat.RGBA32, false, false);
                texture.LoadImage(www.bytes);

                savePath = Path.ChangeExtension(savePath, "png").Replace("\\", "/").Replace(@"\", "/");
                File.WriteAllBytes(savePath, texture.EncodeToPNG());
            }
            else
            {
                AsmoLogger.Error("CrossPromoCacheManager", "Error when downloading file", new Hashtable() { { "url", url }, { "www_error", www.error } });
            }
        }

        #region public Cache loading
        public static IEnumerator LoadMoreGame(RestAPI restAPI, AuthenticationTokens authenticationTokens, int nbColumns, MoreGamesFilters moreGamefilters)
        {
            //---> Cross promo status : disconnected => load offline
            if (authenticationTokens == null)
            {
                LoadMoreGameOffline(restAPI, nbColumns, moreGamefilters);
            }
            //---> Cross promo status : connected => try to load online
            else
            {
                yield return WebChecker.WebRequest(
                //---> Can download
                () => { restAPI.MoreGameRequest(authenticationTokens.access_token, moreGamefilters, nbColumns); },
                //---> Cannot download
                () =>
                {
                    LoadMoreGameOffline(restAPI, nbColumns, moreGamefilters);
                });
            }
        }

        public static IEnumerator LoadInterstitial(RestAPI restAPI, AuthenticationTokens authenticationTokens, int nbColumns)
        {
            //---> Cross promo status : disconnected => load offline
            if (authenticationTokens == null)
            {
                LoadInterstitialOffline(restAPI, nbColumns);
            }
            //---> Cross promo status : connected => try to load online
            else
            {
                yield return WebChecker.WebRequest(
                //---> Can download
                () => { restAPI.InterstitialRequest(authenticationTokens.access_token, nbColumns); },
                //---> Cannot download
                () =>
                {
                    LoadInterstitialOffline(restAPI, nbColumns);
                });
            }
        }

        public static IEnumerator LoadBanner(RestAPI restAPI, AuthenticationTokens authenticationTokens)
        {
            //---> Cross promo status : disconnected => load offline
            if (authenticationTokens == null)
            {
                LoadBannerOffline(restAPI);
            }
            //---> Cross promo status : connected => try to load online
            else
            {
                yield return WebChecker.WebRequest(
                //---> Can download
                () => { restAPI.BannerRequest(authenticationTokens.access_token); },
                //---> Cannot download
                () =>
                {
                    LoadBannerOffline(restAPI);
                });
            }
        }
        #endregion

        #region Offline cache loading
        private static void LoadMoreGameOffline(RestAPI restAPI, int nbColumns, MoreGamesFilters moreGamefilters)
        {
            string pathLocalCacheGroupProducts = Path.Combine(Application.persistentDataPath, string.Format("AsmodeeDigital/CrossPromo/Cache/GroupProducts/{0}.json", moreGamefilters));
            string pathStaticCacheGroupProducts = Path.Combine(Application.dataPath, string.Format("Resources/AsmodeeDigital/CrossPromo/Cache/GroupProducts/{0}.json", moreGamefilters));

            GroupProduct groupProduct = GetFromCache<GroupProduct>(pathLocalCacheGroupProducts, pathStaticCacheGroupProducts);

            restAPI.RaiseReceivingGroupProductEvent(groupProduct);
        }

        private static void LoadInterstitialOffline(RestAPI restAPI, int nbColumns)
        {
            string pathLocalCacheInterstital = Path.Combine(Application.persistentDataPath, string.Format("AsmodeeDigital/CrossPromo/Cache/interstitial.json"));
            string pathStaticCacheInterstitial = Path.Combine(Application.dataPath, string.Format("Resources/AsmodeeDigital/CrossPromo/Cache/interstitial.json"));

            GroupProduct groupProduct = GetFromCache<GroupProduct>(pathLocalCacheInterstital, pathStaticCacheInterstitial);

            restAPI.RaiseReceivingGroupProductEvent(groupProduct);
        }

        private static void LoadBannerOffline(RestAPI restAPI)
        {
            string pathLocalCacheBanner = Path.Combine(Application.persistentDataPath, string.Format("AsmodeeDigital/CrossPromo/Cache/banner.json"));
            string pathStaticCacheBanner = Path.Combine(Application.dataPath, string.Format("Resources/AsmodeeDigital/CrossPromo/Cache/banner.json"));

            Product product = GetFromCache<Product>(pathLocalCacheBanner, pathStaticCacheBanner);

            restAPI.RaiseReceivingProductEvent(product);
        }
        #endregion

        #region Load image
        public static IEnumerator LoadProductTileImage(Product product, RawImage image, Action afterLoading)
        {
            string pathLocalCacheTileUrl = Path.Combine(Application.persistentDataPath, string.Format("AsmodeeDigital/CrossPromo/Cache/Product/{0}/tile_url_{1}x{2}{3}", product.id, product.tile.width, product.tile.height, Path.GetExtension(product.tile.image_url)));
            string pathStaticCacheTileUrl = Path.Combine(Application.dataPath, string.Format("Resources/AsmodeeDigital/CrossPromo/Cache/Product/{0}/tile_url_{1}x{2}{3}", product.id, product.tile.width, product.tile.height, Path.GetExtension(product.tile.image_url)));

            yield return LoadTexture(product.tile.image_url, pathLocalCacheTileUrl, pathStaticCacheTileUrl, image, afterLoading);
        }

        public static IEnumerator LoadProductIcon(Product product, Image image, Action afterLoading)
        {
            string pathLocalCacheIconUrl = Path.Combine(Application.persistentDataPath, string.Format("AsmodeeDigital/CrossPromo/Cache/Product/{0}/icon_url{1}", product.id, Path.GetExtension(product.icon_url)));
            string pathStaticCacheIconUrl = Path.Combine(Application.dataPath, string.Format("Resources/AsmodeeDigital/CrossPromo/Cache/Product/{0}/icon_url{1}", product.id, Path.GetExtension(product.icon_url)));

            yield return LoadTexture(product.icon_url, pathLocalCacheIconUrl, pathStaticCacheIconUrl, image, afterLoading);
        }

        public static IEnumerator LoadProductImage(Product product, string pathImage, Image image, Action afterLoading)
        {
            string pathLocalCacheImage = Path.Combine(Application.persistentDataPath, string.Format("AsmodeeDigital/CrossPromo/Cache/Product/{0}/Images/{1}", product.id, Path.GetFileName(pathImage)));
            string pathStaticCacheImage = Path.Combine(Application.dataPath, string.Format("Resources/AsmodeeDigital/CrossPromo/Cache/Product/{0}/Images/{1}", product.id, Path.GetFileName(pathImage)));

            yield return LoadTexture(pathImage, pathLocalCacheImage, pathStaticCacheImage, image, afterLoading);
        }

        public static IEnumerator LoadProductAward(Product product, string pathImage, Image image, Action afterLoading)
        {
            string pathLocalCacheImage = Path.Combine(Application.persistentDataPath, string.Format("AsmodeeDigital/CrossPromo/Cache/Product/{0}/Awards/{1}", product.id, Path.GetFileName(pathImage)));
            string pathStaticCacheImage = Path.Combine(Application.dataPath, string.Format("Resources/AsmodeeDigital/CrossPromo/Cache/Product/{0}/Awards/{1}", product.id, Path.GetFileName(pathImage)));

            yield return LoadTexture(pathImage, pathLocalCacheImage, pathStaticCacheImage, image, afterLoading);
        }

        public static IEnumerator LoadBannerImage(Product product, RawImage image, Action afterLoading)
        {
            string pathLocalCacheBannerUrl = Path.Combine(Application.persistentDataPath, string.Format("AsmodeeDigital/CrossPromo/Cache/Product/{0}/banner_url{1}", product.id, Path.GetExtension(product.banner_url)));
            string pathStaticCacheBannerUrl = Path.Combine(Application.dataPath, string.Format("Resources/AsmodeeDigital/CrossPromo/Cache/Product/{0}/banner_url{1}", product.id, Path.GetExtension(product.banner_url)));

            yield return LoadTexture(product.banner_url, pathLocalCacheBannerUrl, pathStaticCacheBannerUrl, image, afterLoading);
        }
        #endregion

        private static bool FreshResourceInCache(string pathCache)
        {
            if (!String.IsNullOrEmpty(pathCache) && File.Exists(pathCache))
            {
                DateTime dateTime = File.GetLastWriteTime(pathCache);
                double lastWrite = DateTime.Now.Subtract(dateTime).TotalDays;
                return (lastWrite <= 1);
            }
            else
                return false;
        }

        private static T GetFromCache<T>(string pathLocalCache, string pathStaticCache)
        {
            //---> Get object from local cache if it's fresh enough
            if (FreshResourceInCache(pathLocalCache))
            {
                string json = File.ReadAllText(pathLocalCache);
                return JsonUtility.FromJson<T>(json);
            }
            //---> Get static cache
            else
            {
                pathStaticCache = pathStaticCache.Remove(0, Path.Combine(Application.dataPath, "Resources/").Length);
                pathStaticCache = Path.Combine(Path.GetDirectoryName(pathStaticCache), Path.GetFileNameWithoutExtension(pathStaticCache));

                Debug.Log(pathStaticCache);

                TextAsset jsonAsset = Resources.Load<TextAsset>(pathStaticCache);
                return JsonUtility.FromJson<T>(jsonAsset.text);
            }
        }

        private static IEnumerator LoadTexture(string path, string pathLocalCache, string pathStaticCache, MaskableGraphic image, Action afterLoading)
        {
            //---> Get image from the cache if it's fresh enough
            if (FreshResourceInCache(pathLocalCache))
            {
                yield return LoadAndWriteTexture(pathLocalCache, pathLocalCache, image, afterLoading);
            }
            //---> Get image from the web (if possible, otherwise from the cache)
            else
            {
                string pathCache = File.Exists(pathLocalCache) ? pathLocalCache : pathStaticCache;
                AsmoLogger.Warning("CrossPromoCacheManager", "Asset needs to be refreshed", new Hashtable() { { "path", path } });

                yield return WebChecker.WebRequest(
                        //---> Can download
                        LoadAndWriteTexture(path, pathLocalCache, image, afterLoading),
                        //---> Cannot download
                        LoadAndWriteTexture(pathCache, pathLocalCache, image, afterLoading));
            }
        }

        private static IEnumerator LoadAndWriteTexture(string path, string pathLocalCache, MaskableGraphic image, Action afterLoading)
        {
            yield return TextureLoader.LoadTexture(path, image,
              (byte[] bytes) =>
              {
                  if (afterLoading != null)
                      afterLoading();

                  if (path.StartsWith("http"))
                  {
                      if (!Directory.Exists(Path.GetDirectoryName(pathLocalCache)))
                          Directory.CreateDirectory(Path.GetDirectoryName(pathLocalCache));

                      File.WriteAllBytes(pathLocalCache, bytes);
                  };
              });
        }
    }
}
