// 
// Decompiled by Procyon v0.6.0
// 

package io.netty.handler.codec.compression;

import io.netty.util.internal.SystemPropertyUtil;
import io.netty.util.internal.logging.InternalLoggerFactory;
import io.netty.util.internal.EmptyArrays;
import io.netty.buffer.ByteBuf;
import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.PromiseNotifier;
import io.netty.channel.ChannelPromise;
import io.netty.channel.ChannelFuture;
import io.netty.util.internal.ObjectUtil;
import java.util.zip.CRC32;
import io.netty.channel.ChannelHandlerContext;
import java.util.zip.Deflater;
import io.netty.util.internal.logging.InternalLogger;

public class JdkZlibEncoder extends ZlibEncoder
{
    private static final InternalLogger logger;
    private static final int MAX_INITIAL_OUTPUT_BUFFER_SIZE;
    private static final int MAX_INPUT_BUFFER_SIZE;
    private final ZlibWrapper wrapper;
    private final Deflater deflater;
    private volatile boolean finished;
    private volatile ChannelHandlerContext ctx;
    private final CRC32 crc;
    private static final byte[] gzipHeader;
    private boolean writeHeader;
    
    public JdkZlibEncoder() {
        this(6);
    }
    
    public JdkZlibEncoder(final int compressionLevel) {
        this(ZlibWrapper.ZLIB, compressionLevel);
    }
    
    public JdkZlibEncoder(final ZlibWrapper wrapper) {
        this(wrapper, 6);
    }
    
    public JdkZlibEncoder(final ZlibWrapper wrapper, final int compressionLevel) {
        this.writeHeader = true;
        ObjectUtil.checkInRange(compressionLevel, -1, 9, "compressionLevel");
        ObjectUtil.checkNotNull(wrapper, "wrapper");
        if (wrapper == ZlibWrapper.ZLIB_OR_NONE) {
            throw new IllegalArgumentException("wrapper '" + ZlibWrapper.ZLIB_OR_NONE + "' is not allowed for compression.");
        }
        this.wrapper = wrapper;
        this.deflater = new Deflater(compressionLevel, wrapper != ZlibWrapper.ZLIB);
        this.crc = ((wrapper == ZlibWrapper.GZIP) ? new CRC32() : null);
    }
    
    public JdkZlibEncoder(final byte[] dictionary) {
        this(6, dictionary);
    }
    
    public JdkZlibEncoder(final int compressionLevel, final byte[] dictionary) {
        this.writeHeader = true;
        ObjectUtil.checkInRange(compressionLevel, -1, 9, "compressionLevel");
        ObjectUtil.checkNotNull(dictionary, "dictionary");
        this.wrapper = ZlibWrapper.ZLIB;
        (this.deflater = new Deflater(compressionLevel)).setDictionary(dictionary);
        this.crc = null;
    }
    
    @Override
    public ChannelFuture close() {
        return this.close(this.ctx().newPromise());
    }
    
    @Override
    public ChannelFuture close(final ChannelPromise promise) {
        final ChannelHandlerContext ctx = this.ctx();
        final EventExecutor executor = ctx.executor();
        if (executor.inEventLoop()) {
            return this.finishEncode(ctx, promise);
        }
        final ChannelPromise p = ctx.newPromise();
        executor.execute(() -> {
            final ChannelFuture f = this.finishEncode(this.ctx(), p);
            PromiseNotifier.cascade(f, (Promise<? super Object>)promise);
            return;
        });
        return p;
    }
    
    private ChannelHandlerContext ctx() {
        final ChannelHandlerContext ctx = this.ctx;
        if (ctx == null) {
            throw new IllegalStateException("not added to a pipeline");
        }
        return ctx;
    }
    
    @Override
    public boolean isClosed() {
        return this.finished;
    }
    
    @Override
    protected void encode(final ChannelHandlerContext ctx, final ByteBuf uncompressed, final ByteBuf out) throws Exception {
        if (this.finished) {
            out.writeBytes(uncompressed);
            return;
        }
        final int len = uncompressed.readableBytes();
        if (len == 0) {
            return;
        }
        if (uncompressed.hasArray()) {
            this.encodeSome(uncompressed, out);
        }
        else {
            final int heapBufferSize = Math.min(len, JdkZlibEncoder.MAX_INPUT_BUFFER_SIZE);
            final ByteBuf heapBuf = ctx.alloc().heapBuffer(heapBufferSize, heapBufferSize);
            try {
                while (uncompressed.isReadable()) {
                    uncompressed.readBytes(heapBuf, Math.min(heapBuf.writableBytes(), uncompressed.readableBytes()));
                    this.encodeSome(heapBuf, out);
                    heapBuf.clear();
                }
            }
            finally {
                heapBuf.release();
            }
        }
        this.deflater.setInput(EmptyArrays.EMPTY_BYTES);
    }
    
