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

package io.netty.handler.codec.compression;

import io.netty.channel.ChannelHandlerContext;
import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.MessageToByteEncoder;

public class SnappyFrameEncoder extends MessageToByteEncoder<ByteBuf>
{
    private static final short SNAPPY_SLICE_SIZE = Short.MAX_VALUE;
    private static final int SNAPPY_SLICE_JUMBO_SIZE = 65535;
    private static final int MIN_COMPRESSIBLE_LENGTH = 18;
    private static final byte[] STREAM_START;
    private final Snappy snappy;
    private boolean started;
    private final int sliceSize;
    
    public SnappyFrameEncoder() {
        this(32767);
    }
    
    public static SnappyFrameEncoder snappyEncoderWithJumboFrames() {
        return new SnappyFrameEncoder(65535);
    }
    
    private SnappyFrameEncoder(final int sliceSize) {
        super(ByteBuf.class);
        this.snappy = new Snappy();
        this.sliceSize = sliceSize;
    }
    
    @Override
    protected void encode(final ChannelHandlerContext ctx, final ByteBuf in, final ByteBuf out) throws Exception {
        if (!in.isReadable()) {
            return;
        }
        if (!this.started) {
            this.started = true;
            out.writeBytes(SnappyFrameEncoder.STREAM_START);
        }
        int dataLength = in.readableBytes();
        if (dataLength > 18) {
            while (true) {
                final int lengthIdx = out.writerIndex() + 1;
                if (dataLength < 18) {
                    final ByteBuf slice = in.readSlice(dataLength);
                    writeUnencodedChunk(slice, out, dataLength);
                    break;
                }
                out.writeInt(0);
                if (dataLength <= this.sliceSize) {
                    final ByteBuf slice = in.readSlice(dataLength);
                    calculateAndWriteChecksum(slice, out);
                    this.snappy.encode(slice, out, dataLength);
                    setChunkLength(out, lengthIdx);
                    break;
                }
                final ByteBuf slice = in.readSlice(this.sliceSize);
                calculateAndWriteChecksum(slice, out);
                this.snappy.encode(slice, out, this.sliceSize);
                setChunkLength(out, lengthIdx);
                dataLength -= this.sliceSize;
            }
        }
        else {
            writeUnencodedChunk(in, out, dataLength);
        }
    }
    
    private static void writeUnencodedChunk(final ByteBuf in, final ByteBuf out, final int dataLength) {
        out.writeByte(1);
        writeChunkLength(out, dataLength + 4);
        calculateAndWriteChecksum(in, out);
        out.writeBytes(in, dataLength);
    }
    
    private static void setChunkLength(final ByteBuf out, final int lengthIdx) {
        final int chunkLength = out.writerIndex() - lengthIdx - 3;
        if (chunkLength >>> 24 != 0) {
            throw new CompressionException("compressed data too large: " + chunkLength);
        }
        out.setMediumLE(lengthIdx, chunkLength);
    }
    
    private static void writeChunkLength(final ByteBuf out, final int chunkLength) {
        out.writeMediumLE(chunkLength);
    }
    
    private static void calculateAndWriteChecksum(final ByteBuf slice, final ByteBuf out) {
        out.writeIntLE(Snappy.calculateChecksum(slice));
    }
    
    static {
        STREAM_START = new byte[] { -1, 6, 0, 0, 115, 78, 97, 80, 112, 89 };
    }
}
