// Code generated by smithy-go-codegen DO NOT EDIT.

package awsrestjson

import (
	"context"
	"fmt"
	"github.com/aws/aws-sdk-go-v2/internal/protocoltest/awsrestjson/schemas"
	"github.com/aws/aws-sdk-go-v2/internal/protocoltest/awsrestjson/types"
	smithy "github.com/aws/smithy-go"
	"github.com/aws/smithy-go/eventstream"
	"github.com/aws/smithy-go/middleware"
	smithyhttp "github.com/aws/smithy-go/transport/http"
)

// EventStreamWriter provides the interface for writing events to a stream.
//
// The writer's Close method must allow multiple concurrent calls.
type EventStreamWriter interface {
	Send(context.Context, types.EventStream) error
	Close() error
	Err() error
}

// EventStreamReader provides the interface for reading events from a stream.
//
// The writer's Close method must allow multiple concurrent calls.
type EventStreamReader interface {
	Events() <-chan types.EventStream
	Close() error
	Err() error
}
type eventStreamWriter struct {
	writer *smithyhttp.EventStreamWriter
}

var _ EventStreamWriter = (*eventStreamWriter)(nil)

func (w *eventStreamWriter) Send(ctx context.Context, event types.EventStream) error {
	var variant *smithy.Schema
	switch event.(type) {
	case *types.EventStreamMemberBlobPayload:
		variant = schemas.EventStream_blobPayload
	case *types.EventStreamMemberHeaders:
		variant = schemas.EventStream_headers
	case *types.EventStreamMemberHeadersAndExplicitPayload:
		variant = schemas.EventStream_headersAndExplicitPayload
	case *types.EventStreamMemberHeadersAndImplicitPayload:
		variant = schemas.EventStream_headersAndImplicitPayload
	case *types.EventStreamMemberStringPayload:
		variant = schemas.EventStream_stringPayload
	case *types.EventStreamMemberStructurePayload:
		variant = schemas.EventStream_structurePayload
	case *types.EventStreamMemberUnionPayload:
		variant = schemas.EventStream_unionPayload
	default:
		return fmt.Errorf("unknown event type: %T", event)
	}
	sv, ok := event.(smithy.Serializable)
	if !ok {
		return fmt.Errorf("event %T is not serializable", event)
	}
	return w.writer.Send(ctx, variant, sv)
}

func (w *eventStreamWriter) Close() error {
	return w.writer.Close()
}

func (w *eventStreamWriter) Err() error {
	return w.writer.Err()
}

type eventStreamReader struct {
	reader *smithyhttp.EventStreamReader
	ch     chan types.EventStream
	done   chan struct{}
}

var _ EventStreamReader = (*eventStreamReader)(nil)

func newEventStreamReader(reader *smithyhttp.EventStreamReader) *eventStreamReader {
	r := &eventStreamReader{
		reader: reader,
		ch:     make(chan types.EventStream),
		done:   make(chan struct{}),
	}
	go r.pipe()
	return r
}

func (r *eventStreamReader) pipe() {
	defer close(r.ch)
	for event := range r.reader.Events() {
		var ev types.EventStream
		switch v := event.(type) {
		case *types.BlobPayloadEvent:
			ev = &types.EventStreamMemberBlobPayload{Value: *v}
		case *types.HeadersEvent:
			ev = &types.EventStreamMemberHeaders{Value: *v}
		case *types.HeadersAndExplicitPayloadEvent:
			ev = &types.EventStreamMemberHeadersAndExplicitPayload{Value: *v}
		case *types.HeadersAndImplicitPayloadEvent:
			ev = &types.EventStreamMemberHeadersAndImplicitPayload{Value: *v}
		case *types.StringPayloadEvent:
			ev = &types.EventStreamMemberStringPayload{Value: *v}
		case *types.StructurePayloadEvent:
			ev = &types.EventStreamMemberStructurePayload{Value: *v}
		case *types.UnionPayloadEvent:
			ev = &types.EventStreamMemberUnionPayload{Value: *v}
		case *eventstream.UnknownUnionMember:
			ev = &types.UnknownUnionMember{Tag: v.Tag, Value: v.Value}
		default:
			continue
		}
		select {
		case r.ch <- ev:
		case <-r.done:
			return
		}
	}
}

func (r *eventStreamReader) Events() <-chan types.EventStream {
	return r.ch
}

func (r *eventStreamReader) Close() error {
	close(r.done)
	return r.reader.Close()
}

func (r *eventStreamReader) Err() error {
	return r.reader.Err()
}

type deserializeOpEventStreamDuplexStream struct {
	options *Options
}

