/*
 * ====================================================================
 * 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.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 *
 */
package org.apache.http.nio.client.methods;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.UnsupportedEncodingException;
import java.net.URI;

import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpOptions;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpTrace;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.utils.URIUtils;
import org.apache.http.entity.ContentType;
import org.apache.http.nio.entity.HttpAsyncContentProducer;
import org.apache.http.nio.entity.NByteArrayEntity;
import org.apache.http.nio.entity.NStringEntity;
import org.apache.http.nio.protocol.BasicAsyncRequestProducer;
import org.apache.http.nio.protocol.BasicAsyncResponseConsumer;
import org.apache.http.nio.protocol.HttpAsyncRequestProducer;
import org.apache.http.nio.protocol.HttpAsyncResponseConsumer;
import org.apache.http.util.Args;

/**
 * Factory methods for asynchronous request producers and response consumers.
 *
 * @since 4.0
 */
public final class HttpAsyncMethods {

    /**
     * Creates asynchronous request generator for the given request message.
     *
     * @param target request target.
     * @param request request message.
     * @return asynchronous request generator
     */
    public static HttpAsyncRequestProducer create(final HttpHost target, final HttpRequest request) {
        Args.notNull(target, "HTTP host");
        Args.notNull(request, "HTTP request");
        return new RequestProducerImpl(target, request);
    }

    /**
     * Creates asynchronous request generator for the given request message.
     *
     * @param request request message.
     * @return asynchronous request generator
     */
    public static HttpAsyncRequestProducer create(final HttpUriRequest request) {
        Args.notNull(request, "HTTP request");
        final HttpHost target = URIUtils.extractHost(request.getURI());
        return new RequestProducerImpl(target, request);
    }

    /**
     * Creates asynchronous {@code GET} request generator.
     *
     * @param requestURI request URI.
     * @return asynchronous request generator
     */
    public static HttpAsyncRequestProducer createGet(final URI requestURI) {
        return create(new HttpGet(requestURI));
    }

    /**
     * Creates asynchronous {@code GET} request generator.
     *
     * @param requestURI request URI.
     * @return asynchronous request generator
     */
    public static HttpAsyncRequestProducer createGet(final String requestURI) {
        return create(new HttpGet(URI.create(requestURI)));
    }

    /**
     * Creates asynchronous {@code HEAD} request generator.
     *
     * @param requestURI request URI.
     * @return asynchronous request generator
     */
    public static HttpAsyncRequestProducer createHead(final URI requestURI) {
        return create(new HttpHead(requestURI));
    }

    /**
     * Creates asynchronous {@code HEAD} request generator.
     *
     * @param requestURI request URI.
     * @return asynchronous request generator
     */
    public static HttpAsyncRequestProducer createHead(final String requestURI) {
        return create(new HttpHead(URI.create(requestURI)));
    }

    /**
     * Creates asynchronous {@code DELETE} request generator.
     *
     * @param requestURI request URI.
     * @return asynchronous request generator
     */
    public static HttpAsyncRequestProducer createDelete(final URI requestURI) {
        return create(new HttpDelete(requestURI));
    }

    /**
     * Creates asynchronous {@code DELETE} request generator.
     *
     * @param requestURI request URI.
     * @return asynchronous request generator
     */
    public static HttpAsyncRequestProducer createDelete(final String requestURI) {
        return create(new HttpDelete(URI.create(requestURI)));
    }

    /**
     * Creates asynchronous {@code OPTIONS} request generator.
     *
     * @param requestURI request URI.
     * @return asynchronous request generator
     */
    public static HttpAsyncRequestProducer createOptions(final URI requestURI) {
        return create(new HttpOptions(requestURI));
    }

    /**
     * Creates asynchronous {@code OPTIONS} request generator.
     *
     * @param requestURI request URI.
     * @return asynchronous request generator
     */
    public static HttpAsyncRequestProducer createOptions(final String requestURI) {
        return create(new HttpOptions(URI.create(requestURI)));
    }

