using System.IO.Compression;

namespace RHLauncher
{
    internal class MIPDecoder
    {
        private static readonly byte[] codeMip = new byte[] {
        0x30, 0x22, 0x41, 0xa8, 0x5b, 0xa6, 0x6a, 0x49, 0xbf, 0x53, 0x35, 0xe5, 0x9e, 14, 0xec, 0xb8,
        0x5e, 0x15, 0x1f, 0xc1, 0x4f, 0xec, 0x77, 0xe8, 0xb7, 0x4e, 0x87, 230, 0xf5, 60, 0xb3, 0x43,
        0xcc, 0x53, 0x36, 0xac, 90, 0x77, 0xb8, 0xdd, 0x30, 0x74, 140, 0x4a, 0x9a, 0x9b, 0xbc, 10,
        0xa4, 0xad, 0xbb, 0x13, 0x4b, 140, 0xd4, 0x80, 0xce, 0x65, 0x1d, 8, 90, 0x6a, 0x6f, 0x25,
        0xf9, 0x3f, 0xef, 0x1b, 0xa4, 0x72, 20, 0xed, 0x97, 0x22, 0x4a, 0x2e, 0xb8, 150, 0x4b, 0x8e,
        150, 0x93, 0xf1, 40, 0xb2, 11, 60, 0xf8, 0x5d, 170, 0xa9, 130, 0x13, 110, 0xc1, 0xa9,
        0x20, 0x57, 0xb2, 0x5b, 0x16, 0xcf, 0x9e, 0x5f, 0xd4, 0xcc, 0x2e, 0xf5, 0xc9, 0x4c, 0x1c, 0xee,
        0xe3, 0x3f, 0x29, 0xb3, 6, 0x70, 0x43, 0x3d, 0xf5, 0x90, 0xa2, 0x42, 2, 0x98, 80, 0xfd,
        0x5d, 0x4e, 0x92, 0xad, 0xad, 0x7f, 0xab, 0x60, 0x2c, 0xb8, 0x43, 0x76, 0x8f, 0x5f, 230, 0xa7,
        0x19, 0xe0, 0xb9, 0xb5, 0x62, 0x6b, 0xd4, 0x47, 0x69, 0x34, 14, 0x6d, 0xa4, 0x52, 0xe3, 100,
        0x4a, 0x65, 0x47, 0xf5, 0x3f, 0x53, 0x5e, 0x8b, 0x1b, 0xfd, 0x21, 0xf7, 0xba, 0x68, 0xf9, 0xdf,
        0x68, 0xa8, 150, 15, 0x8b, 1, 0x97, 0x58, 140, 30, 0xef, 0xb3, 0x41, 0x44, 0x21, 0xda,
        0xe0, 0xf4, 0xe0, 0x2d, 0xcd, 11, 240, 0x5c, 0x59, 0xd6, 0x99, 0xe7, 1, 0x15, 0x67, 50,
        0xe0, 0x12, 0x2f, 0xcd, 0xa2, 0xde, 0x52, 0xce, 0xec, 0xef, 0x77, 14, 0xbc, 0x38, 100, 0x8d,
        180, 0xdb, 0x67, 0xff, 200, 0x66, 12, 0x8a, 0x60, 0xe1, 0x2e, 0, 0x43, 0xa9, 0x37, 0x9c,
        0x11, 170, 0xb9, 0x98, 0xed, 0x21, 0x35, 0xd4, 0xc3, 0xde, 0x65, 0x54, 0x9d, 0x1c, 0xb0, 0xa9
     };

        private static void BytesWithCodeMip(byte[] toBytes)
        {
            for (int i = 0; i < toBytes.Length; i++)
            {
                toBytes[i] = (byte)(toBytes[i] ^ codeMip[i & 0xff]);
            }
        }

        public static async Task DecompressFileAsync(string filePath, string outputPath, CancellationToken cancellationToken)
        {
            byte[] buffer;
            using (FileStream fileStream = new(filePath, FileMode.Open, FileAccess.Read))
            {
                buffer = new byte[fileStream.Length];
                await fileStream.ReadAsync(buffer, 0, (int)fileStream.Length, cancellationToken);
            }
            BytesWithCodeMip(buffer);
            buffer = DecompressBytes(buffer);
            using FileStream outputFileStream = new(outputPath, FileMode.Create, FileAccess.Write);
            await outputFileStream.WriteAsync(buffer, 0, buffer.Length, cancellationToken);
        }

        private static byte[] DecompressBytes(byte[] toBytes)
        {
            using MemoryStream inputStream = new(toBytes);
            using DeflateStream deflateStream = new(inputStream, CompressionMode.Decompress);
            using MemoryStream outputStream = new();
            deflateStream.CopyTo(outputStream);
            return outputStream.ToArray();
        }

        //Zlib have issues with some files
        private static byte[] DecompressBytes1(byte[] toBytes)
        {
            int num = (toBytes.Length << 4) - toBytes.Length;
            byte[] buffer = new byte[num];
            int err = ZLibDll.Decompress(toBytes, toBytes.Length, buffer, ref num);
            if (err != 0) throw new Exception(string.Format("Decompress returned error code {0}.", err));

            toBytes = new byte[num];
            Buffer.BlockCopy(buffer, 0, toBytes, 0, num);
            return toBytes;
        }
    }
}
