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

package io.netty.handler.codec.http3;

import io.netty.handler.codec.quic.QuicStreamType;
import io.netty.util.internal.StringUtil;
import io.netty.buffer.Unpooled;
import io.netty.util.CharsetUtil;
import io.netty.handler.codec.quic.QuicChannel;
import io.netty.channel.Channel;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelFuture;
import org.jetbrains.annotations.Nullable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.quic.QuicStreamChannel;
import io.netty.util.internal.ObjectUtil;

final class Http3CodecUtils
{
    static final long MIN_RESERVED_FRAME_TYPE = 64L;
    static final long MAX_RESERVED_FRAME_TYPE = 66571993090L;
    static final int HTTP3_DATA_FRAME_TYPE = 0;
    static final int HTTP3_HEADERS_FRAME_TYPE = 1;
    static final int HTTP3_CANCEL_PUSH_FRAME_TYPE = 3;
    static final int HTTP3_SETTINGS_FRAME_TYPE = 4;
    static final int HTTP3_PUSH_PROMISE_FRAME_TYPE = 5;
    static final int HTTP3_GO_AWAY_FRAME_TYPE = 7;
    static final int HTTP3_MAX_PUSH_ID_FRAME_TYPE = 13;
    static final int HTTP3_CANCEL_PUSH_FRAME_MAX_LEN = 8;
    static final int HTTP3_SETTINGS_FRAME_MAX_LEN = 256;
    static final int HTTP3_GO_AWAY_FRAME_MAX_LEN = 8;
    static final int HTTP3_MAX_PUSH_ID_FRAME_MAX_LEN = 8;
    static final int HTTP3_CONTROL_STREAM_TYPE = 0;
    static final int HTTP3_PUSH_STREAM_TYPE = 1;
    static final int HTTP3_QPACK_ENCODER_STREAM_TYPE = 2;
    static final int HTTP3_QPACK_DECODER_STREAM_TYPE = 3;
    
    private Http3CodecUtils() {
    }
    
    static long checkIsReservedFrameType(final long type) {
        return ObjectUtil.checkInRange(type, 64L, 66571993090L, "type");
    }
    
    static boolean isReservedFrameType(final long type) {
        return type >= 64L && type <= 66571993090L;
    }
    
    static boolean isServerInitiatedQuicStream(final QuicStreamChannel channel) {
        return channel.streamId() % 2L != 0L;
    }
    
    static boolean isReservedHttp2FrameType(final long type) {
        switch ((int)type) {
            case 2:
            case 6:
            case 8:
            case 9: {
                return true;
            }
            default: {
                return false;
            }
        }
    }
    
    static boolean isReservedHttp2Setting(final long key) {
        return 2L <= key && key <= 5L;
    }
    
    static int numBytesForVariableLengthInteger(final long value) {
        if (value <= 63L) {
            return 1;
        }
        if (value <= 16383L) {
            return 2;
        }
        if (value <= 1073741823L) {
            return 4;
        }
        if (value <= 4611686018427387903L) {
            return 8;
        }
        throw new IllegalArgumentException();
    }
    
    static void writeVariableLengthInteger(final ByteBuf out, final long value) {
        final int numBytes = numBytesForVariableLengthInteger(value);
        writeVariableLengthInteger(out, value, numBytes);
    }
    
    static void writeVariableLengthInteger(final ByteBuf out, final long value, final int numBytes) {
        final int writerIndex = out.writerIndex();
        switch (numBytes) {
            case 1: {
                out.writeByte((byte)value);
                break;
            }
            case 2: {
                out.writeShort((short)value);
                encodeLengthIntoBuffer(out, writerIndex, (byte)64);
                break;
            }
            case 4: {
                out.writeInt((int)value);
                encodeLengthIntoBuffer(out, writerIndex, (byte)(-128));
                break;
            }
            case 8: {
                out.writeLong(value);
                encodeLengthIntoBuffer(out, writerIndex, (byte)(-64));
                break;
            }
            default: {
                throw new IllegalArgumentException();
            }
        }
    }
    