    /**
     * Creates asynchronous {@code TRACE} request generator.
     *
     * @param requestURI request URI.
     * @return asynchronous request generator
     */
    public static HttpAsyncRequestProducer createTrace(final URI requestURI) {
        return create(new HttpTrace(requestURI));
    }

    /**
     * Creates asynchronous {@code TRACE} request generator.
     *
     * @param requestURI request URI.
     * @return asynchronous request generator
     */
    public static HttpAsyncRequestProducer createTrace(final String requestURI) {
        return create(new HttpTrace(URI.create(requestURI)));
    }

    /**
     * Creates asynchronous {@code POST} request generator.
     *
     * @param requestURI request URI.
     * @param content request content.
     * @param contentType request contentType.
     * @return asynchronous request generator
     */
    public static HttpAsyncRequestProducer createPost(
            final URI requestURI,
            final String content,
            final ContentType contentType) throws UnsupportedEncodingException {
        final HttpPost httppost = new HttpPost(requestURI);
        final NStringEntity entity = new NStringEntity(content, contentType);
        httppost.setEntity(entity);
        final HttpHost target = URIUtils.extractHost(requestURI);
        return new RequestProducerImpl(target, httppost, entity);
    }

    /**
     * Creates asynchronous {@code POST} request generator.
     *
     * @param requestURI request URI.
     * @param content request content.
     * @param contentType request contentType.
     * @return asynchronous request generator
     */
    public static HttpAsyncRequestProducer createPost(
            final String requestURI,
            final String content,
            final ContentType contentType) throws UnsupportedEncodingException {
        return createPost(URI.create(requestURI), content, contentType);
    }

    /**
     * Creates asynchronous {@code POST} request generator.
     *
     * @param requestURI request URI.
     * @param content request content.
     * @param contentType request contentType.
     * @return asynchronous request generator
     */
    public static HttpAsyncRequestProducer createPost(
            final URI requestURI,
            final byte[] content,
            final ContentType contentType) {
        final HttpPost httppost = new HttpPost(requestURI);
        final NByteArrayEntity entity = new NByteArrayEntity(content, contentType);
        httppost.setEntity(entity);
        final HttpHost target = URIUtils.extractHost(requestURI);
        return new RequestProducerImpl(target, httppost, entity);
    }

    /**
     * Creates asynchronous {@code POST} request generator.
     *
     * @param requestURI request URI.
     * @param content request content.
     * @param contentType request contentType.
     * @return asynchronous request generator
     */
    public static HttpAsyncRequestProducer createPost(
            final String requestURI,
            final byte[] content,
            final ContentType contentType) {
        return createPost(URI.create(requestURI), content, contentType);
    }

    /**
     * Creates asynchronous {@code PUT} request generator.
     *
     * @param requestURI request URI.
     * @param content request content.
     * @param contentType request contentType.
     * @return asynchronous request generator
     */
    public static HttpAsyncRequestProducer createPut(
            final URI requestURI,
            final String content,
            final ContentType contentType) throws UnsupportedEncodingException {
        final HttpPut httpput = new HttpPut(requestURI);
        final NStringEntity entity = new NStringEntity(content, contentType);
        httpput.setEntity(entity);
        final HttpHost target = URIUtils.extractHost(requestURI);
        return new RequestProducerImpl(target, httpput, entity);
    }

    /**
     * Creates asynchronous {@code PUT} request generator.
     *
     * @param requestURI request URI.
     * @param content request content.
     * @param contentType request contentType.
     * @return asynchronous request generator
     */
    public static HttpAsyncRequestProducer createPut(
            final String requestURI,
            final String content,
            final ContentType contentType) throws UnsupportedEncodingException {
        return createPut(URI.create(requestURI), content, contentType);
    }

