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

package io.netty.handler.codec.http3;

import io.netty.util.ReferenceCountUtil;
import io.netty.channel.ChannelInboundHandlerAdapter;
import java.util.List;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import org.jetbrains.annotations.Nullable;
import java.util.function.LongFunction;
import io.netty.channel.ChannelHandler;
import java.util.function.Supplier;
import io.netty.util.AttributeKey;
import io.netty.handler.codec.ByteToMessageDecoder;

abstract class Http3UnidirectionalStreamInboundHandler extends ByteToMessageDecoder
{
    private static final AttributeKey<Boolean> REMOTE_CONTROL_STREAM;
    private static final AttributeKey<Boolean> REMOTE_QPACK_DECODER_STREAM;
    private static final AttributeKey<Boolean> REMOTE_QPACK_ENCODER_STREAM;
    final Http3FrameCodec.Http3FrameCodecFactory codecFactory;
    final Http3ControlStreamInboundHandler localControlStreamHandler;
    final Http3ControlStreamOutboundHandler remoteControlStreamHandler;
    final Supplier<ChannelHandler> qpackEncoderHandlerFactory;
    final Supplier<ChannelHandler> qpackDecoderHandlerFactory;
    final LongFunction<ChannelHandler> unknownStreamHandlerFactory;
    
    Http3UnidirectionalStreamInboundHandler(final Http3FrameCodec.Http3FrameCodecFactory codecFactory, final Http3ControlStreamInboundHandler localControlStreamHandler, final Http3ControlStreamOutboundHandler remoteControlStreamHandler, @Nullable LongFunction<ChannelHandler> unknownStreamHandlerFactory, final Supplier<ChannelHandler> qpackEncoderHandlerFactory, final Supplier<ChannelHandler> qpackDecoderHandlerFactory) {
        this.codecFactory = codecFactory;
        this.localControlStreamHandler = localControlStreamHandler;
        this.remoteControlStreamHandler = remoteControlStreamHandler;
        this.qpackEncoderHandlerFactory = qpackEncoderHandlerFactory;
        this.qpackDecoderHandlerFactory = qpackDecoderHandlerFactory;
        if (unknownStreamHandlerFactory == null) {
            unknownStreamHandlerFactory = (LongFunction<ChannelHandler>)(type -> ReleaseHandler.INSTANCE);
        }
        this.unknownStreamHandlerFactory = unknownStreamHandlerFactory;
    }
    
    @Override
    protected void decode(final ChannelHandlerContext ctx, final ByteBuf in, final List<Object> out) {
        if (!in.isReadable()) {
            return;
        }
        final int len = Http3CodecUtils.numBytesForVariableLengthInteger(in.getByte(in.readerIndex()));
        if (in.readableBytes() < len) {
            return;
        }
        final long type = Http3CodecUtils.readVariableLengthInteger(in, len);
        switch ((int)type) {
            case 0: {
                this.initControlStream(ctx);
                break;
            }
            case 1: {
                final int pushIdLen = Http3CodecUtils.numBytesForVariableLengthInteger(in.getByte(in.readerIndex()));
                if (in.readableBytes() < pushIdLen) {
                    return;
                }
                final long pushId = Http3CodecUtils.readVariableLengthInteger(in, pushIdLen);
                this.initPushStream(ctx, pushId);
                break;
            }
            case 2: {
                this.initQpackEncoderStream(ctx);
                break;
            }
            case 3: {
                this.initQpackDecoderStream(ctx);
                break;
            }
            default: {
                this.initUnknownStream(ctx, type);
                break;
            }
        }
    }
    
    private void initControlStream(final ChannelHandlerContext ctx) {
        if (ctx.channel().parent().attr(Http3UnidirectionalStreamInboundHandler.REMOTE_CONTROL_STREAM).setIfAbsent(true) == null) {
            ctx.pipeline().addLast(this.localControlStreamHandler);
            ctx.pipeline().replace(this, null, this.codecFactory.newCodec(Http3ControlStreamFrameTypeValidator.INSTANCE, Http3RequestStreamCodecState.NO_STATE, Http3RequestStreamCodecState.NO_STATE));
        }
        else {
            Http3CodecUtils.connectionError(ctx, Http3ErrorCode.H3_STREAM_CREATION_ERROR, "Received multiple control streams.", false);
        }
    }
    
    private boolean ensureStreamNotExistsYet(final ChannelHandlerContext ctx, final AttributeKey<Boolean> key) {
        return ctx.channel().parent().attr(key).setIfAbsent(true) == null;
    }
    
    abstract void initPushStream(final ChannelHandlerContext p0, final long p1);
    
    private void initQpackEncoderStream(final ChannelHandlerContext ctx) {
        if (this.ensureStreamNotExistsYet(ctx, Http3UnidirectionalStreamInboundHandler.REMOTE_QPACK_ENCODER_STREAM)) {
            ctx.pipeline().replace(this, null, this.qpackEncoderHandlerFactory.get());
        }
        else {
            Http3CodecUtils.connectionError(ctx, Http3ErrorCode.H3_STREAM_CREATION_ERROR, "Received multiple QPACK encoder streams.", false);
        }
    }
    
    private void initQpackDecoderStream(final ChannelHandlerContext ctx) {
        if (this.ensureStreamNotExistsYet(ctx, Http3UnidirectionalStreamInboundHandler.REMOTE_QPACK_DECODER_STREAM)) {
            ctx.pipeline().replace(this, null, this.qpackDecoderHandlerFactory.get());
        }
        else {
            Http3CodecUtils.connectionError(ctx, Http3ErrorCode.H3_STREAM_CREATION_ERROR, "Received multiple QPACK decoder streams.", false);
        }
    }
    
    private void initUnknownStream(final ChannelHandlerContext ctx, final long streamType) {
        ctx.pipeline().replace(this, null, this.unknownStreamHandlerFactory.apply(streamType));
    }
    
    static {
        REMOTE_CONTROL_STREAM = AttributeKey.valueOf("H3_REMOTE_CONTROL_STREAM");
        REMOTE_QPACK_DECODER_STREAM = AttributeKey.valueOf("H3_REMOTE_QPACK_DECODER_STREAM");
        REMOTE_QPACK_ENCODER_STREAM = AttributeKey.valueOf("H3_REMOTE_QPACK_ENCODER_STREAM");
    }
    
    static final class ReleaseHandler extends ChannelInboundHandlerAdapter
    {
        static final ReleaseHandler INSTANCE;
        
        @Override
        public boolean isSharable() {
            return true;
        }
        
        @Override
        public void channelRead(final ChannelHandlerContext ctx, final Object msg) throws Exception {
            ReferenceCountUtil.release(msg);
        }
        
        static {
            INSTANCE = new ReleaseHandler();
        }
    }
}