func (*deserializeOpEventStreamDuplexStream) ID() string {
	return "OperationEventStreamDeserializer"
}

func (m *deserializeOpEventStreamDuplexStream) HandleDeserialize(
	ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler,
) (
	middleware.DeserializeOutput, middleware.Metadata, error,
) {
	var out middleware.DeserializeOutput
	var md middleware.Metadata
	var err error

	output := &DuplexStreamOutput{}
	output.initialReply = make(chan DuplexStreamInitialReply, 1)

	inputStreamWriter := smithyhttp.GetInputStreamWriter(ctx)
	if inputStreamWriter == nil {
		return out, md, fmt.Errorf("input stream writer not found in context")
	}
	if rscheme := getResolvedAuthScheme(ctx); rscheme != nil {
		if es, ok := rscheme.Scheme.Signer().(smithyhttp.EventStreamSigner); ok {
			req, _ := in.Request.(*smithyhttp.Request)
			msgSigner, serr := es.NewMessageSigner(ctx, req, getIdentity(ctx), rscheme.SignerProperties)
			if serr != nil {
				return out, md, fmt.Errorf("event stream signer: %w", serr)
			}
			inputStreamWriter = eventstream.NewSigningWriter(inputStreamWriter, msgSigner)
		}
	}
	eventWriter := &eventStreamWriter{
		writer: smithyhttp.NewEventStreamWriter(m.options.Protocol, schemas.EventStream, inputStreamWriter),
	}
	defer func() {
		if err != nil {
			_ = eventWriter.Close()
		}
	}()
	asyncResult := make(chan deserializeResult, 1)
	asyncReader := newAsyncEventStreamReader(asyncResult)
	eventReader := newEventStreamReader(
		smithyhttp.NewEventStreamReader(m.options.Protocol, schemas.EventStream, TypeRegistry, asyncReader.pipeReader),
	)

	output.eventStream = NewDuplexStreamEventStream(func(stream *DuplexStreamEventStream) {
		stream.Writer = eventWriter
		stream.Reader = eventReader
	})

	go output.eventStream.waitStreamClose()

	prc, _ := ctx.Value(partialResultChan{}).(chan PartialResult)
	if prc != nil {
		select {
		case <-prc:
		default:
		}
		prc <- PartialResult{
			Output:   output,
			Metadata: middleware.Metadata{},
		}
	}

	out, md, err = next.HandleDeserialize(ctx, in)
	if err != nil {
		asyncResult <- deserializeResult{err: err}
		middleware.AddEventStreamOutputToMetadata(&md, output)
		return out, md, err
	}

	resp, ok := out.RawResponse.(*smithyhttp.Response)
	if !ok {
		return out, md, fmt.Errorf("unknown transport type: %T", out.RawResponse)
	}

	asyncResult <- deserializeResult{reader: resp.Body}
	middleware.AddEventStreamOutputToMetadata(&md, output)
	return out, md, nil
}

type deserializeOpEventStreamDuplexStreamWithDistinctStreams struct {
	options *Options
}

func (*deserializeOpEventStreamDuplexStreamWithDistinctStreams) ID() string {
	return "OperationEventStreamDeserializer"
}

func (m *deserializeOpEventStreamDuplexStreamWithDistinctStreams) HandleDeserialize(
	ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler,
) (
	middleware.DeserializeOutput, middleware.Metadata, error,
) {
	out, md, err := next.HandleDeserialize(ctx, in)
	if err != nil {
		return out, md, err
	}

	_, ok := out.RawResponse.(*smithyhttp.Response)
	if !ok {
		return out, md, fmt.Errorf("unknown transport type: %T", out.RawResponse)
	}

	output, ok := out.Result.(*DuplexStreamWithDistinctStreamsOutput)
	if out.Result != nil && !ok {
		return out, md, fmt.Errorf("unexpected output result type %T, expected *DuplexStreamWithDistinctStreamsOutput", out.Result)
	} else if out.Result == nil {
		output = &DuplexStreamWithDistinctStreamsOutput{}
		out.Result = output
	}

	inputStreamWriter := smithyhttp.GetInputStreamWriter(ctx)
	if inputStreamWriter == nil {
		return out, md, fmt.Errorf("input stream writer not found in context")
	}
	if rscheme := getResolvedAuthScheme(ctx); rscheme != nil {
		if es, ok := rscheme.Scheme.Signer().(smithyhttp.EventStreamSigner); ok {
			req, _ := in.Request.(*smithyhttp.Request)
			msgSigner, serr := es.NewMessageSigner(ctx, req, getIdentity(ctx), rscheme.SignerProperties)
			if serr != nil {
				return out, md, fmt.Errorf("event stream signer: %w", serr)
			}
			inputStreamWriter = eventstream.NewSigningWriter(inputStreamWriter, msgSigner)
		}
	}
	eventWriter := &eventStreamWriter{
		writer: smithyhttp.NewEventStreamWriter(m.options.Protocol, schemas.EventStream, inputStreamWriter),
	}
	defer func() {
		if err != nil {
			_ = eventWriter.Close()
		}
	}()
	if m.options.Protocol.HasInitialEventMessage() {
		opInput, ok := getOperationInput(ctx).(smithy.Serializable)
		if !ok {
			return out, md, fmt.Errorf("operation input is not serializable")
		}
		if err = m.options.Protocol.SerializeInitialRequest(schemas.DuplexStreamWithDistinctStreamsInput, opInput, inputStreamWriter); err != nil {
			return out, md, fmt.Errorf("serialize initial request: %w", err)
		}
	}

	output.eventStream = NewDuplexStreamWithDistinctStreamsEventStream(func(stream *DuplexStreamWithDistinctStreamsEventStream) {
		stream.Writer = eventWriter

	})

	go output.eventStream.waitStreamClose()

	return out, md, nil
}

