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

package io.netty.handler.codec.http3;

import io.netty.util.concurrent.Future;
import java.util.Objects;
import io.netty.channel.Channel;
import io.netty.handler.codec.quic.QuicStreamChannel;
import io.netty.handler.codec.quic.QuicStreamType;
import io.netty.handler.codec.quic.QuicChannel;
import io.netty.channel.ChannelHandlerContext;
import org.jetbrains.annotations.Nullable;
import io.netty.channel.ChannelHandler;
import java.util.function.LongFunction;
import io.netty.channel.ChannelInboundHandlerAdapter;

public abstract class Http3ConnectionHandler extends ChannelInboundHandlerAdapter
{
    final Http3FrameCodec.Http3FrameCodecFactory codecFactory;
    final LongFunction<ChannelHandler> unknownInboundStreamHandlerFactory;
    final boolean disableQpackDynamicTable;
    final Http3ControlStreamInboundHandler localControlStreamHandler;
    final Http3ControlStreamOutboundHandler remoteControlStreamHandler;
    final QpackDecoder qpackDecoder;
    final QpackEncoder qpackEncoder;
    private boolean controlStreamCreationInProgress;
    final long maxTableCapacity;
    
    Http3ConnectionHandler(final boolean server, @Nullable final ChannelHandler inboundControlStreamHandler, @Nullable final LongFunction<ChannelHandler> unknownInboundStreamHandlerFactory, @Nullable Http3SettingsFrame localSettings, final boolean disableQpackDynamicTable) {
        this.unknownInboundStreamHandlerFactory = unknownInboundStreamHandlerFactory;
        this.disableQpackDynamicTable = disableQpackDynamicTable;
        if (localSettings == null) {
            localSettings = new DefaultHttp3SettingsFrame();
        }
        else {
            localSettings = DefaultHttp3SettingsFrame.copyOf(localSettings);
        }
        Long maxFieldSectionSize = localSettings.get(Http3SettingsFrame.HTTP3_SETTINGS_MAX_FIELD_SECTION_SIZE);
        if (maxFieldSectionSize == null) {
            maxFieldSectionSize = Long.MAX_VALUE;
        }
        this.maxTableCapacity = localSettings.getOrDefault(Http3SettingsFrame.HTTP3_SETTINGS_QPACK_MAX_TABLE_CAPACITY, 0L);
        final int maxBlockedStreams = Math.toIntExact(localSettings.getOrDefault(Http3SettingsFrame.HTTP3_SETTINGS_QPACK_BLOCKED_STREAMS, 0L));
        this.qpackDecoder = new QpackDecoder(this.maxTableCapacity, maxBlockedStreams);
        this.qpackEncoder = new QpackEncoder();
        this.codecFactory = Http3FrameCodec.newFactory(this.qpackDecoder, maxFieldSectionSize, this.qpackEncoder);
        this.remoteControlStreamHandler = new Http3ControlStreamOutboundHandler(server, localSettings, this.codecFactory.newCodec(Http3FrameTypeValidator.NO_VALIDATION, Http3RequestStreamCodecState.NO_STATE, Http3RequestStreamCodecState.NO_STATE));
        this.localControlStreamHandler = new Http3ControlStreamInboundHandler(server, inboundControlStreamHandler, this.qpackEncoder, this.remoteControlStreamHandler);
    }
    
    private void createControlStreamIfNeeded(final ChannelHandlerContext ctx) {
        if (!this.controlStreamCreationInProgress && Http3.getLocalControlStream(ctx.channel()) == null) {
            this.controlStreamCreationInProgress = true;
            final QuicChannel channel = (QuicChannel)ctx.channel();
            channel.createStream(QuicStreamType.UNIDIRECTIONAL, this.remoteControlStreamHandler).addListener(f -> {
                if (!f.isSuccess()) {
                    ctx.fireExceptionCaught((Throwable)new Http3Exception(Http3ErrorCode.H3_STREAM_CREATION_ERROR, "Unable to open control stream", f.cause()));
                    ctx.close();
                }
                else {
                    Http3.setLocalControlStream(channel, f.getNow());
                }
            });
        }
    }
    
    public final boolean isGoAwayReceived() {
        return this.localControlStreamHandler.isGoAwayReceived();
    }
    
    final ChannelHandler newCodec(final Http3RequestStreamCodecState encodeState, final Http3RequestStreamCodecState decodeState) {
        return this.codecFactory.newCodec(Http3RequestStreamFrameTypeValidator.INSTANCE, encodeState, decodeState);
    }
    
    final ChannelHandler newRequestStreamValidationHandler(final QuicStreamChannel forStream, final Http3RequestStreamCodecState encodeState, final Http3RequestStreamCodecState decodeState) {
        final QpackAttributes qpackAttributes = Http3.getQpackAttributes(forStream.parent());
        assert qpackAttributes != null;
        if (this.localControlStreamHandler.isServer()) {
            return Http3RequestStreamValidationHandler.newServerValidator(qpackAttributes, this.qpackDecoder, encodeState, decodeState);
        }
        final Http3ControlStreamInboundHandler localControlStreamHandler = this.localControlStreamHandler;
        Objects.requireNonNull(localControlStreamHandler);
        return Http3RequestStreamValidationHandler.newClientValidator(localControlStreamHandler::isGoAwayReceived, qpackAttributes, this.qpackDecoder, encodeState, decodeState);
    }
    
    final ChannelHandler newPushStreamValidationHandler(final QuicStreamChannel forStream, final Http3RequestStreamCodecState decodeState) {
        if (this.localControlStreamHandler.isServer()) {
            return Http3PushStreamServerValidationHandler.INSTANCE;
        }
        final QpackAttributes qpackAttributes = Http3.getQpackAttributes(forStream.parent());
        assert qpackAttributes != null;
        return new Http3PushStreamClientValidationHandler(qpackAttributes, this.qpackDecoder, decodeState);
    }
    
    @Override
    public void handlerAdded(final ChannelHandlerContext ctx) {
        final QuicChannel channel = (QuicChannel)ctx.channel();
        Http3.setQpackAttributes(channel, new QpackAttributes(channel, this.disableQpackDynamicTable));
        if (ctx.channel().isActive()) {
            this.createControlStreamIfNeeded(ctx);
        }
    }
    
    @Override
    public void channelActive(final ChannelHandlerContext ctx) {
        this.createControlStreamIfNeeded(ctx);
        ctx.fireChannelActive();
    }
    
    @Override
    public void channelRead(final ChannelHandlerContext ctx, final Object msg) {
        if (msg instanceof QuicStreamChannel) {
            final QuicStreamChannel channel = (QuicStreamChannel)msg;
            switch (channel.type()) {
                case BIDIRECTIONAL: {
                    this.initBidirectionalStream(ctx, channel);
                    break;
                }
                case UNIDIRECTIONAL: {
                    this.initUnidirectionalStream(ctx, channel);
                    break;
                }
                default: {
                    throw new Error("Unexpected channel type: " + channel.type());
                }
            }
        }
        ctx.fireChannelRead(msg);
    }
    
    abstract void initBidirectionalStream(final ChannelHandlerContext p0, final QuicStreamChannel p1);
    
    abstract void initUnidirectionalStream(final ChannelHandlerContext p0, final QuicStreamChannel p1);
    
    long maxTableCapacity() {
        return this.maxTableCapacity;
    }
    
    @Override
    public boolean isSharable() {
        return false;
    }
}
