package tls

import (
	
	
	
)

type Roller struct {
	HelloIDs            []ClientHelloID
	HelloIDMu           sync.Mutex
	WorkingHelloID      *ClientHelloID
	TcpDialTimeout      time.Duration
	TlsHandshakeTimeout time.Duration
	r                   *prng
}

// NewRoller creates Roller object with default range of HelloIDs to cycle through until a
// working/unblocked one is found.
func () (*Roller, error) {
	,  := newPRNG()
	if  != nil {
		return nil, 
	}

	 := .Intn(14)
	 = 7 + 

	 := .Intn(20)
	 = 11 + 

	return &Roller{
		HelloIDs: []ClientHelloID{
			HelloChrome_Auto,
			HelloFirefox_Auto,
			HelloIOS_Auto,
			HelloRandomized,
		},
		TcpDialTimeout:      time.Second * time.Duration(),
		TlsHandshakeTimeout: time.Second * time.Duration(),
		r:                   ,
	}, nil
}

// Dial attempts to establish connection to given address using different HelloIDs.
// If a working HelloID is found, it is used again for subsequent Dials.
// If tcp connection fails or all HelloIDs are tried, returns with last error.
//
// Usage examples:
//    Dial("tcp4", "google.com:443", "google.com")
//    Dial("tcp", "10.23.144.22:443", "mywebserver.org")
func ( *Roller) (, ,  string) (*UConn, error) {
	 := make([]ClientHelloID, len(.HelloIDs))
	copy(, .HelloIDs)
	.r.rand.Shuffle(len(.HelloIDs), func(,  int) {
		[], [] = [], []
	})

	.HelloIDMu.Lock()
	 := .WorkingHelloID // keep using same helloID, if it works
	.HelloIDMu.Unlock()
	if  != nil {
		 := false
		for ,  := range  {
			if  == * {
				[] = [0]
				[0] = * // push working hello ID first
				 = true
				break
			}
		}
		if ! {
			 = append([]ClientHelloID{*}, ...)
		}
	}

	var  net.Conn
	var  error
	for ,  := range  {
		,  = net.DialTimeout(, , .TcpDialTimeout)
		if  != nil {
			return nil,  // on tcp Dial failure return with error right away
		}

		 := UClient(, nil, )
		.SetSNI()
		.SetDeadline(time.Now().Add(.TlsHandshakeTimeout))
		 = .Handshake()
		.SetDeadline(time.Time{}) // unset timeout
		if  != nil {
			continue // on tls Dial error keep trying HelloIDs
		}

		.HelloIDMu.Lock()
		.WorkingHelloID = &.ClientHelloID
		.HelloIDMu.Unlock()
		return , 
	}
	return nil, 
}