/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.camel.component.undertow;

import java.io.IOException;
import java.nio.ByteBuffer;

import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import io.undertow.server.handlers.form.EagerFormParsingHandler;
import io.undertow.util.Headers;
import io.undertow.util.HttpString;
import io.undertow.util.Methods;
import io.undertow.util.MimeMappings;
import io.undertow.util.StatusCodes;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.TypeConverter;
import org.apache.camel.impl.DefaultConsumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The Undertow consumer which is also an Undertow HttpHandler implementation to handle incoming request.
 */
public class UndertowConsumer extends DefaultConsumer implements HttpHandler {

    private static final Logger LOG = LoggerFactory.getLogger(UndertowConsumer.class);

    private HttpHandlerRegistrationInfo registrationInfo;

    public UndertowConsumer(UndertowEndpoint endpoint, Processor processor) {
        super(endpoint, processor);
    }

    @Override
    public UndertowEndpoint getEndpoint() {
        return (UndertowEndpoint) super.getEndpoint();
    }

    @Override
    protected void doStart() throws Exception {
        super.doStart();
        getEndpoint().getComponent().registerConsumer(this);
    }

    @Override
    protected void doStop() throws Exception {
        super.doStop();
        getEndpoint().getComponent().unregisterConsumer(this);
    }

    public HttpHandlerRegistrationInfo getHttpHandlerRegistrationInfo() {
        if (registrationInfo == null) {
            UndertowEndpoint endpoint = getEndpoint();

            registrationInfo = new HttpHandlerRegistrationInfo();
            registrationInfo.setUri(endpoint.getHttpURI());
            registrationInfo.setMethodRestrict(endpoint.getHttpMethodRestrict());
            registrationInfo.setMatchOnUriPrefix(endpoint.getMatchOnUriPrefix());
        }
        return registrationInfo;
    }

    public HttpHandler getHttpHandler() {
        // wrap with EagerFormParsingHandler to enable undertow form parsers
        return new EagerFormParsingHandler().setNext(this);
    }

    @Override
    public void handleRequest(HttpServerExchange httpExchange) throws Exception {
        HttpString requestMethod = httpExchange.getRequestMethod();

        if (Methods.OPTIONS.equals(requestMethod) && !getEndpoint().isOptionsEnabled()) {
            String allowedMethods;
            if (getEndpoint().getHttpMethodRestrict() != null) {
                allowedMethods = "OPTIONS," + getEndpoint().getHttpMethodRestrict();
            } else {
                allowedMethods = "GET,HEAD,POST,PUT,DELETE,TRACE,OPTIONS,CONNECT,PATCH";
            }
            //return list of allowed methods in response headers
            httpExchange.setStatusCode(StatusCodes.OK);
            httpExchange.getResponseHeaders().put(ExchangeHeaders.CONTENT_TYPE, MimeMappings.DEFAULT_MIME_MAPPINGS.get("txt"));
            httpExchange.getResponseHeaders().put(ExchangeHeaders.CONTENT_LENGTH, 0);
            httpExchange.getResponseHeaders().put(Headers.ALLOW, allowedMethods);
            httpExchange.getResponseSender().close();
            return;
        }

        //perform blocking operation on exchange
        if (httpExchange.isInIoThread()) {
            httpExchange.dispatch(this);
            return;
        }

        //create new Exchange
        //binding is used to extract header and payload(if available)
        Exchange camelExchange = getEndpoint().createExchange(httpExchange);

        //Unit of Work to process the Exchange
        createUoW(camelExchange);
        try {
            getProcessor().process(camelExchange);
        } catch (Exception e) {
            getExceptionHandler().handleException(e);
        } finally {
            doneUoW(camelExchange);
        }

        Object body = getResponseBody(httpExchange, camelExchange);
        TypeConverter tc = getEndpoint().getCamelContext().getTypeConverter();

        if (body == null) {
            LOG.trace("No payload to send as reply for exchange: " + camelExchange);
            httpExchange.getResponseHeaders().put(ExchangeHeaders.CONTENT_TYPE, MimeMappings.DEFAULT_MIME_MAPPINGS.get("txt"));
            httpExchange.getResponseSender().send("No response available");
        } else {
            ByteBuffer bodyAsByteBuffer = tc.convertTo(ByteBuffer.class, body);
            httpExchange.getResponseSender().send(bodyAsByteBuffer);
        }
        httpExchange.getResponseSender().close();
    }

    private Object getResponseBody(HttpServerExchange httpExchange, Exchange camelExchange) throws IOException {
        Object result;
        if (camelExchange.hasOut()) {
            result = getEndpoint().getUndertowHttpBinding().toHttpResponse(httpExchange, camelExchange.getOut());
        } else {
            result = getEndpoint().getUndertowHttpBinding().toHttpResponse(httpExchange, camelExchange.getIn());
        }
        return result;
    }

}

@@ -900,16 +900,45 @@
 uffer;%0A%0A
+import io.undertow.Handlers;%0A
 import i
@@ -2922,16 +2922,105 @@
       //
+ allow for HTTP 1.1 continue%0A        return Handlers.httpContinueRead(%0A                //
  wrap wi
@@ -3086,22 +3086,23 @@
         
-return
+       
  new Eag
@@ -3133,24 +3133,25 @@
 etNext(this)
+)
 ;%0A    %7D%0A%0A   

allow for 100-continue processing