using SimpleJSON;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace COM3D2.MultipleMaids.Util
{
    internal static class MMData
    {
        private const string CM_EXTENSIONS_DB = "cm3d2_extensions.json";
        private const string UI_NAMES_DB = "ui_names.json";
        private const string BGM_DB = "bgm.json";
        private const string VOICES_DB = "personal_voices.json";

        public static Dictionary<string, string> Dances { get; private set; }
        public static Dictionary<string, string> Dogus { get; private set; }
        public static Dictionary<string, string> Particles { get; private set; }
        public static Dictionary<string, string> Items { get; private set; }
        public static Dictionary<string, string> Backgrounds { get; private set; }
        public static Dictionary<string, IVoice[]> NormalVoices { get; private set; }
        public static Dictionary<string, IVoice[]> HVoices { get; private set; }

        private static readonly Random rand = new Random();

        static MMData()
        {
            Dances = new Dictionary<string, string>();
            Dogus = new Dictionary<string, string>();
            Particles = new Dictionary<string, string>();
            Items = new Dictionary<string, string>();
            Backgrounds = new Dictionary<string, string>();
            NormalVoices = new Dictionary<string, IVoice[]>();
            HVoices = new Dictionary<string, IVoice[]>();
        }

        private static void ParseUINames(string location)
        {
            string uiNames = Path.Combine(location, UI_NAMES_DB);

            if (File.Exists(uiNames))
            {
                JSONNode uiNamesJson = JSON.Parse(File.ReadAllText(uiNames));

                Dogus = ParseObj(uiNamesJson, "dogu");
                Particles = ParseObj(uiNamesJson, "particles");
                Items = ParseObj(uiNamesJson, "items");
                Backgrounds = ParseObj(uiNamesJson, "bg");
            }
        }

        private static void ParseExtensions(string location)
        {
            string extensionsPath = Path.Combine(location, CM_EXTENSIONS_DB);

            if (File.Exists(extensionsPath))
            {
                JSONNode extensionsJson = JSON.Parse(File.ReadAllText(extensionsPath));

                foreach (string key in extensionsJson.Keys)
                {
                    if ((key != "CM3D2_Legacy" || !GameUty.IsEnabledCompatibilityMode) && GameMain.Instance.BgMgr.CreateAssetBundle(key) == null)
                    {
                        continue;
                    }

                    Dogus.Merge(ParseObj(extensionsJson, "dogu"));
                    Items.Merge(ParseObj(extensionsJson, "item"));
                    Backgrounds.Merge(ParseObj(extensionsJson, "bg"));
                }
            }
        }

        private static void ParseBGM(string location)
        {
            string bgmPath = Path.Combine(location, BGM_DB);

            if (File.Exists(bgmPath))
            {
                JSONNode bgmJson = JSON.Parse(File.ReadAllText(bgmPath));
                Backgrounds = bgmJson.Linq.ToDictionary(k => k.Key, k => k.Value.ToString());
            }
        }

        private static void ParseVoices(string location)
        {
            void Parse(Dictionary<string, IVoice[]> target, JSONNode node, string type)
            {
                JSONNode normalVoices = node[type];
                if (!normalVoices)
                {
                    return;
                }

                foreach (string key in normalVoices.Keys)
                {
                    JSONArray obj = normalVoices[key].AsArray;
                    IVoice[] voices = new IVoice[obj.Count];

                    for (int i = 0; i < voices.Length; i++)
                    {
                        JSONNode voiceObj = obj[i];
                        if (voiceObj.IsNumber)
                        {
                            voices[i] = new SingleVoice(voiceObj.AsInt);
                        }
                        else if (voiceObj.IsArray && voiceObj.Count == 2)
                        {
                            voices[i] = new VoiceRange(voiceObj[0].AsInt, voiceObj[1].AsInt);
                        }
                    }

                    target[key] = voices;
                }
            }

            string personalVoicesPath = Path.Combine(location, VOICES_DB);

            if (File.Exists(VOICES_DB))
            {
                JSONNode voicesJson = JSON.Parse(File.ReadAllText(personalVoicesPath));

                Parse(NormalVoices, voicesJson, "normal");
                Parse(HVoices, voicesJson, "h");
            }
        }

        public static void LoadDatabase(string location)
        {
            ParseUINames(location);
            ParseExtensions(location);
            ParseVoices(location);
            ParseBGM(location);
        }

        public static void Merge<TKey, TVal>(this Dictionary<TKey, TVal> self, Dictionary<TKey, TVal> with)
        {
            if (with.Count == 0)
            {
                return;
            }

            foreach (KeyValuePair<TKey, TVal> keyVal in with)
            {
                self[keyVal.Key] = keyVal.Value;
            }
        }

        public static Dictionary<string, string> ParseObj(JSONNode obj, string key)
        {
            return !obj[key] ? new Dictionary<string, string>() : obj[key].Linq.ToDictionary(k => k.Key, k => k.Value.ToString());
        }

        public static int PickRandomVoice(Dictionary<string, IVoice[]> voiceStorage, string personality)
        {
            if (!voiceStorage.TryGetValue(personality, out IVoice[] voices))
            {
                return -1;
            }

            return voices[rand.Next(voices.Length)].PickRandomVoice();
        }
    }
}
