using System;

using BestHTTP.Extensions;
using BestHTTP.PlatformSupport.Memory;

namespace BestHTTP.Decompression
{
    public sealed class BrotliDecompressor : IDecompressor
    {
#if ((NET_STANDARD_2_1 || UNITY_2021_2_OR_NEWER) && (!(ENABLE_MONO && UNITY_ANDROID) || (!UNITY_WEBGL || UNITY_EDITOR))) && BESTHTTP_ENABLE_BROTLI
        private BufferPoolMemoryStream decompressorInputStream;
        private BufferPoolMemoryStream decompressorOutputStream;
        private System.IO.Compression.BrotliStream decompressorStream;
#endif

        private int MinLengthToDecompress = 256;

        public static bool IsSupported()
        {
            // Not enabled under android with the mono runtime
#if ((NET_STANDARD_2_1 || UNITY_2021_2_OR_NEWER) && (!(ENABLE_MONO && UNITY_ANDROID) || (!UNITY_WEBGL || UNITY_EDITOR))) && BESTHTTP_ENABLE_BROTLI
            return true;
#else
            return false;
#endif
        }

        public BrotliDecompressor(int minLengthToDecompress)
        {
            this.MinLengthToDecompress = minLengthToDecompress;
        }

        public DecompressedData Decompress(byte[] data, int offset, int count, bool forceDecompress = false, bool dataCanBeLarger = false)
        {
#if ((NET_STANDARD_2_1 || UNITY_2021_2_OR_NEWER) && (!(ENABLE_MONO && UNITY_ANDROID) || (!UNITY_WEBGL || UNITY_EDITOR))) && BESTHTTP_ENABLE_BROTLI
            if (decompressorInputStream == null)
                decompressorInputStream = new BufferPoolMemoryStream(count);

            if (data != null)
                decompressorInputStream.Write(data, offset, count);

            if (!forceDecompress && decompressorInputStream.Length < MinLengthToDecompress)
                return new DecompressedData(null, 0);

            decompressorInputStream.Position = 0;

            if (decompressorStream == null)
            {
                decompressorStream = new System.IO.Compression.BrotliStream(decompressorInputStream,
                                                             System.IO.Compression.CompressionMode.Decompress,
                                                             true);
            }

            if (decompressorOutputStream == null)
                decompressorOutputStream = new BufferPoolMemoryStream();
            decompressorOutputStream.SetLength(0);

            byte[] copyBuffer = BufferPool.Get(1024, true);

            int readCount;
            int sumReadCount = 0;
            while ((readCount = decompressorStream.Read(copyBuffer, 0, copyBuffer.Length)) != 0)
            {
                decompressorOutputStream.Write(copyBuffer, 0, readCount);
                sumReadCount += readCount;
            }

            BufferPool.Release(copyBuffer);

            // If no read is done (returned with any data) don't zero out the input stream, as it would delete any not yet used data.
            if (sumReadCount > 0)
                decompressorStream.SetLength(0);

            byte[] result = decompressorOutputStream.ToArray(dataCanBeLarger);

            return new DecompressedData(result, dataCanBeLarger ? (int)decompressorOutputStream.Length : result.Length);
#else
            return default(DecompressedData);
#endif
        }

        public void Dispose()
        {
#if ((NET_STANDARD_2_1 || UNITY_2021_2_OR_NEWER) && (!(ENABLE_MONO && UNITY_ANDROID) || (!UNITY_WEBGL || UNITY_EDITOR))) && BESTHTTP_ENABLE_BROTLI
            this.decompressorStream?.Dispose();
            this.decompressorStream = null;
#endif
        }
    }
}
