using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Newtonsoft.Json;

namespace CacheReader
{
    public class CachedItem
    {
        public string texName;
        public string m_strMenuName;
        public string m_strInfo;
        public string m_mpn;
        public string m_strCateName;
        public string m_eColorSetMPN;
        public string m_strMenuNameInColorSet;
        public string m_pcMultiColorID;
        public bool m_boDelOnly;
        public float m_fPriority;
        public bool m_bMan;
    }
    
    public class CacheData
    {
        public int version;
        public string menuHash;
        public string modMenuHash;
        public Dictionary<string, CachedItem> cachedItems;
    }
    
    internal class Program
    {
        private static List<string> mpnList = new List<string>
        {
            "null_mpn",
            "MuneL",
            "MuneS",
            "MuneTare",
            "RegFat",
            "ArmL",
            "Hara",
            "RegMeet",
            "KubiScl",
            "UdeScl",
            "EyeScl",
            "EyeSclX",
            "EyeSclY",
            "EyePosX",
            "EyePosY",
            "EyeClose",
            "EyeBallPosX",
            "EyeBallPosY",
            "EyeBallSclX",
            "EyeBallSclY",
            "EarNone",
            "EarElf",
            "EarRot",
            "EarScl",
            "NosePos",
            "NoseScl",
            "FaceShape",
            "FaceShapeSlim",
            "MayuShapeIn",
            "MayuShapeOut",
            "MayuX",
            "MayuY",
            "MayuRot",
            "HeadX",
            "HeadY",
            "DouPer",
            "sintyou",
            "koshi",
            "kata",
            "west",
            "MuneUpDown",
            "MuneYori",
            "MuneYawaraka",
            "body",
            "moza",
            "head",
            "hairf",
            "hairr",
            "hairt",
            "hairs",
            "hairaho",
            "haircolor",
            "skin",
            "acctatoo",
            "accnail",
            "underhair",
            "hokuro",
            "mayu",
            "lip",
            "eye",
            "eye_hi",
            "eye_hi_r",
            "chikubi",
            "chikubicolor",
            "eyewhite",
            "nose",
            "facegloss",
            "wear",
            "skirt",
            "mizugi",
            "bra",
            "panz",
            "stkg",
            "shoes",
            "headset",
            "glove",
            "acchead",
            "accha",
            "acchana",
            "acckamisub",
            "acckami",
            "accmimi",
            "accnip",
            "acckubi",
            "acckubiwa",
            "accheso",
            "accude",
            "accashi",
            "accsenaka",
            "accshippo",
            "accanl",
            "accvag",
            "megane",
            "accxxx",
            "handitem",
            "acchat",
            "onepiece",
            "set_maidwear",
            "set_mywear",
            "set_underwear",
            "set_body",
            "folder_eye",
            "folder_mayu",
            "folder_underhair",
            "folder_skin",
            "folder_eyewhite",
            "kousoku_upper",
            "kousoku_lower",
            "seieki_naka",
            "seieki_hara",
            "seieki_face",
            "seieki_mune",
            "seieki_hip",
            "seieki_ude",
            "seieki_ashi"
        };

        private static Dictionary<int, string> mpns =
            mpnList.ToDictionary(v => mpnList.IndexOf(v), v => v);
        
        private static Dictionary<int, string> parts = new Dictionary<int, string>
        {
            [-1] = "NONE",
            [0]="EYE_L",
            [1] = "EYE_R",
            [2]="HAIR",
            [3]="EYE_BROW",
            [4]="UNDER_HAIR",
            [5]="SKIN",
            [6]="NIPPLE",
            [7]="HAIR_OUTLINE",
            [8]="SKIN_OUTLINE",
            [9]="EYE_WHITE",
            [10]="MAX"
        };
        
        public static void Main(string[] args)
        {
            if (args.Length < 1)
                return;

            var path = args[0];
            if (!File.Exists(path))
                return;
            
            var data = new CacheData();
            
            using var br = new BinaryReader(File.OpenRead(path));
            try
            {
                data.version = br.ReadInt32();


                var menuFilesHashSize = br.ReadInt32();
                var menuFilesHash = br.ReadBytes(menuFilesHashSize);
                data.menuHash = Convert.ToBase64String(menuFilesHash);

                var modMenuFilesHashSize = br.ReadInt32();
                var modMenuFilesHash = br.ReadBytes(modMenuFilesHashSize);
                data.modMenuHash = Convert.ToBase64String(modMenuFilesHash);
                
                data.cachedItems = new Dictionary<string, CachedItem>();
                while (true)
                {
                    var key = br.ReadNullableString();
                    Console.WriteLine(key);
                    var item = new CachedItem
                    {
                        texName = br.ReadNullableString(),
                        m_strMenuName = br.ReadNullableString(),
                        m_strInfo = br.ReadNullableString(),
                        m_mpn =
                            mpns.TryGetValue(br.ReadInt32(), out var val)
                                ? val
                                : "INVALID",
                        m_strCateName = br.ReadNullableString(),
                        m_eColorSetMPN =
                            mpns.TryGetValue(br.ReadInt32(), out val)
                                ? val
                                : "INVALID",
                        m_strMenuNameInColorSet = br.ReadNullableString(),
                        m_pcMultiColorID =
                            parts.TryGetValue(br.ReadInt32(), out val)
                                ? val
                                : "INVALID",
                        m_boDelOnly = br.ReadBoolean(),
                        m_fPriority = br.ReadSingle(),
                        m_bMan = br.ReadBoolean()
                    };
                    data.cachedItems[key] = item;
                }
            }
            catch (FormatException e)
            {
                Console.WriteLine($"Failed to deserialize cache because {e.Message}");
            }
            catch (EndOfStreamException)
            {
                // End of file, no need to read more
            }
            
            File.WriteAllText("data.json", JsonConvert.SerializeObject(data, Formatting.Indented));
        }
    }
    
    internal static class BinaryExtensions
    {
        public static string ReadNullableString(this BinaryReader br)
        {
            return br.ReadBoolean() ? br.ReadString() : null;
        }

        public static void WriteNullableString(this BinaryWriter bw, string value)
        {
            bw.Write(value != null);
            if (value != null)
                bw.Write(value);
        }
    }
}