    private static void encodeLengthIntoBuffer(final ByteBuf out, final int index, final byte b) {
        out.setByte(index, out.getByte(index) | b);
    }
    
    static long readVariableLengthInteger(final ByteBuf in, final int len) {
        switch (len) {
            case 1: {
                return in.readUnsignedByte();
            }
            case 2: {
                return in.readUnsignedShort() & 0x3FFF;
            }
            case 4: {
                return in.readUnsignedInt() & 0x3FFFFFFFL;
            }
            case 8: {
                return in.readLong() & 0x3FFFFFFFFFFFFFFFL;
            }
            default: {
                throw new IllegalArgumentException();
            }
        }
    }
    
    static int numBytesForVariableLengthInteger(final byte b) {
        final byte val = (byte)(b >> 6);
        if ((val & 0x1) != 0x0) {
            if ((val & 0x2) != 0x0) {
                return 8;
            }
            return 2;
        }
        else {
            if ((val & 0x2) != 0x0) {
                return 4;
            }
            return 1;
        }
    }
    
    static void criticalStreamClosed(final ChannelHandlerContext ctx) {
        if (ctx.channel().parent().isActive()) {
            connectionError(ctx, Http3ErrorCode.H3_CLOSED_CRITICAL_STREAM, "Critical stream closed.", false);
        }
    }
    
    static void connectionError(final ChannelHandlerContext ctx, final Http3Exception exception, final boolean fireException) {
        if (fireException) {
            ctx.fireExceptionCaught((Throwable)exception);
        }
        connectionError(ctx.channel(), exception.errorCode(), exception.getMessage());
    }
    
    static void connectionError(final ChannelHandlerContext ctx, final Http3ErrorCode errorCode, @Nullable final String msg, final boolean fireException) {
        if (fireException) {
            ctx.fireExceptionCaught((Throwable)new Http3Exception(errorCode, msg));
        }
        connectionError(ctx.channel(), errorCode, msg);
    }
    
    static void closeOnFailure(final ChannelFuture future) {
        if (future.isDone() && !future.isSuccess()) {
            future.channel().close();
            return;
        }
        future.addListener((GenericFutureListener<? extends Future<? super Void>>)ChannelFutureListener.CLOSE_ON_FAILURE);
    }
    
    static void connectionError(final Channel channel, final Http3ErrorCode errorCode, @Nullable final String msg) {
        QuicChannel quicChannel;
        if (channel instanceof QuicChannel) {
            quicChannel = (QuicChannel)channel;
        }
        else {
            quicChannel = (QuicChannel)channel.parent();
        }
        ByteBuf buffer;
        if (msg != null) {
            buffer = quicChannel.alloc().buffer();
            buffer.writeCharSequence(msg, CharsetUtil.US_ASCII);
        }
        else {
            buffer = Unpooled.EMPTY_BUFFER;
        }
        quicChannel.close(true, errorCode.code, buffer);
    }
    
    static void streamError(final ChannelHandlerContext ctx, final Http3ErrorCode errorCode) {
        ((QuicStreamChannel)ctx.channel()).shutdownOutput(errorCode.code);
    }
    
    static void readIfNoAutoRead(final ChannelHandlerContext ctx) {
        if (!ctx.channel().config().isAutoRead()) {
            ctx.read();
        }
    }
    
    @Nullable
    static Http3ConnectionHandler getConnectionHandlerOrClose(final QuicChannel ch) {
        final Http3ConnectionHandler connectionHandler = ch.pipeline().get(Http3ConnectionHandler.class);
        if (connectionHandler == null) {
            connectionError(ch, Http3ErrorCode.H3_INTERNAL_ERROR, "Couldn't obtain the " + StringUtil.simpleClassName(Http3ConnectionHandler.class) + " of the parent Channel");
            return null;
        }
        return connectionHandler;
    }
    
    static void verifyIsUnidirectional(final QuicStreamChannel ch) {
        if (ch.type() != QuicStreamType.UNIDIRECTIONAL) {
            throw new IllegalArgumentException("Invalid stream type: " + ch.type() + " for stream: " + ch.streamId());
        }
    }
}
