package webtransport

import (
	
	
	
	
	
	
	
	

	
	
	
)

var errNoWebTransport = errors.New("server didn't enable WebTransport")

type Dialer struct {
	// TLSClientConfig is the TLS client config used when dialing the QUIC connection.
	// It must set the h3 ALPN.
	TLSClientConfig *tls.Config

	// QUICConfig is the QUIC config used when dialing the QUIC connection.
	QUICConfig *quic.Config

	// StreamReorderingTime is the time an incoming WebTransport stream that cannot be associated
	// with a session is buffered.
	// This can happen if the response to a CONNECT request (that creates a new session) is reordered,
	// and arrives after the first WebTransport stream(s) for that session.
	// Defaults to 5 seconds.
	StreamReorderingTimeout time.Duration

	// DialAddr is the function used to dial the underlying QUIC connection.
	// If unset, quic.DialAddrEarly will be used.
	DialAddr func(ctx context.Context, addr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlyConnection, error)

	ctx       context.Context
	ctxCancel context.CancelFunc

	initOnce sync.Once

	conns sessionManager
}

func ( *Dialer) () {
	 := .StreamReorderingTimeout
	if  == 0 {
		 = 5 * time.Second
	}
	.conns = *newSessionManager()
	.ctx, .ctxCancel = context.WithCancel(context.Background())
}

func ( *Dialer) ( context.Context,  string,  http.Header) (*http.Response, *Session, error) {
	.initOnce.Do(func() { .init() })

	// Technically, this is not true. DATAGRAMs could be sent using the Capsule protocol.
	// However, quic-go currently enforces QUIC datagram support if HTTP/3 datagrams are enabled.
	 := .QUICConfig
	if  == nil {
		 = &quic.Config{EnableDatagrams: true}
	} else if !.QUICConfig.EnableDatagrams {
		return nil, nil, errors.New("webtransport: DATAGRAM support required, enable it via QUICConfig.EnableDatagrams")
	}

	 := .TLSClientConfig
	if  == nil {
		 = &tls.Config{}
	} else {
		 = .Clone()
	}
	if len(.NextProtos) == 0 {
		.NextProtos = []string{http3.NextProtoH3}
	}

	,  := url.Parse()
	if  != nil {
		return nil, nil, 
	}
	if  == nil {
		 = http.Header{}
	}
	.Set(webTransportDraftOfferHeaderKey, "1")
	 := &http.Request{
		Method: http.MethodConnect,
		Header: ,
		Proto:  "webtransport",
		Host:   .Host,
		URL:    ,
	}
	 = .WithContext()

	 := .DialAddr
	if  == nil {
		 = quic.DialAddrEarly
	}
	,  := (, .Host, , )
	if  != nil {
		return nil, nil, 
	}
	 := &http3.Transport{
		EnableDatagrams: true,
		StreamHijacker: func( http3.FrameType,  quic.ConnectionTracingID,  quic.Stream,  error) ( bool,  error) {
			if isWebTransportError() {
				return true, nil
			}
			if  != webTransportFrameType {
				return false, nil
			}
			,  := quicvarint.Read(quicvarint.NewReader())
			if  != nil {
				if isWebTransportError() {
					return true, nil
				}
				return false, 
			}
			.conns.AddStream(, , sessionID())
			return true, nil
		},
		UniStreamHijacker: func( http3.StreamType,  quic.ConnectionTracingID,  quic.ReceiveStream,  error) ( bool) {
			if  != webTransportUniStreamType && !isWebTransportError() {
				return false
			}
			.conns.AddUniStream(, )
			return true
		},
	}

	 := .NewClientConn()
	select {
	case <-.ReceivedSettings():
	case <-.ctx.Done():
		return nil, nil, context.Cause(.ctx)
	}
	 := .Settings()
	if !.EnableExtendedConnect {
		return nil, nil, errors.New("server didn't enable Extended CONNECT")
	}
	if !.EnableDatagrams {
		return nil, nil, errors.New("server didn't enable HTTP/3 datagram support")
	}
	if .Other == nil {
		return nil, nil, errNoWebTransport
	}
	,  := .Other[settingsEnableWebtransport]
	if ! ||  != 1 {
		return nil, nil, errNoWebTransport
	}

	,  := .OpenRequestStream() // TODO: put this on the Connection (maybe introduce a ClientConnection?)
	if  != nil {
		return nil, nil, 
	}
	if  := .SendRequestHeader();  != nil {
		return nil, nil, 
	}
	// TODO(#136): create the session to allow optimistic opening of streams and sending of datagrams
	,  := .ReadResponse()
	if  != nil {
		return nil, nil, 
	}
	if .StatusCode < 200 || .StatusCode >= 300 {
		return , nil, fmt.Errorf("received status %d", .StatusCode)
	}
	return , .conns.AddSession(, sessionID(.StreamID()), ), nil
}

func ( *Dialer) () error {
	.ctxCancel()
	return nil
}