package webtransport

import (
	
	
	
	
	
	

	
)

const sessionCloseErrorCode quic.StreamErrorCode = 0x170d7b68

type SendStream interface {
	io.Writer
	io.Closer

	StreamID() quic.StreamID
	CancelWrite(StreamErrorCode)

	SetWriteDeadline(time.Time) error
}

type ReceiveStream interface {
	io.Reader

	StreamID() quic.StreamID
	CancelRead(StreamErrorCode)

	SetReadDeadline(time.Time) error
}

type Stream interface {
	SendStream
	ReceiveStream
	SetDeadline(time.Time) error
}

type sendStream struct {
	str quic.SendStream
	// WebTransport stream header.
	// Set by the constructor, set to nil once sent out.
	// Might be initialized to nil if this sendStream is part of an incoming bidirectional stream.
	streamHdr []byte

	onClose func()

	once sync.Once
}

var _ SendStream = &sendStream{}

func newSendStream( quic.SendStream,  []byte,  func()) *sendStream {
	return &sendStream{str: , streamHdr: , onClose: }
}

func ( *sendStream) () ( error) {
	.once.Do(func() {
		if ,  := .str.Write(.streamHdr);  != nil {
			 = 
			return
		}
		.streamHdr = nil
	})
	return
}

func ( *sendStream) ( []byte) (int, error) {
	if  := .maybeSendStreamHeader();  != nil {
		return 0, 
	}
	,  := .str.Write()
	if  != nil && !isTimeoutError() {
		.onClose()
	}
	return , maybeConvertStreamError()
}

func ( *sendStream) ( StreamErrorCode) {
	.str.CancelWrite(webtransportCodeToHTTPCode())
	.onClose()
}

func ( *sendStream) () {
	.str.CancelWrite(sessionCloseErrorCode)
}

func ( *sendStream) () error {
	if  := .maybeSendStreamHeader();  != nil {
		return 
	}
	.onClose()
	return maybeConvertStreamError(.str.Close())
}

func ( *sendStream) ( time.Time) error {
	return maybeConvertStreamError(.str.SetWriteDeadline())
}

func ( *sendStream) () quic.StreamID {
	return .str.StreamID()
}

type receiveStream struct {
	str     quic.ReceiveStream
	onClose func()
}

var _ ReceiveStream = &receiveStream{}

func newReceiveStream( quic.ReceiveStream,  func()) *receiveStream {
	return &receiveStream{str: , onClose: }
}

func ( *receiveStream) ( []byte) (int, error) {
	,  := .str.Read()
	if  != nil && !isTimeoutError() {
		.onClose()
	}
	return , maybeConvertStreamError()
}

func ( *receiveStream) ( StreamErrorCode) {
	.str.CancelRead(webtransportCodeToHTTPCode())
	.onClose()
}

func ( *receiveStream) () {
	.str.CancelRead(sessionCloseErrorCode)
}

func ( *receiveStream) ( time.Time) error {
	return maybeConvertStreamError(.str.SetReadDeadline())
}

func ( *receiveStream) () quic.StreamID {
	return .str.StreamID()
}

type stream struct {
	*sendStream
	*receiveStream

	mx                             sync.Mutex
	sendSideClosed, recvSideClosed bool
	onClose                        func()
}

var _ Stream = &stream{}

func newStream( quic.Stream,  []byte,  func()) *stream {
	 := &stream{onClose: }
	.sendStream = newSendStream(, , func() { .registerClose(true) })
	.receiveStream = newReceiveStream(, func() { .registerClose(false) })
	return 
}

func ( *stream) ( bool) {
	.mx.Lock()
	if  {
		.sendSideClosed = true
	} else {
		.recvSideClosed = true
	}
	 := .sendSideClosed && .recvSideClosed
	.mx.Unlock()

	if  {
		.onClose()
	}
}

func ( *stream) () {
	.sendStream.closeWithSession()
	.receiveStream.closeWithSession()
}

func ( *stream) ( time.Time) error {
	 := .sendStream.SetWriteDeadline()
	 := .receiveStream.SetReadDeadline()
	if  != nil {
		return 
	}
	return 
}

func ( *stream) () quic.StreamID {
	return .receiveStream.StreamID()
}

func maybeConvertStreamError( error) error {
	if  == nil {
		return nil
	}
	var  *quic.StreamError
	if errors.As(, &) {
		,  := httpCodeToWebtransportCode(.ErrorCode)
		if  != nil {
			return fmt.Errorf("stream reset, but failed to convert stream error %d: %w", .ErrorCode, )
		}
		return &StreamError{
			ErrorCode: ,
			Remote:    .Remote,
		}
	}
	return 
}

func isTimeoutError( error) bool {
	,  := .(net.Error)
	if ! {
		return false
	}
	return .Timeout()
}