type deserializeOpEventStreamDuplexStreamWithInitialMessages struct {
	options *Options
}

func (*deserializeOpEventStreamDuplexStreamWithInitialMessages) ID() string {
	return "OperationEventStreamDeserializer"
}

func (m *deserializeOpEventStreamDuplexStreamWithInitialMessages) HandleDeserialize(
	ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler,
) (
	middleware.DeserializeOutput, middleware.Metadata, error,
) {
	var out middleware.DeserializeOutput
	var md middleware.Metadata
	var err error

	output := &DuplexStreamWithInitialMessagesOutput{}
	output.initialReply = make(chan DuplexStreamWithInitialMessagesInitialReply, 1)

	inputStreamWriter := smithyhttp.GetInputStreamWriter(ctx)
	if inputStreamWriter == nil {
		return out, md, fmt.Errorf("input stream writer not found in context")
	}
	if rscheme := getResolvedAuthScheme(ctx); rscheme != nil {
		if es, ok := rscheme.Scheme.Signer().(smithyhttp.EventStreamSigner); ok {
			req, _ := in.Request.(*smithyhttp.Request)
			msgSigner, serr := es.NewMessageSigner(ctx, req, getIdentity(ctx), rscheme.SignerProperties)
			if serr != nil {
				return out, md, fmt.Errorf("event stream signer: %w", serr)
			}
			inputStreamWriter = eventstream.NewSigningWriter(inputStreamWriter, msgSigner)
		}
	}
	eventWriter := &eventStreamWriter{
		writer: smithyhttp.NewEventStreamWriter(m.options.Protocol, schemas.EventStream, inputStreamWriter),
	}
	defer func() {
		if err != nil {
			_ = eventWriter.Close()
		}
	}()
	asyncResult := make(chan deserializeResult, 1)
	asyncReader := newAsyncEventStreamReader(asyncResult)
	eventReader := newEventStreamReader(
		smithyhttp.NewEventStreamReader(m.options.Protocol, schemas.EventStream, TypeRegistry, asyncReader.pipeReader),
	)

	output.eventStream = NewDuplexStreamWithInitialMessagesEventStream(func(stream *DuplexStreamWithInitialMessagesEventStream) {
		stream.Writer = eventWriter
		stream.Reader = eventReader
	})

	go output.eventStream.waitStreamClose()

	prc, _ := ctx.Value(partialResultChan{}).(chan PartialResult)
	if prc != nil {
		select {
		case <-prc:
		default:
		}
		prc <- PartialResult{
			Output:   output,
			Metadata: middleware.Metadata{},
		}
	}

	out, md, err = next.HandleDeserialize(ctx, in)
	if err != nil {
		asyncResult <- deserializeResult{err: err}
		middleware.AddEventStreamOutputToMetadata(&md, output)
		return out, md, err
	}

	resp, ok := out.RawResponse.(*smithyhttp.Response)
	if !ok {
		return out, md, fmt.Errorf("unknown transport type: %T", out.RawResponse)
	}

	asyncResult <- deserializeResult{reader: resp.Body}
	middleware.AddEventStreamOutputToMetadata(&md, output)
	return out, md, nil
}

type deserializeOpEventStreamInputStream struct {
	options *Options
}

func (*deserializeOpEventStreamInputStream) ID() string {
	return "OperationEventStreamDeserializer"
}

