package swarm

import (
	
	
	

	
	ma 
	manet 
)

// The 250ms value is from happy eyeballs RFC 8305. This is a rough estimate of 1 RTT
const (
	// duration by which TCP dials are delayed relative to the last QUIC dial
	PublicTCPDelay  = 250 * time.Millisecond
	PrivateTCPDelay = 30 * time.Millisecond

	// duration by which QUIC dials are delayed relative to previous QUIC dial
	PublicQUICDelay  = 250 * time.Millisecond
	PrivateQUICDelay = 30 * time.Millisecond

	// RelayDelay is the duration by which relay dials are delayed relative to direct addresses
	RelayDelay = 500 * time.Millisecond

	// delay for other transport addresses. This will apply to /webrtc-direct.
	PublicOtherDelay  = 1 * time.Second
	PrivateOtherDelay = 100 * time.Millisecond
)

// NoDelayDialRanker ranks addresses with no delay. This is useful for simultaneous connect requests.
func ( []ma.Multiaddr) []network.AddrDelay {
	return getAddrDelay(, 0, 0, 0, 0)
}

// DefaultDialRanker determines the ranking of outgoing connection attempts.
//
// Addresses are grouped into three distinct groups:
//
//   - private addresses (localhost and local networks (RFC 1918))
//   - public addresses
//   - relay addresses
//
// Within each group, the addresses are ranked according to the ranking logic described below.
// We then dial addresses according to this ranking, with short timeouts applied between dial attempts.
// This ranking logic dramatically reduces the number of simultaneous dial attempts, while introducing
// no additional latency in the vast majority of cases.
//
// Private and public address groups are dialed in parallel.
// Dialing relay addresses is delayed by 500 ms, if we have any non-relay alternatives.
//
// Within each group (private, public, relay addresses) we apply the following ranking logic:
//
//  1. If both IPv6 QUIC and IPv4 QUIC addresses are present, we do a Happy Eyeballs RFC 8305 style ranking.
//     First dial the IPv6 QUIC address with the lowest port. After this we dial the IPv4 QUIC address with
//     the lowest port delayed by 250ms (PublicQUICDelay) for public addresses, and 30ms (PrivateQUICDelay)
//     for local addresses. After this we dial all the rest of the addresses delayed by 250ms (PublicQUICDelay)
//     for public addresses, and 30ms (PrivateQUICDelay) for local addresses.
//  2. If only one of QUIC IPv6 or QUIC IPv4 addresses are present, dial the QUIC address with the lowest port
//     first. After this we dial the rest of the QUIC addresses delayed by 250ms (PublicQUICDelay) for public
//     addresses, and 30ms (PrivateQUICDelay) for local addresses.
//  3. If a QUIC or WebTransport address is present, TCP addresses dials are delayed relative to the last QUIC dial:
//     We prefer to end up with a QUIC connection. For public addresses, the delay introduced is 250ms (PublicTCPDelay),
//     and for private addresses 30ms (PrivateTCPDelay).
//  4. For the TCP addresses we follow a strategy similar to QUIC with an optimisation for handling the long TCP
//     handshake time described in 6. If both IPv6 TCP and IPv4 TCP addresses are present, we do a Happy Eyeballs
//     style ranking. First dial the IPv6 TCP address with the lowest port. After this, dial the IPv4 TCP address
//     with the lowest port delayed by 250ms (PublicTCPDelay) for public addresses, and 30ms (PrivateTCPDelay)
//     for local addresses. After this we dial all the rest of the addresses delayed by 250ms (PublicTCPDelay) for
//     public addresses, and 30ms (PrivateTCPDelay) for local addresses.
//  5. If only one of TCP IPv6 or TCP IPv4 addresses are present, dial the TCP address with the lowest port
//     first. After this we dial the rest of the TCP addresses delayed by 250ms (PublicTCPDelay) for public
//     addresses, and 30ms (PrivateTCPDelay) for local addresses.
//  6. When a TCP socket is connected and awaiting security and muxer upgrade, we stop new dials for 2*PublicTCPDelay
//     to allow for the upgrade to complete.
//  7. WebRTC Direct, and other IP transport addresses are dialed 1 second after the last QUIC or TCP dial.
//     We only ever need to dial these if the peer doesn't have any other transport available, in which
//     case these are dialed immediately.
//
// We dial lowest ports first as they are more likely to be the listen port.
func ( []ma.Multiaddr) []network.AddrDelay {
	,  := filterAddrs(, isRelayAddr)
	,  := filterAddrs(, manet.IsPrivateAddr)
	,  := filterAddrs(, func( ma.Multiaddr) bool { return isProtocolAddr(, ma.P_IP4) || isProtocolAddr(, ma.P_IP6) })

	var  time.Duration
	if len() > 0 {
		// if there is a public direct address available delay relay dials
		 = RelayDelay
	}

	 := make([]network.AddrDelay, 0, len())
	 = append(, getAddrDelay(, PrivateTCPDelay, PrivateQUICDelay, PrivateOtherDelay, 0)...)
	 = append(, getAddrDelay(, PublicTCPDelay, PublicQUICDelay, PublicOtherDelay, 0)...)
	 = append(, getAddrDelay(, PublicTCPDelay, PublicQUICDelay, PublicOtherDelay, )...)
	var  time.Duration
	if len() > 0 {
		 = [len()-1].Delay
	}

	for  := 0;  < len(); ++ {
		 = append(, network.AddrDelay{Addr: [], Delay:  + PublicOtherDelay})
	}

	return 
}

