using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using TaikoSoundEditor.Collections;
using TaikoSoundEditor.Commons.IO;
using TaikoSoundEditor.Data;

namespace TaikoSoundEditor.Commons.Utils
{
    public class DatatableIO
    {
        public bool IsEncrypted { get; set; }

        public T DeserializeCollection<T, I>(string path, Type itemType) where T: new()
        {
            if (!itemType.GetInterfaces().Any(_ => _ == typeof(I)))
                throw new ArgumentException($"Type {itemType} does not implement {typeof(I)}");

            var json = IsEncrypted
                ? GZ.DecompressBytes(SSL.DecryptDatatable(File.ReadAllBytes(path)))
                : GZ.DecompressString(path);
            var col = new T();

            var itemsProp = typeof(T).GetProperty("Items");
            var items = itemsProp.GetValue(col);

            var addRange = itemsProp.PropertyType.GetMethod("AddRange");
            addRange.Invoke(items, new object[] { Collections.Collections.FromJson<I>(json, itemType).Items });
            return col;
        }

        public WordList DeserializeWordList(string path)
        {
            return DeserializeCollection<WordList, IWord>(path, DatatableTypes.Word);            
        }


        public T Deserialize<T>(string path)
        {
            if (!IsEncrypted)
            {                
                return Json.Deserialize<T>(GZ.DecompressString(path));
            }
            else
            {                
                return Json.Deserialize<T>(GZ.DecompressBytes(SSL.DecryptDatatable(File.ReadAllBytes(path))));
            }
        }

        public void Serialize<T>(string path, T item, bool indented = false, bool fixBools = false)
        {
            var str = JsonFix(Json.Serialize(item, indented));
            if(fixBools)
            {
                str = str
                    .Replace("\"new\": true,", "\"new\":true,")
                    .Replace("\"new\": false,", "\"new\":false,"); // is this still needed?
            }

            if (IsEncrypted)
                File.WriteAllBytes(path, SSL.EncryptDatatable(GZ.CompressToBytes(str)));
            else
                File.WriteAllBytes(path, GZ.CompressToBytes(str));
        }

        private static string JsonFix(string json)
        {
            var specialChars = "!@#$%^&*()_+=`~[]{}<>\\/'";
            foreach (var c in specialChars)
            {
                json = json.Replace($"\\u00{((int)c):X2}", $"{c}");
            }


            return json
                .Replace("\\u0022", "\\\"")
                .Replace("\r\n      ", "\r\n\t\t")
                .Replace("\r\n      ", "\r\n\t\t")
                .Replace("{\r\n  \"items\": [", "{\"items\":[")
                .Replace("    }", "\t}")
                .Replace("  ]\r\n}", "\t]\r\n}");
        }

    }
}