/*
 * Copyright (c) 2012 Sonatype, Inc. All rights reserved.
 *
 * This program is licensed to you under the Apache License Version 2.0,
 * and you may not use this file except in compliance with the Apache License Version 2.0.
 * You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the Apache License Version 2.0 is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
 */

package org.asynchttpclient.providers.grizzly;

import static org.asynchttpclient.util.MiscUtil.isNonEmpty;
import static org.glassfish.grizzly.http.CookiesBuilder.ServerCookiesBuilder;

import org.asynchttpclient.Cookie;
import org.asynchttpclient.HttpResponseBodyPart;
import org.asynchttpclient.HttpResponseHeaders;
import org.asynchttpclient.HttpResponseStatus;
import org.asynchttpclient.providers.ResponseBase;
import org.asynchttpclient.util.AsyncHttpProviderUtils;

import org.glassfish.grizzly.Buffer;
import org.glassfish.grizzly.http.Cookies;
import org.glassfish.grizzly.http.util.Header;
import org.glassfish.grizzly.utils.Charsets;
import org.glassfish.grizzly.memory.Buffers;
import org.glassfish.grizzly.memory.MemoryManager;
import org.glassfish.grizzly.utils.BufferInputStream;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * {@link org.asynchttpclient.HttpResponseBodyPart} implementation using the Grizzly 2.0 HTTP client
 * codec.
 *
 * @author The Grizzly Team
 * @since 1.7.0
 */
public class GrizzlyResponse extends ResponseBase {

    private final Boolean rfc6265Enabled;

    private Buffer responseBody;
    private boolean initialized;


    // ------------------------------------------------------------ Constructors


    public GrizzlyResponse(final HttpResponseStatus status,
                           final HttpResponseHeaders headers,
                           final List<HttpResponseBodyPart> bodyParts,
                           final boolean rfc6265Enabled) {
        super(status, headers, bodyParts);
        this.rfc6265Enabled = rfc6265Enabled;
    }




    // --------------------------------------------------- Methods from Response

    /**
     * {@inheritDoc}
     */
    public InputStream getResponseBodyAsStream() throws IOException {

        return new BufferInputStream(getResponseBody0());

    }


    /**
     * {@inheritDoc}
     */
    public String getResponseBodyExcerpt(int maxLength, String charset) throws IOException {
       charset = calculateCharset(charset);
        final Buffer responseBody = getResponseBody0();
        final int len = Math.min(responseBody.remaining(), maxLength);
        final int pos = responseBody.position();
        return responseBody.toStringContent(getCharset(charset), pos, len + pos);

    }


    /**
     * {@inheritDoc}
     */
    public String getResponseBody(String charset) throws IOException {

        return getResponseBody0().toStringContent(getCharset(charset));

    }


    /**
     * {@inheritDoc}
     */
    @Override
    public byte[] getResponseBodyAsBytes() throws IOException {
        final byte[] responseBodyBytes = new byte[responseBody.remaining()];
        final int origPos = responseBody.position();
        responseBody.get(responseBodyBytes);
        responseBody.position(origPos);
        return responseBodyBytes;
    }


    /**
     * {@inheritDoc}
     */
    public String getResponseBodyExcerpt(int maxLength) throws IOException {

        return getResponseBodyExcerpt(maxLength, null);

    }


    /**
     * {@inheritDoc}
     */
    public String getResponseBody() throws IOException {

        return getResponseBody(null);

    }

    /**
     * @return the response body as a Grizzly {@link Buffer}.
     */
    @SuppressWarnings("UnusedDeclaration")
    public Buffer getResponseBodyAsBuffer() {
        return getResponseBody0();
    }

    /**
     * {@inheritDoc}
     */
    public List<Cookie> buildCookies() {

        List<String> values = headers.getHeaders().get(Header.SetCookie.toString());
        if (isNonEmpty(values)) {
            ServerCookiesBuilder builder = new ServerCookiesBuilder(false, rfc6265Enabled);
            for (int i = 0, len = values.size(); i < len; i++) {
                builder.parse(values.get(i));
            }
            return convertCookies(builder.build());

        } else {
            return Collections.unmodifiableList(Collections.<Cookie>emptyList());
        }
    }

    // --------------------------------------------------------- Private Methods


    private List<Cookie> convertCookies(Cookies cookies) {

        final org.glassfish.grizzly.http.Cookie[] grizzlyCookies = cookies.get();
        List<Cookie> convertedCookies = new ArrayList<Cookie>(grizzlyCookies.length);
        for (int i = 0, len = grizzlyCookies.length; i < len; i++) {
            org.glassfish.grizzly.http.Cookie gCookie = grizzlyCookies[i];
            convertedCookies.add(new Cookie(gCookie.getDomain(),
                                   gCookie.getName(),
                                   gCookie.getValue(),
                                   gCookie.getValue(),
                                   gCookie.getPath(),
                                   gCookie.getMaxAge(),
                                   gCookie.isSecure(),
                                   gCookie.getVersion(),
                                   gCookie.isHttpOnly(),
                                   false,
                                   gCookie.getComment(),
                                   null,
                                   Collections.<Integer> emptySet()));
        }
        return Collections.unmodifiableList(convertedCookies);

    }


    private Charset getCharset(final String charset) {

        String charsetLocal = charset;

        if (charsetLocal == null) {
            String contentType = getContentType();
            if (contentType != null) {
                charsetLocal = AsyncHttpProviderUtils.parseCharset(contentType);
            }
        }

        if (charsetLocal == null) {
            charsetLocal = Charsets.DEFAULT_CHARACTER_ENCODING;
        }

        return Charsets.lookupCharset(charsetLocal);

    }

    private synchronized Buffer getResponseBody0() {
        if (!initialized) {
            if (isNonEmpty(bodyParts)) {
                if (bodyParts.size() == 1) {
                    responseBody = ((GrizzlyResponseBodyPart) bodyParts.get(
                            0)).getBodyBuffer();
                } else {
                    final Buffer firstBuffer =
                            ((GrizzlyResponseBodyPart) bodyParts.get(
                                    0)).getBodyBuffer();
                    final MemoryManager<?> mm =
                            MemoryManager.DEFAULT_MEMORY_MANAGER;
                    Buffer constructedBodyBuffer = firstBuffer;
                    for (int i = 1, len = bodyParts.size(); i < len; i++) {
                        constructedBodyBuffer =
                                Buffers.appendBuffers(mm,
                                                      constructedBodyBuffer,
                                                      ((GrizzlyResponseBodyPart) bodyParts
                                                              .get(i)).getBodyBuffer());
                    }
                    responseBody = constructedBodyBuffer;
                }
            } else {
                responseBody = Buffers.EMPTY_BUFFER;
            }
            initialized = true;
        }
        return responseBody;
    }
}

@@ -3416,24 +3416,80 @@
 Exception %7B%0A
+        final Buffer responseBody = getResponseBody0();%0A
         fina

Call getResponseBody0 before allocating the array.