using System;
using System.IO;

namespace Zlib
{
    public static class Utils
    {
        //Returns true if there was an error, out params the uncompressed bytes
        public static bool Inflate(byte[] compressed, out byte[] uncompressed)
        {
            int outputSize = 2048;
            byte[] output = new Byte[outputSize];
            using (MemoryStream ms = new MemoryStream())
            {
                ZlibCodec compressor = new ZlibCodec();
                compressor.InitializeInflate(false);
                compressor.InputBuffer = compressed;
                compressor.AvailableBytesIn = compressed.Length;
                compressor.NextIn = 0;
                compressor.OutputBuffer = output;
                int bytesToWrite = 0;
                int result = ZlibConstants.Z_OK;
                while (true)
                {
                    compressor.AvailableBytesOut = outputSize;
                    compressor.NextOut = 0;
                    result = compressor.Inflate(FlushType.None);

                    bytesToWrite = outputSize - compressor.AvailableBytesOut;
                    if (bytesToWrite > 0)
                        ms.Write(output, 0, bytesToWrite);

                    if (result != ZlibConstants.Z_OK)
                        break;
                }
                compressor.EndInflate();
                if (result != ZlibConstants.Z_STREAM_END)
                {
                    uncompressed = new byte[0];
                    return true;
                }
                uncompressed = ms.ToArray();
                return false;
            }
        }

        //Returns true if there was an error. out params the compressed bytes
        public static bool Deflate(byte[] uncompressed, out byte[] compressed)
        {
            int outputSize = 2048;
            byte[] output = new Byte[outputSize];
            int lengthToCompress = uncompressed.Length;

            using (MemoryStream ms = new MemoryStream())
            {
                ZlibCodec compressor = new ZlibCodec();
                compressor.InitializeDeflate(CompressionLevel.BestCompression, false);

                compressor.InputBuffer = uncompressed;
                compressor.AvailableBytesIn = lengthToCompress;
                compressor.NextIn = 0;
                compressor.OutputBuffer = output;

                bool isError = false;
                foreach (FlushType f in new FlushType[] { FlushType.None, FlushType.Finish })
                {
                    int bytesToWrite = 0;
                    int result = ZlibConstants.Z_OK;
                    do
                    {
                        compressor.AvailableBytesOut = outputSize;
                        compressor.NextOut = 0;
                        result = compressor.Deflate(f);

                        bytesToWrite = outputSize - compressor.AvailableBytesOut;
                        if (bytesToWrite > 0)
                            ms.Write(output, 0, bytesToWrite);

                        if (result != ZlibConstants.Z_OK)
                        {
                            if (result != ZlibConstants.Z_STREAM_END)
                            {
                                isError = true;
                            }
                            break;
                        }

                    }
                    while ((f == FlushType.None && (compressor.AvailableBytesIn != 0 || compressor.AvailableBytesOut == 0)) ||
                           (f == FlushType.Finish && bytesToWrite != 0));
                }

                compressor.EndDeflate();
                ms.Flush();
                if (isError)
                {
                    compressed = new byte[0];
                    return true;
                }
                compressed = ms.ToArray();
                return false;
            }
        }
    }
}
