<#@ template language="C#" inherits="BaseResponseUnmarshaller"#>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#
    AddLicenseHeader();

    AddCommonUsingStatements();
#>
using ThirdParty.Json.LitJson;

namespace <#=this.Config.Namespace #>.Model.Internal.MarshallTransformations
{
    /// <summary>
    /// Response Unmarshaller for <#=this.UnmarshallerBaseName #> operation
    /// </summary>  
    public class <#=this.UnmarshallerBaseName #>ResponseUnmarshaller : JsonResponseUnmarshaller
    {
        /// <summary>
        /// Unmarshaller the response from the service to the response class.
        /// </summary>  
        /// <param name="context"></param>
        /// <returns></returns>
        public override AmazonWebServiceResponse Unmarshall(JsonUnmarshallerContext context)
        {
            <#=this.UnmarshallerBaseName #>Response response = new <#=this.Operation.Name #>Response();

<#
    var payload = this.Operation.ResponsePayloadMember;
    var unmarshallPayload = payload != null && payload.IsStructure;
    var payloadIsStream = payload != null && !unmarshallPayload;
    bool isEventStreamOutput = this.Operation.IsEventStreamOutput;
    if (this.Operation.ResponseHasBodyMembers || payload != null)
    {
        if (this.Operation.AllowEmptyResult)
        {
            throw new NotImplementedException("AllowEmptyResult is not implemented for JSON unmarshallers");
        }
        if (isEventStreamOutput)
        {
#>
            response.<#=payload.PropertyName#> = new <#=payload.Shape.Name#>(context.Stream);
<#
        }
        else if (payloadIsStream)
        {
            if (payload.IsStreaming)
            {
#>
            response.<#=payload.PropertyName#> = context.Stream;
<#
            }
            else if (payload.ModelShape.IsString)
            {
#>
            using (var sr = new StreamReader(context.Stream))
            {
                response.<#=payload.PropertyName#> = sr.ReadToEnd();
            }
<#
            }
            else if (payload.ModelShape.IsMemoryStream)
            {
#>
            var ms = new MemoryStream();
            Amazon.Util.AWSSDKUtils.CopyStream(context.Stream, ms);
            ms.Seek(0, SeekOrigin.Begin);
            response.<#=payload.PropertyName#> = ms;
<#
            }
            else
            {
                // At this point, all valid configurations have been handled.  Valid use of payload is defined:
                // https://awslabs.github.io/smithy/1.0/spec/core/http-traits.html#httppayload-trait
                throw new Exception(
                    $"{payload.PropertyName} can not be a Payload as Type {payload.Shape.Type} is not a valid target for the httpPayload trait.  " +
                    "The httpPayload trait can be applied to structure members that target a string, blob, structure, union, document, set, map, or list.");
            }
        }
        else if (unmarshallPayload)
        {
#>
            var unmarshaller = <#= payload.DetermineTypeUnmarshallerInstantiate() #>;
            response.<#=payload.PropertyName#> = unmarshaller.Unmarshall(context);
<#
        }
		else if (this.IsWrapped)
		{
#>
			response.<#=this.WrappedResultMember#> = <#=this.Structure.Name#>Unmarshaller.Instance.Unmarshall(context);
<#
		}
        else
        {
#>
            context.Read();
            int targetDepth = context.CurrentDepth;
            while (context.ReadAtDepth(targetDepth))
            {
<#
        
            foreach (var member in this.Operation.ResponseBodyMembers)
            {
#>
                if (context.TestExpression("<#=member.MarshallName#>", targetDepth))
                {
                    var unmarshaller = <#= member.DetermineTypeUnmarshallerInstantiate() #>;
                    response.<#=member.PropertyName#> = unmarshaller.Unmarshall(context);
                    continue;
                }
<#
            }
#>
            }
<#
        }
    }
	UnmarshallHeaders();
	ProcessStatusCode();
#>

            return response;
        }

        /// <summary>
        /// Unmarshaller error response to exception.
        /// </summary>  
        /// <param name="context"></param>
        /// <param name="innerException"></param>
        /// <param name="statusCode"></param>
        /// <returns></returns>
        public override AmazonServiceException UnmarshallException(JsonUnmarshallerContext context, Exception innerException, HttpStatusCode statusCode)
        {
            var errorResponse = JsonErrorResponseUnmarshaller.GetInstance().Unmarshall(context);
            errorResponse.InnerException = innerException;
            errorResponse.StatusCode = statusCode;

            var responseBodyBytes = context.GetResponseBodyBytes();

            using (var streamCopy = new MemoryStream(responseBodyBytes))
<#
            if  (this.Config.ServiceModel.IsAwsQueryCompatible)
            {
#>
<# // Create a copy of context with headers in the response #>

            using (var contextCopy = new JsonUnmarshallerContext(streamCopy, true, context.ResponseData))
<#
            }
            else
            {
#>
            using (var contextCopy = new JsonUnmarshallerContext(streamCopy, false, null))
<#
            }
#>
            {
<#
    foreach (var exception in this.Operation.Exceptions)
    {
#>
                if (errorResponse.Code != null && errorResponse.Code.Equals("<#=exception.Code #>"))
                {
                    return <#=exception.Name#>Unmarshaller.Instance.Unmarshall(contextCopy, errorResponse);
                }
<#
    }
#>
            }
<#
            if  (this.Config.ServiceModel.IsAwsQueryCompatible)
            {
                GenerateAWSQueryCompatibleBlock();
#>
            return new <#=this.BaseException#>(errorResponse.Message, errorResponse.InnerException, errorType, errorCode, errorResponse.RequestId, errorResponse.StatusCode);
<#
            }
            else
            {
#>
            return new <#=this.BaseException#>(errorResponse.Message, errorResponse.InnerException, errorResponse.Type, errorResponse.Code, errorResponse.RequestId, errorResponse.StatusCode);
<#
            }
#>
        }

<#
    if (payload != null && payload.IsStreaming)
    {
#>
        /// <summary>
        /// Overriden to return true indicating the response contains streaming data.
        /// </summary>
        public override bool HasStreamingProperty
        {
            get
            {
                return true;
            }
        }

<#
	}
    this.AddResponseSingletonMethod();
#>
<#
    if(isEventStreamOutput)
    {
#>
        protected override bool ShouldReadEntireResponse(IWebResponseData response, bool readEntireResponse)
        {
            return false;
        }
        /// <summary>
        /// Specifies that the response should be streamed
        /// </summary>
        public override bool HasStreamingProperty => true;
<#
    }
#>
    }
}