    /**
     * Creates asynchronous {@code PUT} request generator.
     *
     * @param requestURI request URI.
     * @param content request content.
     * @param contentType request contentType.
     * @return asynchronous request generator
     */
    public static HttpAsyncRequestProducer createPut(
            final URI requestURI,
            final byte[] content,
            final ContentType contentType) {
        final HttpPut httpput = new HttpPut(requestURI);
        final NByteArrayEntity entity = new NByteArrayEntity(content, contentType);
        httpput.setEntity(entity);
        final HttpHost target = URIUtils.extractHost(requestURI);
        return new RequestProducerImpl(target, httpput, entity);
    }

    /**
     * Creates asynchronous {@code PUT} request generator.
     *
     * @param requestURI request URI.
     * @param content request content.
     * @param contentType request contentType.
     * @return asynchronous request generator
     */
    public static HttpAsyncRequestProducer createPut(
            final String requestURI,
            final byte[] content,
            final ContentType contentType) {
        return createPut(URI.create(requestURI), content, contentType);
    }

    /**
     * Creates asynchronous zero-copy {@code POST} request generator.
     *
     * @param requestURI request URI.
     * @param content request content.
     * @param contentType request contentType.
     * @return asynchronous request generator
     */
    public static HttpAsyncRequestProducer createZeroCopyPost(
            final URI requestURI,
            final File content,
            final ContentType contentType) throws FileNotFoundException {
        return new ZeroCopyPost(requestURI, content, contentType);
    }

    /**
     * Creates asynchronous zero-copy {@code POST} request generator.
     *
     * @param requestURI request URI.
     * @param content request content.
     * @param contentType request contentType.
     * @return asynchronous request generator
     */
    public static HttpAsyncRequestProducer createZeroCopyPost(
            final String requestURI,
            final File content,
            final ContentType contentType) throws FileNotFoundException {
        return new ZeroCopyPost(URI.create(requestURI), content, contentType);
    }

    /**
     * Creates asynchronous zero-copy {@code PUT} request generator.
     *
     * @param requestURI request URI.
     * @param content request content.
     * @param contentType request contentType.
     * @return asynchronous request generator
     */
    public static HttpAsyncRequestProducer createZeroCopyPut(
            final URI requestURI,
            final File content,
            final ContentType contentType) throws FileNotFoundException {
        return new ZeroCopyPut(requestURI, content, contentType);
    }

    /**
     * Creates asynchronous zero-copy {@code PUT} request generator.
     *
     * @param requestURI request URI.
     * @param content request content.
     * @param contentType request contentType.
     * @return asynchronous request generator
     */
    public static HttpAsyncRequestProducer createZeroCopyPut(
            final String requestURI,
            final File content,
            final ContentType contentType) throws FileNotFoundException {
        return new ZeroCopyPut(URI.create(requestURI), content, contentType);
    }

    /**
     * Creates basic response consumer that will buffer response content in memory.
     * @return asynchronous response consumer.
     */
    public static HttpAsyncResponseConsumer<HttpResponse> createConsumer() {
        return new BasicAsyncResponseConsumer();
    }

    /**
     * Creates zero-copy response consumer that will stream response content
     * directly to the given file.
     * @return asynchronous response consumer.
     */
    public static HttpAsyncResponseConsumer<HttpResponse> createZeroCopyConsumer(
            final File file) throws FileNotFoundException {
        return new ZeroCopyConsumer<HttpResponse>(file) {

            @Override
            protected HttpResponse process(
                    final HttpResponse response,
                    final File file,
                    final ContentType contentType) {
                return response;
            }

        };
    }

    static class RequestProducerImpl extends BasicAsyncRequestProducer {

        protected RequestProducerImpl(
                final HttpHost target,
                final HttpEntityEnclosingRequest request,
                final HttpAsyncContentProducer producer) {
            super(target, request, producer);
        }

        public RequestProducerImpl(final HttpHost target, final HttpRequest request) {
            super(target, request);
        }

    }

}