// getAddrDelay ranks a group of addresses according to the ranking logic explained in
// documentation for defaultDialRanker.
// offset is used to delay all addresses by a fixed duration. This is useful for delaying all relay
// addresses relative to direct addresses.
func getAddrDelay( []ma.Multiaddr,  time.Duration,  time.Duration,
	 time.Duration,  time.Duration) []network.AddrDelay {
	if len() == 0 {
		return nil
	}

	sort.Slice(, func(,  int) bool { return score([]) < score([]) })

	// addrs is now sorted by (Transport, IPVersion). Reorder addrs for happy eyeballs dialing.
	// For QUIC and TCP, if we have both IPv6 and IPv4 addresses, move the
	// highest priority IPv4 address to the second position.
	 := false
	 := false
	// tcpStartIdx is the index of the first TCP Address
	var  int
	{
		 := 0
		// If the first QUIC address is IPv6 move the first QUIC IPv4 address to second position
		if isQUICAddr([0]) && isProtocolAddr([0], ma.P_IP6) {
			for  := 1;  < len(); ++ {
				if isQUICAddr([]) && isProtocolAddr([], ma.P_IP4) {
					// The first IPv4 address is at position j
					// Move the jth element at position 1 shifting the affected elements
					if  > 1 {
						 := []
						copy([2:], [1:])
						[1] = 
					}
					 = true
					 =  + 1
					break
				}
			}
		}

		for  = ;  < len(); ++ {
			if isProtocolAddr([], ma.P_TCP) {
				break
			}
		}

		// If the first TCP address is IPv6 move the first TCP IPv4 address to second position
		if  < len() && isProtocolAddr([], ma.P_IP6) {
			for  :=  + 1;  < len(); ++ {
				if isProtocolAddr([], ma.P_TCP) && isProtocolAddr([], ma.P_IP4) {
					// First TCP IPv4 address is at position j, move it to position tcpStartIdx+1
					// which is the second priority TCP address
					if  > +1 {
						 := []
						copy([+2:], [+1:])
						[+1] = 
					}
					 = true
					break
				}
			}
		}
	}

	 := make([]network.AddrDelay, 0, len())
	var  time.Duration
	var  time.Duration
	for ,  := range  {
		var  time.Duration
		switch {
		case isQUICAddr():
			// We dial an IPv6 address, then after quicDelay an IPv4
			// address, then after a further quicDelay we dial the rest of the addresses.
			if  == 1 {
				 = 
			}
			if  > 1 {
				// If we have happy eyeballs for QUIC, dials after the second position
				// will be delayed by 2*quicDelay
				if  {
					 = 2 * 
				} else {
					 = 
				}
			}
			 = 
			 =  + 
		case isProtocolAddr(, ma.P_TCP):
			// We dial an IPv6 address, then after tcpDelay an IPv4
			// address, then after a further tcpDelay we dial the rest of the addresses.
			if  == +1 {
				 = 
			}
			if  > +1 {
				// If we have happy eyeballs for TCP, dials after the second position
				// will be delayed by 2*tcpDelay
				if  {
					 = 2 * 
				} else {
					 = 
				}
			}
			 += 
			 = 
		// if it's neither quic, webtransport, tcp, or websocket address
		default:
			 =  + 
		}
		 = append(, network.AddrDelay{Addr: , Delay:  + })
	}
	return 
}

// score scores a multiaddress for dialing delay. Lower is better.
// The lower 16 bits of the result are the port. Low ports are ranked higher because they're
// more likely to be listen addresses.
// The addresses are ranked as:
// QUICv1 IPv6 > QUICdraft29 IPv6 > QUICv1 IPv4 > QUICdraft29 IPv4 >
// WebTransport IPv6 > WebTransport IPv4 > TCP IPv6 > TCP IPv4
func score( ma.Multiaddr) int {
	 := 0
	if isProtocolAddr(, ma.P_IP4) {
		 = 1 << 18
	}

	if ,  := .ValueForProtocol(ma.P_WEBTRANSPORT);  == nil {
		,  := .ValueForProtocol(ma.P_UDP)
		,  := strconv.Atoi()
		return  + (1 << 19) + 
	}
	if ,  := .ValueForProtocol(ma.P_QUIC);  == nil {
		,  := .ValueForProtocol(ma.P_UDP)
		,  := strconv.Atoi()
		return  +  + (1 << 17)
	}
	if ,  := .ValueForProtocol(ma.P_QUIC_V1);  == nil {
		,  := .ValueForProtocol(ma.P_UDP)
		,  := strconv.Atoi()
		return  + 
	}
	if ,  := .ValueForProtocol(ma.P_TCP);  == nil {
		,  := strconv.Atoi()
		return  +  + (1 << 20)
	}
	if ,  := .ValueForProtocol(ma.P_WEBRTC_DIRECT);  == nil {
		return 1 << 21
	}
	return (1 << 30)
}

func isProtocolAddr( ma.Multiaddr,  int) bool {
	 := false
	ma.ForEach(, func( ma.Component) bool {
		if .Protocol().Code ==  {
			 = true
			return false
		}
		return true
	})
	return 
}

func isQUICAddr( ma.Multiaddr) bool {
	return isProtocolAddr(, ma.P_QUIC) || isProtocolAddr(, ma.P_QUIC_V1)
}

// filterAddrs filters an address slice in place
func filterAddrs( []ma.Multiaddr,  func( ma.Multiaddr) bool) (,  []ma.Multiaddr) {
	 := 0
	for  := 0;  < len(); ++ {
		if ([]) {
			[], [] = [], []
			++
		}
	}
	return [:], [:]
}