func (m *deserializeOpEventStreamInputStream) HandleDeserialize(
	ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler,
) (
	middleware.DeserializeOutput, middleware.Metadata, error,
) {
	out, md, err := next.HandleDeserialize(ctx, in)
	if err != nil {
		return out, md, err
	}

	_, ok := out.RawResponse.(*smithyhttp.Response)
	if !ok {
		return out, md, fmt.Errorf("unknown transport type: %T", out.RawResponse)
	}

	output, ok := out.Result.(*InputStreamOutput)
	if out.Result != nil && !ok {
		return out, md, fmt.Errorf("unexpected output result type %T, expected *InputStreamOutput", out.Result)
	} else if out.Result == nil {
		output = &InputStreamOutput{}
		out.Result = output
	}

	inputStreamWriter := smithyhttp.GetInputStreamWriter(ctx)
	if inputStreamWriter == nil {
		return out, md, fmt.Errorf("input stream writer not found in context")
	}
	if rscheme := getResolvedAuthScheme(ctx); rscheme != nil {
		if es, ok := rscheme.Scheme.Signer().(smithyhttp.EventStreamSigner); ok {
			req, _ := in.Request.(*smithyhttp.Request)
			msgSigner, serr := es.NewMessageSigner(ctx, req, getIdentity(ctx), rscheme.SignerProperties)
			if serr != nil {
				return out, md, fmt.Errorf("event stream signer: %w", serr)
			}
			inputStreamWriter = eventstream.NewSigningWriter(inputStreamWriter, msgSigner)
		}
	}
	eventWriter := &eventStreamWriter{
		writer: smithyhttp.NewEventStreamWriter(m.options.Protocol, schemas.EventStream, inputStreamWriter),
	}
	defer func() {
		if err != nil {
			_ = eventWriter.Close()
		}
	}()
	if m.options.Protocol.HasInitialEventMessage() {
		opInput, ok := getOperationInput(ctx).(smithy.Serializable)
		if !ok {
			return out, md, fmt.Errorf("operation input is not serializable")
		}
		if err = m.options.Protocol.SerializeInitialRequest(schemas.InputStreamInput, opInput, inputStreamWriter); err != nil {
			return out, md, fmt.Errorf("serialize initial request: %w", err)
		}
	}

	output.eventStream = NewInputStreamEventStream(func(stream *InputStreamEventStream) {
		stream.Writer = eventWriter

	})

	go output.eventStream.waitStreamClose()

	return out, md, nil
}

type deserializeOpEventStreamInputStreamWithInitialRequest struct {
	options *Options
}

func (*deserializeOpEventStreamInputStreamWithInitialRequest) ID() string {
	return "OperationEventStreamDeserializer"
}

func (m *deserializeOpEventStreamInputStreamWithInitialRequest) HandleDeserialize(
	ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler,
) (
	middleware.DeserializeOutput, middleware.Metadata, error,
) {
	out, md, err := next.HandleDeserialize(ctx, in)
	if err != nil {
		return out, md, err
	}

	_, ok := out.RawResponse.(*smithyhttp.Response)
	if !ok {
		return out, md, fmt.Errorf("unknown transport type: %T", out.RawResponse)
	}

	output, ok := out.Result.(*InputStreamWithInitialRequestOutput)
	if out.Result != nil && !ok {
		return out, md, fmt.Errorf("unexpected output result type %T, expected *InputStreamWithInitialRequestOutput", out.Result)
	} else if out.Result == nil {
		output = &InputStreamWithInitialRequestOutput{}
		out.Result = output
	}

	inputStreamWriter := smithyhttp.GetInputStreamWriter(ctx)
	if inputStreamWriter == nil {
		return out, md, fmt.Errorf("input stream writer not found in context")
	}
	if rscheme := getResolvedAuthScheme(ctx); rscheme != nil {
		if es, ok := rscheme.Scheme.Signer().(smithyhttp.EventStreamSigner); ok {
			req, _ := in.Request.(*smithyhttp.Request)
			msgSigner, serr := es.NewMessageSigner(ctx, req, getIdentity(ctx), rscheme.SignerProperties)
			if serr != nil {
				return out, md, fmt.Errorf("event stream signer: %w", serr)
			}
			inputStreamWriter = eventstream.NewSigningWriter(inputStreamWriter, msgSigner)
		}
	}
	eventWriter := &eventStreamWriter{
		writer: smithyhttp.NewEventStreamWriter(m.options.Protocol, schemas.EventStream, inputStreamWriter),
	}
	defer func() {
		if err != nil {
			_ = eventWriter.Close()
		}
	}()
	if m.options.Protocol.HasInitialEventMessage() {
		opInput, ok := getOperationInput(ctx).(smithy.Serializable)
		if !ok {
			return out, md, fmt.Errorf("operation input is not serializable")
		}
		if err = m.options.Protocol.SerializeInitialRequest(schemas.InputStreamWithInitialRequestInput, opInput, inputStreamWriter); err != nil {
			return out, md, fmt.Errorf("serialize initial request: %w", err)
		}
	}

	output.eventStream = NewInputStreamWithInitialRequestEventStream(func(stream *InputStreamWithInitialRequestEventStream) {
		stream.Writer = eventWriter

	})

	go output.eventStream.waitStreamClose()

	return out, md, nil
}

