using System;
using System.IO;

public static class LZMAHelper
{
    public enum LZMADictionarySize : Int32
    {
        Dict256KiB = 262144, // 2^18 = 262144 = 256 KiB
        Dict512KiB = 524288, // 2^19
        Dict1MiB = 1048576, // 2^20
        Dict2MiB = 2097152, // 2^21
        Dict4MiB = 4194304  // 2^22
    };

    public static LZMADictionarySize DefaultDictionarySize = LZMADictionarySize.Dict512KiB;

    public static int Compress(string inFile, string outFile)
    {
        var coder = new SevenZip.Compression.LZMA.Encoder();
        var input = new FileStream(inFile, FileMode.Open);
        var output = new FileStream(outFile, FileMode.Create);

        // Write the encoder properties
        coder.WriteCoderProperties(output);

        coder.SetCoderProperties(
            new SevenZip.CoderPropID[] { SevenZip.CoderPropID.DictionarySize },
            new object[] { (Int32)DefaultDictionarySize }
            );

        // Write the decompressed file size.
        output.Write(BitConverter.GetBytes(input.Length), 0, 4);

        // Encode the file.
        coder.Code(input, output, input.Length, -1, null);

        var ret = output.Length;
        output.Flush();
        output.Close();
        input.Close();

        return (int)ret;
    }

    public static int Compress(byte[] data, string outFile)
    {
        var coder = new SevenZip.Compression.LZMA.Encoder();
        var input = new MemoryStream(data);
        var output = new FileStream(outFile, FileMode.Create);

        coder.SetCoderProperties(
            new SevenZip.CoderPropID[] { SevenZip.CoderPropID.DictionarySize },
            new object[] { (Int32)DefaultDictionarySize }
            );

        // Write the encoder properties
        coder.WriteCoderProperties(output);

        // Write the decompressed file size.
        output.Write(BitConverter.GetBytes(input.Length), 0, 4);

        // Encode the file.
        coder.Code(input, output, input.Length, -1, null);

        var ret = output.Length;
        output.Flush();
        output.Close();
        input.Close();

        return (int)ret;
    }


    public static byte[] Compress(byte[] data)
    {
        var coder = new SevenZip.Compression.LZMA.Encoder();
        var input = new MemoryStream(data);
        var output = new MemoryStream();

        coder.SetCoderProperties(
            new SevenZip.CoderPropID[] { SevenZip.CoderPropID.DictionarySize },
            new object[] { (Int32)DefaultDictionarySize }
            );

        // Write the encoder properties
        coder.WriteCoderProperties(output);

        // Write the decompressed file size.
        output.Write(BitConverter.GetBytes(input.Length), 0, 4);

        // Encode the file.
        coder.Code(input, output, input.Length, -1, null);

        var ret = output.Length;
        output.Flush();
        output.Close();
        input.Close();

        return output.ToArray();
    }

    private static readonly ObjectPool<SevenZip.Compression.LZMA.Decoder> CoderPool = new ObjectPool<SevenZip.Compression.LZMA.Decoder>(
        () =>
        {
            return new SevenZip.Compression.LZMA.Decoder();
        });

    public static void Decompress(string inFile, string outFile)
    {

        var coder = CoderPool.Get();

        //var coder = new SevenZip.Compression.LZMA.Decoder();
        var input = new FileStream(inFile, FileMode.Open);
        var output = new FileStream(outFile, FileMode.Create);

        // Read the decoder properties
        var properties = new byte[5];
        input.Read(properties, 0, 5);

        // Read in the decompress file size.
        var fileLengthBytes = new byte[4];
        input.Read(fileLengthBytes, 0, 4);
        var fileLength = BitConverter.ToInt32(fileLengthBytes, 0);

        // Decompress the file.
        coder.SetDecoderProperties(properties);
        coder.Code(input, output, input.Length, fileLength, null);
        output.Flush();
        output.Close();
        input.Close();

        CoderPool.Put(coder);

    }


    public static void Decompress(byte[] data, string outFile)
    {
        var coder = CoderPool.Get();
        //var coder = new SevenZip.Compression.LZMA.Decoder();
        var input = new MemoryStream(data);
        var output = new FileStream(outFile, FileMode.Create);

        // Read the decoder properties
        var properties = new byte[5];
        input.Read(properties, 0, 5);

        // Read in the decompress file size.
        var fileLengthBytes = new byte[4];
        input.Read(fileLengthBytes, 0, 4);
        var fileLength = BitConverter.ToInt32(fileLengthBytes, 0);

        // Decompress the file.
        coder.SetDecoderProperties(properties);
        coder.Code(input, output, input.Length, fileLength, null);
        output.Flush();
        output.Close();
        input.Close();
        CoderPool.Put(coder);
    }
}