package checksum

import (
	
	smithyhttp 
)

// InputMiddlewareOptions provides the options for the request
// checksum middleware setup.
type InputMiddlewareOptions struct {
	// GetAlgorithm is a function to get the checksum algorithm of the
	// input payload from the input parameters.
	//
	// Given the input parameter value, the function must return the algorithm
	// and true, or false if no algorithm is specified.
	GetAlgorithm func(interface{}) (string, bool)

	// Forces the middleware to compute the input payload's checksum. The
	// request will fail if the algorithm is not specified or unable to compute
	// the checksum.
	RequireChecksum bool

	// Enables support for wrapping the serialized input payload with a
	// content-encoding: aws-check wrapper, and including a trailer for the
	// algorithm's checksum value.
	//
	// The checksum will not be computed, nor added as trailing checksum, if
	// the Algorithm's header is already set on the request.
	EnableTrailingChecksum bool

	// Enables support for computing the SHA256 checksum of input payloads
	// along with the algorithm specified checksum. Prevents downstream
	// middleware handlers (computePayloadSHA256) re-reading the payload.
	//
	// The SHA256 payload checksum will only be used for computed for requests
	// that are not TLS, or do not enable trailing checksums.
	//
	// The SHA256 payload hash will not be computed, if the Algorithm's header
	// is already set on the request.
	EnableComputeSHA256PayloadHash bool

	// Enables support for setting the aws-chunked decoded content length
	// header for the decoded length of the underlying stream. Will only be set
	// when used with trailing checksums, and aws-chunked content-encoding.
	EnableDecodedContentLengthHeader bool
}

// AddInputMiddleware adds the middleware for performing checksum computing
// of request payloads, and checksum validation of response payloads.
func ( *middleware.Stack,  InputMiddlewareOptions) ( error) {
	// TODO ensure this works correctly with presigned URLs

	// Middleware stack:
	// * (OK)(Initialize) --none--
	// * (OK)(Serialize) EndpointResolver
	// * (OK)(Build) ComputeContentLength
	// * (AD)(Build) Header ComputeInputPayloadChecksum
	//    * SIGNED Payload - If HTTP && not support trailing checksum
	//    * UNSIGNED Payload - If HTTPS && not support trailing checksum
	// * (RM)(Build) ContentChecksum - OK to remove
	// * (OK)(Build) ComputePayloadHash
	//    * v4.dynamicPayloadSigningMiddleware
	//    * v4.computePayloadSHA256
	//    * v4.unsignedPayload
	//   (OK)(Build) Set computedPayloadHash header
	// * (OK)(Finalize) Retry
	// * (AD)(Finalize) Trailer ComputeInputPayloadChecksum,
	//    * Requires HTTPS && support trailing checksum
	//    * UNSIGNED Payload
	//    * Finalize run if HTTPS && support trailing checksum
	// * (OK)(Finalize) Signing
	// * (OK)(Deserialize) --none--

	// Initial checksum configuration look up middleware
	 = .Initialize.Add(&setupInputContext{
		GetAlgorithm: .GetAlgorithm,
	}, middleware.Before)
	if  != nil {
		return 
	}

	.Build.Remove("ContentChecksum")

	// Create the compute checksum middleware that will be added as both a
	// build and finalize handler.
	 := &computeInputPayloadChecksum{
		RequireChecksum:                  .RequireChecksum,
		EnableTrailingChecksum:           .EnableTrailingChecksum,
		EnableComputePayloadHash:         .EnableComputeSHA256PayloadHash,
		EnableDecodedContentLengthHeader: .EnableDecodedContentLengthHeader,
	}

	// Insert header checksum after ComputeContentLength middleware, must also
	// be before the computePayloadHash middleware handlers.
	 = .Build.Insert(,
		(*smithyhttp.ComputeContentLength)(nil).ID(),
		middleware.After)
	if  != nil {
		return 
	}

	// If trailing checksum is not supported no need for finalize handler to be added.
	if .EnableTrailingChecksum {
		 = .Finalize.Insert(, "Retry", middleware.After)
		if  != nil {
			return 
		}
	}

	return nil
}

// RemoveInputMiddleware Removes the compute input payload checksum middleware
// handlers from the stack.
func ( *middleware.Stack) {
	 := (*setupInputContext)(nil).ID()
	.Initialize.Remove()

	 = (*computeInputPayloadChecksum)(nil).ID()
	.Build.Remove()
	.Finalize.Remove()
}

// OutputMiddlewareOptions provides options for configuring output checksum
// validation middleware.
type OutputMiddlewareOptions struct {
	// GetValidationMode is a function to get the checksum validation
	// mode of the output payload from the input parameters.
	//
	// Given the input parameter value, the function must return the validation
	// mode and true, or false if no mode is specified.
	GetValidationMode func(interface{}) (string, bool)

	// The set of checksum algorithms that should be used for response payload
	// checksum validation. The algorithm(s) used will be a union of the
	// output's returned algorithms and this set.
	//
	// Only the first algorithm in the union is currently used.
	ValidationAlgorithms []string

	// If set the middleware will ignore output multipart checksums. Otherwise
	// an checksum format error will be returned by the middleware.
	IgnoreMultipartValidation bool

	// When set the middleware will log when output does not have checksum or
	// algorithm to validate.
	LogValidationSkipped bool

	// When set the middleware will log when the output contains a multipart
	// checksum that was, skipped and not validated.
	LogMultipartValidationSkipped bool
}

// AddOutputMiddleware adds the middleware for validating response payload's
// checksum.
func ( *middleware.Stack,  OutputMiddlewareOptions) error {
	 := .Initialize.Add(&setupOutputContext{
		GetValidationMode: .GetValidationMode,
	}, middleware.Before)
	if  != nil {
		return 
	}

	// Resolve a supported priority order list of algorithms to validate.
	 := FilterSupportedAlgorithms(.ValidationAlgorithms)

	 := &validateOutputPayloadChecksum{
		Algorithms:                    ,
		IgnoreMultipartValidation:     .IgnoreMultipartValidation,
		LogMultipartValidationSkipped: .LogMultipartValidationSkipped,
		LogValidationSkipped:          .LogValidationSkipped,
	}

	return .Deserialize.Add(, middleware.After)
}

// RemoveOutputMiddleware Removes the compute input payload checksum middleware
// handlers from the stack.
func ( *middleware.Stack) {
	 := (*setupOutputContext)(nil).ID()
	.Initialize.Remove()

	 = (*validateOutputPayloadChecksum)(nil).ID()
	.Deserialize.Remove()
}