using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Gallery.Util.Model;
using Newtonsoft.Json;
using Xamarin.Essentials;
using Xamarin.Forms;

namespace Gallery.Util
{
    public static class Store
    {
        public static readonly string PersonalFolder = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
        public static readonly string CacheFolder = FileSystem.CacheDirectory;

        private const string favoriteFile = "favorites.json";
        private const string imageFolder = "img-original";
        private const string previewFolder = "img-preview";

        public static FavoriteList FavoriteList => GetFavoriteList();
        public static string FavoriteListPath => Path.Combine(PersonalFolder, favoriteFile);
        private static FavoriteList favoriteList;

        public static FavoriteList GetFavoriteList(bool force = false)
        {
            if (force || favoriteList == null)
            {
                var favorites = LoadFavoriteList();
                if (favorites != null)
                {
                    favoriteList = favorites;
                }
                else
                {
                    favoriteList = new FavoriteList();
                }
            }
            return favoriteList;
        }

        private static FavoriteList LoadFavoriteList(string file = null)
        {
            if (file == null)
            {
                file = FavoriteListPath;
            }
            return LoadObject<FavoriteList>(file);
        }

        public static void SaveFavoriteList()
        {
            var file = FavoriteListPath;
            WriteObject(file, FavoriteList);
        }

        private static T LoadObject<T>(string file)
        {
            string content = null;
            if (File.Exists(file))
            {
                try
                {
                    content = File.ReadAllText(file);
                }
                catch (Exception ex)
                {
                    Log.Error("file.read", $"failed to read file: {file}, error: {ex.Message}");
                }
            }
            else
            {
                return default;
            }
            try
            {
                return JsonConvert.DeserializeObject<T>(content);
            }
            catch (Exception ex)
            {
                Log.Error("file.deserialize", $"failed to parse JSON object, error: {ex.Message}");
                return default;
            }
        }

        private static void WriteObject(string file, object obj)
        {
            var folder = Path.GetDirectoryName(file);
            if (!Directory.Exists(folder))
            {
                Directory.CreateDirectory(folder);
            }
            string content;
            try
            {
                content = JsonConvert.SerializeObject(obj, Formatting.None);
            }
            catch (Exception ex)
            {
                Log.Error("object.serialize", $"failed to serialize object, error: {ex.Message}");
                return;
            }
            try
            {
                File.WriteAllText(file, content, Encoding.UTF8);
            }
            catch (Exception ex)
            {
                Log.Error("file.write", $"failed to write file: {file}, error: {ex.Message}");
            }
        }

        public static async Task<ImageSource> LoadRawImage(GalleryItem item, bool downloading, bool force = false, Action<(int loc, int size)> action = null)
        {
            return await LoadImageAsync(item.RawUrl, null, PersonalFolder, Path.Combine(imageFolder, item.Source), downloading, force, action);
        }

        public static string GetRawImagePath(GalleryItem item)
        {
            var file = Path.Combine(PersonalFolder, imageFolder, item.Source, Path.GetFileName(item.RawUrl));
            if (File.Exists(file))
            {
                return file;
            }
            return null;
        }

        public static async Task<ImageSource> LoadPreviewImage(GalleryItem item, bool downloading, bool force = false)
        {
            return await LoadImage(item.PreviewUrl, CacheFolder, Path.Combine(previewFolder, item.Source), downloading, force);
        }

        private static async Task<ImageSource> LoadImage(string url, string working, string folder, bool downloading, bool force)
        {
            var file = Path.Combine(working, folder, Path.GetFileName(url));
            ImageSource image;
            if (!force && File.Exists(file))
            {
                image = ImageSource.FromFile(file);
            }
            else
            {
                image = null;
            }
            if (downloading && image == null)
            {
                file = await NetHelper.DownloadImage(url, working, folder);
                if (file != null)
                {
                    return ImageSource.FromFile(file);
                }
            }
            return image;
        }

        private static async Task<ImageSource> LoadImageAsync(string url, string id, string working, string folder, bool downloading, bool force, Action<(int loc, int size)> action)
        {
            var file = Path.Combine(working, folder, Path.GetFileName(url));
            ImageSource image;
            if (!force && File.Exists(file))
            {
                image = ImageSource.FromFile(file);
            }
            else
            {
                image = null;
            }
            if (downloading && image == null)
            {
                file = await NetHelper.DownloadImageAsync(url, id, working, folder, action);
                if (file != null)
                {
                    image = ImageSource.FromFile(file);
                }
            }
            return image;
        }
    }

    public class FavoriteList : List<GalleryItem>
    {
        public bool Changed { get; private set; }

        public FavoriteList() : base() { }
        public FavoriteList(IEnumerable<GalleryItem> collection) : base(collection) { }

        public new void Insert(int index, GalleryItem item)
        {
            base.Insert(index, item);
            Changed = true;
        }
        public new void InsertRange(int index, IEnumerable<GalleryItem> collection)
        {
            base.InsertRange(index, collection);
            Changed = true;
        }
        public new void RemoveAt(int index)
        {
            base.RemoveAt(index);
            Changed = true;
        }

        public FavoriteList Reload()
        {
            Changed = false;
            return this;
        }
    }
}