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

package io.netty.handler.codec.http.websocketx.extensions;

import io.netty.handler.codec.http.HttpHeaders;
import io.netty.util.concurrent.GenericFutureListener;
import io.netty.util.concurrent.Future;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.DefaultHttpResponse;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelPromise;
import java.util.Iterator;
import java.util.Collections;
import java.util.ArrayList;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.DefaultHttpRequest;
import io.netty.handler.codec.http.LastHttpContent;
import io.netty.channel.ChannelHandlerContext;
import java.util.Arrays;
import io.netty.util.internal.ObjectUtil;
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.List;
import io.netty.channel.ChannelDuplexHandler;

public class WebSocketServerExtensionHandler extends ChannelDuplexHandler
{
    private final List<WebSocketServerExtensionHandshaker> extensionHandshakers;
    private final Queue<List<WebSocketServerExtension>> validExtensions;
    
    public WebSocketServerExtensionHandler(final WebSocketServerExtensionHandshaker... extensionHandshakers) {
        this.validExtensions = new ArrayDeque<List<WebSocketServerExtension>>(4);
        this.extensionHandshakers = Arrays.asList((WebSocketServerExtensionHandshaker[])ObjectUtil.checkNonEmpty((T[])extensionHandshakers, "extensionHandshakers"));
    }
    
    @Override
    public void channelRead(final ChannelHandlerContext ctx, final Object msg) throws Exception {
        if (msg != LastHttpContent.EMPTY_LAST_CONTENT) {
            if (msg instanceof DefaultHttpRequest) {
                this.onHttpRequestChannelRead(ctx, (HttpRequest)msg);
            }
            else if (msg instanceof HttpRequest) {
                this.onHttpRequestChannelRead(ctx, (HttpRequest)msg);
            }
            else {
                super.channelRead(ctx, msg);
            }
        }
        else {
            super.channelRead(ctx, msg);
        }
    }
    
    protected void onHttpRequestChannelRead(final ChannelHandlerContext ctx, final HttpRequest request) throws Exception {
        List<WebSocketServerExtension> validExtensionsList = null;
        if (WebSocketExtensionUtil.isWebsocketUpgrade(request.headers())) {
            final String extensionsHeader = request.headers().getAsString(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS);
            if (extensionsHeader != null) {
                final List<WebSocketExtensionData> extensions = WebSocketExtensionUtil.extractExtensions(extensionsHeader);
                int rsv = 0;
                for (final WebSocketExtensionData extensionData : extensions) {
                    Iterator<WebSocketServerExtensionHandshaker> extensionHandshakersIterator;
                    WebSocketServerExtension validExtension;
                    WebSocketServerExtensionHandshaker extensionHandshaker;
                    for (extensionHandshakersIterator = this.extensionHandshakers.iterator(), validExtension = null; validExtension == null && extensionHandshakersIterator.hasNext(); validExtension = extensionHandshaker.handshakeExtension(extensionData)) {
                        extensionHandshaker = extensionHandshakersIterator.next();
                    }
                    if (validExtension != null && (validExtension.rsv() & rsv) == 0x0) {
                        if (validExtensionsList == null) {
                            validExtensionsList = new ArrayList<WebSocketServerExtension>(1);
                        }
                        rsv |= validExtension.rsv();
                        validExtensionsList.add(validExtension);
                    }
                }
            }
        }
        if (validExtensionsList == null) {
            validExtensionsList = Collections.emptyList();
        }
        this.validExtensions.offer(validExtensionsList);
        super.channelRead(ctx, request);
    }
    
    @Override
    public void write(final ChannelHandlerContext ctx, final Object msg, final ChannelPromise promise) throws Exception {
        if (msg != Unpooled.EMPTY_BUFFER && !(msg instanceof ByteBuf)) {
            if (msg instanceof DefaultHttpResponse) {
                this.onHttpResponseWrite(ctx, (HttpResponse)msg, promise);
            }
            else if (msg instanceof HttpResponse) {
                this.onHttpResponseWrite(ctx, (HttpResponse)msg, promise);
            }
            else {
                super.write(ctx, msg, promise);
            }
        }
        else {
            super.write(ctx, msg, promise);
        }
    }
    
    protected void onHttpResponseWrite(final ChannelHandlerContext ctx, final HttpResponse response, final ChannelPromise promise) throws Exception {
        final List<WebSocketServerExtension> validExtensionsList = this.validExtensions.poll();
        if (HttpResponseStatus.SWITCHING_PROTOCOLS.equals(response.status())) {
            this.handlePotentialUpgrade(ctx, promise, response, validExtensionsList);
        }
        super.write(ctx, response, promise);
    }
    
    private void handlePotentialUpgrade(final ChannelHandlerContext ctx, final ChannelPromise promise, final HttpResponse httpResponse, final List<WebSocketServerExtension> validExtensionsList) {
        final HttpHeaders headers = httpResponse.headers();
        if (WebSocketExtensionUtil.isWebsocketUpgrade(headers)) {
            if (validExtensionsList != null && !validExtensionsList.isEmpty()) {
                final String headerValue = headers.getAsString(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS);
                final List<WebSocketExtensionData> extraExtensions = new ArrayList<WebSocketExtensionData>(this.extensionHandshakers.size());
                for (final WebSocketServerExtension extension : validExtensionsList) {
                    extraExtensions.add(extension.newReponseData());
                }
                final String newHeaderValue = WebSocketExtensionUtil.computeMergeExtensionsHeaderValue(headerValue, extraExtensions);
                promise.addListener((GenericFutureListener<? extends Future<? super Void>>)new ChannelFutureListener() {
                    @Override
                    public void operationComplete(final ChannelFuture future) {
                        if (future.isSuccess()) {
                            for (final WebSocketServerExtension extension : validExtensionsList) {
                                final WebSocketExtensionDecoder decoder = extension.newExtensionDecoder();
                                final WebSocketExtensionEncoder encoder = extension.newExtensionEncoder();
                                final String name = ctx.name();
                                ctx.pipeline().addAfter(name, decoder.getClass().getName(), decoder).addAfter(name, encoder.getClass().getName(), encoder);
                            }
                        }
                    }
                });
                if (newHeaderValue != null) {
                    headers.set(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS, newHeaderValue);
                }
            }
            promise.addListener((GenericFutureListener<? extends Future<? super Void>>)new ChannelFutureListener() {
                @Override
                public void operationComplete(final ChannelFuture future) {
                    if (future.isSuccess()) {
                        ctx.pipeline().remove(WebSocketServerExtensionHandler.this);
                    }
                }
            });
        }
    }
}
