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

namespace RMDEC
{
    public class MVProject
    {

        public MVProject() { }

        //Private internal variables
        private byte[] encryptionKey = new byte[0x10];

        private bool isKeySet = false;
        private bool encryptedImages = false;
        private bool encryptedAudio = false;

        private string gameTitle;
        private string filePath;
        private string systemJsonFile;
        private dynamic jsonData;

        //Public readable variables
        public Boolean IsEncrypted
        {
            get
            {
                if (EncryptedAudio == true || EncryptedImages == true)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }

        public Byte[] EncryptionKey
        {
            get
            {
                if(isKeySet)
                {
                    return encryptionKey;
                }
                else
                {
                    genEncryptionKey();
                    return encryptionKey;
                }
                
            }
        }
        public String GameTitle
        {
            get
            {
                return gameTitle;
            }
        }
        public Boolean EncryptedImages
        {
            get
            {
                return encryptedImages;
            }
            set
            {
                jsonData.hasEncryptedImages = value;
                File.WriteAllText(systemJsonFile, jsonData.ToString(Formatting.None));
                encryptedImages = value;
            }
        }

        public Boolean EncryptedAudio
        {
            get
            {
                return encryptedAudio;
            }
            set
            {
                jsonData.hasEncryptedAudio = value;
                File.WriteAllText(systemJsonFile, jsonData.ToString(Formatting.None));
                encryptedAudio = value;
            }
        }

        public String FilePath
        {
            get
            {
                return filePath;
            }
        }

        //Private functions
        private static byte[] hexStr2Bytes(String HexStr)
        {
            if (HexStr.Length % 2 != 0)
            {
                throw new InvalidDataException(HexStr + " Is not divisible by 2!");
            }

            List<byte> byteList = new List<byte>();

            for (int i = 0; i < HexStr.Length; i += 2)
            {
                string curHex = HexStr.Substring(i, 2);
                byte hexByte = Byte.Parse(curHex, NumberStyles.HexNumber);

                byteList.Add(hexByte);
            }

            byte[] resultingByteArray = byteList.ToArray();
            return resultingByteArray;

        }



        private void genEncryptionKey()
        {
            byte[] keyData = new byte[0x10];
            Random rng = new Random((int)DateTime.Now.Ticks);
            rng.NextBytes(keyData);
            encryptionKey = keyData;
            jsonData.encryptionKey = BitConverter.ToString(keyData, 0x00, keyData.Length).Replace("-", "");
            File.WriteAllText(systemJsonFile, jsonData.ToString(Formatting.None));
            isKeySet = true;
        }

        //Public functions
        public static MVProject ParseSystemJson(string path)
        {
            if (File.Exists(path))
            {
                string jsonStr = File.ReadAllText(path, Encoding.UTF8);
                dynamic systemJson = JObject.Parse(jsonStr);

                //Check if valid system.json
                MVProject mvp = new MVProject();

                if (systemJson.gameTitle != null)
                {
                    mvp.gameTitle = systemJson.gameTitle;
                }
                else
                {
                    throw new InvalidDataException("Not a valid system.json!");
                }

                if (systemJson.hasEncryptedAudio != null)
                {
                    mvp.encryptedAudio = systemJson.hasEncryptedAudio;
                }

                if (systemJson.hasEncryptedImages != null)
                {
                    mvp.encryptedImages = systemJson.hasEncryptedImages;
                }

                if (systemJson.encryptionKey != null)
                {
                    string encKey = systemJson.encryptionKey;
                    mvp.encryptionKey = hexStr2Bytes(encKey);
                    mvp.isKeySet = true;
                }

                mvp.filePath = Path.GetDirectoryName(Path.GetDirectoryName(path));
                mvp.systemJsonFile = path;
                mvp.jsonData = systemJson;
                return mvp;

            }
            else
            {
                throw new FileNotFoundException(path + " was not found!");
            }

        }

        public void EncryptFile(Stream inStream, Stream outStream)
        {
            outStream.Seek(0x00, SeekOrigin.Begin);
            outStream.SetLength(0x00);

            BinaryWriter boutStream = new BinaryWriter(outStream);
            string magic = "RPGMV";
            byte[] magicBytes = Encoding.UTF8.GetBytes(magic);

            outStream.Write(magicBytes, 0x00, magicBytes.Length);
            boutStream.Write(0x00);
            boutStream.Write(0x103);
            outStream.Write(new byte[0x3], 0x00, 0x3);

            inStream.Seek(0x00, SeekOrigin.Begin);
            byte[] plaintextHeader = new byte[0x10];
            inStream.Read(plaintextHeader, 0x00, 0x10);

            byte[] encryptedHeader = RMProject.Xor(plaintextHeader, EncryptionKey);
            outStream.Write(encryptedHeader, 0x00, encryptedHeader.Length);
            inStream.CopyTo(outStream);
        }
        public void DecryptFile(Stream inStream, Stream outStream)
        {
            inStream.Seek(0x00, SeekOrigin.Begin);

            byte[] magic = new byte[0x05];
            inStream.Read(magic, 0x00,0x05);
            string magicStr = Encoding.UTF8.GetString(magic);

            if(magicStr != "RPGMV")
            {
                throw new InvalidDataException("Not an encrypted file!");
            }

            inStream.Seek(0x10, SeekOrigin.Begin);
            byte[] encryptedHeader = new byte[0x10];
            inStream.Read(encryptedHeader, 0x00, 0x10);

            byte[] plaintextHeader = RMProject.Xor(encryptedHeader, EncryptionKey);

            outStream.Seek(0x00, SeekOrigin.Begin);
            outStream.SetLength(0);

            outStream.Write(plaintextHeader, 0x00, plaintextHeader.Length);
            inStream.CopyTo(outStream);
        }

    }
}
