// ------------------------------------------------------------------------------
// Copyright (c) 2017 Microsoft Corporation
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// ------------------------------------------------------------------------------

package com.microsoft.graph.concurrency;

import com.microsoft.graph.core.ClientException;
import com.microsoft.graph.core.Constants;
import com.microsoft.graph.http.GraphServiceException;
import com.microsoft.graph.http.HttpResponseCode;
import com.microsoft.graph.http.HttpResponseHeadersHelper;
import com.microsoft.graph.http.IHttpRequest;
import com.microsoft.graph.http.IStatefulResponseHandler;
import com.microsoft.graph.logger.ILogger;
import com.microsoft.graph.serializer.ISerializer;

import java.io.BufferedInputStream;
import java.io.InputStream;
import java.util.Map;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import okhttp3.Response;

import static com.microsoft.graph.http.HttpResponseCode.HTTP_CREATED;
import static com.microsoft.graph.http.HttpResponseCode.HTTP_OK;

/**
 * Handles the stateful response from the OneDrive upload session
 *
 * @param <UploadType> the expected uploaded item
 */
public class ChunkedUploadResponseHandler<UploadType>
		implements IStatefulResponseHandler<ChunkedUploadResult<UploadType>, UploadType> {

	private final static HttpResponseHeadersHelper  responseHeadersHelper = new HttpResponseHeadersHelper();
	/**
	 * The expected deserialized upload type
	 */
	private final Class<UploadType> deserializeTypeClass;

    private final Class<? extends IUploadSession> uploadSessionClass;
	/**
	 * Creates a chunked upload response handler
	 *
	 * @param uploadType the expected upload item type
     * @param uploadSessionType the type of the upload session
	 */
	protected ChunkedUploadResponseHandler(@Nonnull final Class<UploadType> uploadType, @Nonnull final Class<? extends IUploadSession>  uploadSessionType) {
        this.deserializeTypeClass = uploadType;
        this.uploadSessionClass = uploadSessionType;
	}

	/**
	 * Do nothing before getting the response
	 *
	 * @param response The response
	 */
	@Override
	public void configConnection(@Nonnull final Response response) {
		return;
	}

	/**
	 * Generate the chunked upload response result
	 *
	 * @param request	the HTTP request
	 * @param response the HTTP response
	 * @param serializer the serializer
	 * @param logger	 the system logger
	 * @return the chunked upload result, which could be either an uploaded item or error
	 * @throws Exception an exception occurs if the request was unable to complete for any reason
	 */
	@Override
	@Nullable
	public ChunkedUploadResult<UploadType> generateResult(
			@Nonnull final IHttpRequest request,
			@Nonnull final Response response,
			@Nonnull final ISerializer serializer,
			@Nonnull final ILogger logger) throws Exception {
		if (response.code() >= HttpResponseCode.HTTP_CLIENT_ERROR) {
			logger.logDebug("Receiving error during upload, see detail on result error");

			return new ChunkedUploadResult<>(
					GraphServiceException.createFromResponse(request, null, serializer,
							response, logger));
		} else if (response.code() >= HTTP_OK
				&& response.code() < HttpResponseCode.HTTP_MULTIPLE_CHOICES) {
			final Map<String, String> headers = responseHeadersHelper.getResponseHeadersAsMapStringString(response);
			final String contentType = headers.get(Constants.CONTENT_TYPE_HEADER_NAME);
			final String location = headers.get("Location");
			if (contentType != null
					&& contentType.contains(Constants.JSON_CONTENT_TYPE)) {
				try (final InputStream in = new BufferedInputStream(response.body().byteStream())) {
					if (isUploadSessionCompleted(response)) {
						logger.logDebug("Upload session is completed (ODSP), uploaded item returned.");
						final UploadType uploadedItem = serializer.deserializeObject(in, this.deserializeTypeClass);
						return new ChunkedUploadResult<>(uploadedItem);
					} else {
						final IUploadSession session = serializer.deserializeObject(in, uploadSessionClass);
						logger.logDebug("Chunk bytes has been accepted by the server.");
						return new ChunkedUploadResult<>(session);
					}
				}
			} else if (location != null) {
				logger.logDebug("Upload session is completed (Outlook), uploaded item returned.");
				return new ChunkedUploadResult<>(this.deserializeTypeClass.getDeclaredConstructor().newInstance());
			} else {
				logger.logDebug("Upload session returned an unexpected response");
			}
		}
		return new ChunkedUploadResult<>(new ClientException("Received an unexpected response from the service, response code: " + response.code(), null));
	}

	private boolean isUploadSessionCompleted(Response response) {
		return response.code() == HTTP_OK || response.code() == HTTP_CREATED;
	}
}

@@ -1412,16 +1412,66 @@
 stants;%0A
+import com.microsoft.graph.http.CoreHttpProvider;%0A
 import c
@@ -4764,46 +4764,231 @@
 %09%09%09%09
-if (isUploadSessionCompleted(response)
+final String rawJson = CoreHttpProvider.streamToString(in);%0A%09%09%09%09%09final IUploadSession session = serializer.deserializeObject(rawJson, uploadSessionClass);%0A%0A%09%09%09%09%09if (session == null %7C%7C session.getNextExpectedRanges() == null
 ) %7B%0A
@@ -5244,99 +5244,8 @@
 e %7B%0A
-%09%09%09%09%09%09final IUploadSession session = serializer.deserializeObject(in, uploadSessionClass);%0A
 %09%09%09%09

revert change in ChunkedUploadResponseHandler