type deserializeOpEventStreamOutputStream struct {
	options *Options
}

func (*deserializeOpEventStreamOutputStream) ID() string {
	return "OperationEventStreamDeserializer"
}

func (m *deserializeOpEventStreamOutputStream) HandleDeserialize(
	ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler,
) (
	middleware.DeserializeOutput, middleware.Metadata, error,
) {
	var out middleware.DeserializeOutput
	var md middleware.Metadata
	var err error

	output := &OutputStreamOutput{}
	output.initialReply = make(chan OutputStreamInitialReply, 1)

	asyncResult := make(chan deserializeResult, 1)
	asyncReader := newAsyncEventStreamReader(asyncResult)
	eventReader := newEventStreamReader(
		smithyhttp.NewEventStreamReader(m.options.Protocol, schemas.EventStream, TypeRegistry, asyncReader.pipeReader),
	)

	output.eventStream = NewOutputStreamEventStream(func(stream *OutputStreamEventStream) {

		stream.Reader = eventReader
	})

	go output.eventStream.waitStreamClose()

	prc, _ := ctx.Value(partialResultChan{}).(chan PartialResult)
	if prc != nil {
		select {
		case <-prc:
		default:
		}
		prc <- PartialResult{
			Output:   output,
			Metadata: middleware.Metadata{},
		}
	}

	out, md, err = next.HandleDeserialize(ctx, in)
	if err != nil {
		asyncResult <- deserializeResult{err: err}
		middleware.AddEventStreamOutputToMetadata(&md, output)
		return out, md, err
	}

	resp, ok := out.RawResponse.(*smithyhttp.Response)
	if !ok {
		return out, md, fmt.Errorf("unknown transport type: %T", out.RawResponse)
	}

	asyncResult <- deserializeResult{reader: resp.Body}
	middleware.AddEventStreamOutputToMetadata(&md, output)
	return out, md, nil
}

type deserializeOpEventStreamOutputStreamWithInitialResponse struct {
	options *Options
}

func (*deserializeOpEventStreamOutputStreamWithInitialResponse) ID() string {
	return "OperationEventStreamDeserializer"
}

func (m *deserializeOpEventStreamOutputStreamWithInitialResponse) HandleDeserialize(
	ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler,
) (
	middleware.DeserializeOutput, middleware.Metadata, error,
) {
	var out middleware.DeserializeOutput
	var md middleware.Metadata
	var err error

	output := &OutputStreamWithInitialResponseOutput{}
	output.initialReply = make(chan OutputStreamWithInitialResponseInitialReply, 1)

	asyncResult := make(chan deserializeResult, 1)
	asyncReader := newAsyncEventStreamReader(asyncResult)
	eventReader := newEventStreamReader(
		smithyhttp.NewEventStreamReader(m.options.Protocol, schemas.EventStream, TypeRegistry, asyncReader.pipeReader),
	)

	output.eventStream = NewOutputStreamWithInitialResponseEventStream(func(stream *OutputStreamWithInitialResponseEventStream) {

		stream.Reader = eventReader
	})

	go output.eventStream.waitStreamClose()

	prc, _ := ctx.Value(partialResultChan{}).(chan PartialResult)
	if prc != nil {
		select {
		case <-prc:
		default:
		}
		prc <- PartialResult{
			Output:   output,
			Metadata: middleware.Metadata{},
		}
	}

	out, md, err = next.HandleDeserialize(ctx, in)
	if err != nil {
		asyncResult <- deserializeResult{err: err}
		middleware.AddEventStreamOutputToMetadata(&md, output)
		return out, md, err
	}

	resp, ok := out.RawResponse.(*smithyhttp.Response)
	if !ok {
		return out, md, fmt.Errorf("unknown transport type: %T", out.RawResponse)
	}

	asyncResult <- deserializeResult{reader: resp.Body}
	middleware.AddEventStreamOutputToMetadata(&md, output)
	return out, md, nil
}
