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

namespace RMDEC
{
    public class MVProject
    {
        public MVProject() { }

        //Private internal variables
        private byte[] encryptionKey = new byte[0x10];
        private bool encryptedImages = false;
        private bool encryptedAudio = false;
        private string gameTitle;
        private string filePath;


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

        public Byte[] EncryptionKey
        {
            get
            {
                return encryptionKey;
            }
        }
        public String GameTitle
        {
            get
            {
                return gameTitle;
            }
        }
        public Boolean EncryptedImages
        {
            get
            {
                return encryptedImages;
            }
        }

        public Boolean EncryptedAudio
        {
            get
            {
                return encryptedAudio;
            }
        }

        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 static byte[] xor(byte[] input, byte[] key)
        {
            long inpLen = input.LongLength;
            byte[] output = new byte[inpLen];

            for(long i = 0; i < input.LongLength; i++)
            {
                output[i] = (byte)(input[i] ^ key[i % key.LongLength]);
            }

            return output;
        }

        //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.filePath = Path.GetDirectoryName(Path.GetDirectoryName(path));

                return mvp;

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

        }

        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 = xor(encryptedHeader, encryptionKey);
            outStream.Write(plaintextHeader, 0x00, plaintextHeader.Length);
            inStream.CopyTo(outStream);
        }

    }
}