    private void encodeSome(final ByteBuf in, final ByteBuf out) {
        final byte[] inAry = in.array();
        final int offset = in.arrayOffset() + in.readerIndex();
        if (this.writeHeader) {
            this.writeHeader = false;
            if (this.wrapper == ZlibWrapper.GZIP) {
                out.writeBytes(JdkZlibEncoder.gzipHeader);
            }
        }
        final int len = in.readableBytes();
        if (this.wrapper == ZlibWrapper.GZIP) {
            this.crc.update(inAry, offset, len);
        }
        this.deflater.setInput(inAry, offset, len);
        while (true) {
            this.deflate(out);
            if (!out.isWritable()) {
                out.ensureWritable(out.writerIndex());
            }
            else {
                if (this.deflater.needsInput()) {
                    break;
                }
                continue;
            }
        }
        in.skipBytes(len);
    }
    
    @Override
    protected final ByteBuf allocateBuffer(final ChannelHandlerContext ctx, final ByteBuf msg, final boolean preferDirect) throws Exception {
        int sizeEstimate = (int)Math.ceil(msg.readableBytes() * 1.001) + 12;
        if (this.writeHeader) {
            switch (this.wrapper) {
                case GZIP: {
                    sizeEstimate += JdkZlibEncoder.gzipHeader.length;
                    break;
                }
                case ZLIB: {
                    sizeEstimate += 2;
                    break;
                }
            }
        }
        if (sizeEstimate < 0 || sizeEstimate > JdkZlibEncoder.MAX_INITIAL_OUTPUT_BUFFER_SIZE) {
            return ctx.alloc().heapBuffer(JdkZlibEncoder.MAX_INITIAL_OUTPUT_BUFFER_SIZE);
        }
        return ctx.alloc().heapBuffer(sizeEstimate);
    }
    
    @Override
    public void close(final ChannelHandlerContext ctx, final ChannelPromise promise) throws Exception {
        final ChannelFuture f = this.finishEncode(ctx, ctx.newPromise());
        EncoderUtil.closeAfterFinishEncode(ctx, f, promise);
    }
    
    private ChannelFuture finishEncode(final ChannelHandlerContext ctx, final ChannelPromise promise) {
        if (this.finished) {
            promise.setSuccess();
            return promise;
        }
        this.finished = true;
        ByteBuf footer = ctx.alloc().heapBuffer();
        if (this.writeHeader && this.wrapper == ZlibWrapper.GZIP) {
            this.writeHeader = false;
            footer.writeBytes(JdkZlibEncoder.gzipHeader);
        }
        this.deflater.finish();
        while (!this.deflater.finished()) {
            this.deflate(footer);
            if (!footer.isWritable()) {
                ctx.write(footer);
                footer = ctx.alloc().heapBuffer();
            }
        }
        if (this.wrapper == ZlibWrapper.GZIP) {
            final int crcValue = (int)this.crc.getValue();
            final int uncBytes = this.deflater.getTotalIn();
            footer.writeIntLE(crcValue);
            footer.writeIntLE(uncBytes);
        }
        this.deflater.end();
        return ctx.writeAndFlush(footer, promise);
    }
    
    private void deflate(final ByteBuf out) {
        int numBytes;
        do {
            final int writerIndex = out.writerIndex();
            numBytes = this.deflater.deflate(out.array(), out.arrayOffset() + writerIndex, out.writableBytes(), 2);
            out.writerIndex(writerIndex + numBytes);
        } while (numBytes > 0);
    }
    
    @Override
    public void handlerAdded(final ChannelHandlerContext ctx) throws Exception {
        this.ctx = ctx;
    }
    
    static {
        logger = InternalLoggerFactory.getInstance(JdkZlibEncoder.class);
        gzipHeader = new byte[] { 31, -117, 8, 0, 0, 0, 0, 0, 0, 0 };
        MAX_INITIAL_OUTPUT_BUFFER_SIZE = SystemPropertyUtil.getInt("io.netty.jdkzlib.encoder.maxInitialOutputBufferSize", 65536);
        MAX_INPUT_BUFFER_SIZE = SystemPropertyUtil.getInt("io.netty.jdkzlib.encoder.maxInputBufferSize", 65536);
        if (JdkZlibEncoder.logger.isDebugEnabled()) {
            JdkZlibEncoder.logger.debug("-Dio.netty.jdkzlib.encoder.maxInitialOutputBufferSize={}", (Object)JdkZlibEncoder.MAX_INITIAL_OUTPUT_BUFFER_SIZE);
            JdkZlibEncoder.logger.debug("-Dio.netty.jdkzlib.encoder.maxInputBufferSize={}", (Object)JdkZlibEncoder.MAX_INPUT_BUFFER_SIZE);
        }
    }
}
