package http3

import (
	
	
	
	

	
)

const streamDatagramQueueLen = 32

// stateTrackingStream is an implementation of quic.Stream that delegates
// to an underlying stream
// it takes care of proxying send and receive errors onto an implementation of
// the errorSetter interface (intended to be occupied by a datagrammer)
// it is also responsible for clearing the stream based on its ID from its
// parent connection, this is done through the streamClearer interface when
// both the send and receive sides are closed
type stateTrackingStream struct {
	*quic.Stream

	sendDatagram func([]byte) error
	hasData      chan struct{}
	queue        [][]byte // TODO: use a ring buffer

	mx      sync.Mutex
	sendErr error
	recvErr error

	clearer streamClearer
}

var _ datagramStream = &stateTrackingStream{}

type streamClearer interface {
	clearStream(quic.StreamID)
}

func newStateTrackingStream( *quic.Stream,  streamClearer,  func([]byte) error) *stateTrackingStream {
	 := &stateTrackingStream{
		Stream:       ,
		clearer:      ,
		sendDatagram: ,
		hasData:      make(chan struct{}, 1),
	}

	context.AfterFunc(.Context(), func() {
		.closeSend(context.Cause(.Context()))
	})

	return 
}

func ( *stateTrackingStream) ( error) {
	.mx.Lock()
	defer .mx.Unlock()

	// clear the stream the first time both the send
	// and receive are finished
	if .sendErr == nil {
		if .recvErr != nil {
			.clearer.clearStream(.StreamID())
		}
		.sendErr = 
	}
}

func ( *stateTrackingStream) ( error) {
	.mx.Lock()
	defer .mx.Unlock()

	// clear the stream the first time both the send
	// and receive are finished
	if .recvErr == nil {
		if .sendErr != nil {
			.clearer.clearStream(.StreamID())
		}
		.recvErr = 
		.signalHasDatagram()
	}
}

func ( *stateTrackingStream) () error {
	.closeSend(errors.New("write on closed stream"))
	return .Stream.Close()
}

func ( *stateTrackingStream) ( quic.StreamErrorCode) {
	.closeSend(&quic.StreamError{StreamID: .StreamID(), ErrorCode: })
	.Stream.CancelWrite()
}

func ( *stateTrackingStream) ( []byte) (int, error) {
	,  := .Stream.Write()
	if  != nil && !errors.Is(, os.ErrDeadlineExceeded) {
		.closeSend()
	}
	return , 
}

func ( *stateTrackingStream) ( quic.StreamErrorCode) {
	.closeReceive(&quic.StreamError{StreamID: .StreamID(), ErrorCode: })
	.Stream.CancelRead()
}

func ( *stateTrackingStream) ( []byte) (int, error) {
	,  := .Stream.Read()
	if  != nil && !errors.Is(, os.ErrDeadlineExceeded) {
		.closeReceive()
	}
	return , 
}

func ( *stateTrackingStream) ( []byte) error {
	.mx.Lock()
	 := .sendErr
	.mx.Unlock()
	if  != nil {
		return 
	}

	return .sendDatagram()
}

func ( *stateTrackingStream) () {
	select {
	case .hasData <- struct{}{}:
	default:
	}
}

func ( *stateTrackingStream) ( []byte) {
	.mx.Lock()
	defer .mx.Unlock()

	if .recvErr != nil {
		return
	}
	if len(.queue) >= streamDatagramQueueLen {
		return
	}
	.queue = append(.queue, )
	.signalHasDatagram()
}

func ( *stateTrackingStream) ( context.Context) ([]byte, error) {
:
	.mx.Lock()
	if len(.queue) > 0 {
		 := .queue[0]
		.queue = .queue[1:]
		.mx.Unlock()
		return , nil
	}
	if  := .recvErr;  != nil {
		.mx.Unlock()
		return nil, 
	}
	.mx.Unlock()

	select {
	case <-.Done():
		return nil, context.Cause()
	case <-.hasData:
	}
	goto 
}

func ( *stateTrackingStream) () *quic.Stream {
	return .Stream
}