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

package io.netty.handler.codec.http3;

import io.netty.util.ReferenceCountUtil;
import io.netty.channel.ChannelPromise;
import io.netty.channel.socket.ChannelInputShutdownEvent;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import org.jetbrains.annotations.Nullable;
import io.netty.util.internal.ObjectUtil;
import io.netty.channel.ChannelHandler;

final class Http3ControlStreamOutboundHandler extends Http3FrameTypeDuplexValidationHandler<Http3ControlStreamFrame>
{
    private final boolean server;
    private final ChannelHandler codec;
    private Long sentMaxPushId;
    private Long sendGoAwayId;
    private Http3SettingsFrame localSettings;
    
    Http3ControlStreamOutboundHandler(final boolean server, final Http3SettingsFrame localSettings, final ChannelHandler codec) {
        super(Http3ControlStreamFrame.class);
        this.server = server;
        this.localSettings = ObjectUtil.checkNotNull(localSettings, "localSettings");
        this.codec = ObjectUtil.checkNotNull(codec, "codec");
    }
    
    @Nullable
    Long sentMaxPushId() {
        return this.sentMaxPushId;
    }
    
    @Override
    public void channelActive(final ChannelHandlerContext ctx) {
        final ByteBuf buffer = ctx.alloc().buffer(8);
        Http3CodecUtils.writeVariableLengthInteger(buffer, 0L);
        ctx.write(buffer);
        ctx.pipeline().addFirst(this.codec);
        assert this.localSettings != null;
        Http3CodecUtils.closeOnFailure(ctx.writeAndFlush(this.localSettings));
        this.localSettings = null;
        ctx.fireChannelActive();
    }
    
    @Override
    public void userEventTriggered(final ChannelHandlerContext ctx, final Object evt) {
        if (evt instanceof ChannelInputShutdownEvent) {
            Http3CodecUtils.criticalStreamClosed(ctx);
        }
        ctx.fireUserEventTriggered(evt);
    }
    
    @Override
    public void channelInactive(final ChannelHandlerContext ctx) {
        Http3CodecUtils.criticalStreamClosed(ctx);
        ctx.fireChannelInactive();
    }
    
    @Override
    void write(final ChannelHandlerContext ctx, final Http3ControlStreamFrame msg, final ChannelPromise promise) {
        if (msg instanceof Http3MaxPushIdFrame && !this.handleHttp3MaxPushIdFrame(promise, (Http3MaxPushIdFrame)msg)) {
            ReferenceCountUtil.release(msg);
            return;
        }
        if (msg instanceof Http3GoAwayFrame && !this.handleHttp3GoAwayFrame(promise, (Http3GoAwayFrame)msg)) {
            ReferenceCountUtil.release(msg);
            return;
        }
        ctx.write(msg, promise);
    }
    
    private boolean handleHttp3MaxPushIdFrame(final ChannelPromise promise, final Http3MaxPushIdFrame maxPushIdFrame) {
        final long id = maxPushIdFrame.id();
        if (this.sentMaxPushId != null && id < this.sentMaxPushId) {
            promise.setFailure((Throwable)new Http3Exception(Http3ErrorCode.H3_ID_ERROR, "MAX_PUSH_ID reduced limit."));
            return false;
        }
        this.sentMaxPushId = maxPushIdFrame.id();
        return true;
    }
    
    private boolean handleHttp3GoAwayFrame(final ChannelPromise promise, final Http3GoAwayFrame goAwayFrame) {
        final long id = goAwayFrame.id();
        if (this.server && id % 4L != 0L) {
            promise.setFailure((Throwable)new Http3Exception(Http3ErrorCode.H3_ID_ERROR, "GOAWAY id not valid : " + id));
            return false;
        }
        if (this.sendGoAwayId != null && id > this.sendGoAwayId) {
            promise.setFailure((Throwable)new Http3Exception(Http3ErrorCode.H3_ID_ERROR, "GOAWAY id is bigger then the last sent: " + id + " > " + this.sendGoAwayId));
            return false;
        }
        this.sendGoAwayId = id;
        return true;
    }
    
    @Override
    public boolean isSharable() {
        return false;
    }
}
