// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT

package ice

import (
	
	
	
)

func addrWithOptionalZone( netip.Addr,  string) netip.Addr {
	if  == "" {
		return 
	}
	if .Is6() && (.IsLinkLocalUnicast() || .IsLinkLocalMulticast()) {
		return .WithZone()
	}

	return 
}

// parseAddrFromIface should only be used when it's known the address belongs to that interface.
// e.g. it's LocalAddress on a listener.
func parseAddrFromIface( net.Addr,  string) (netip.Addr, int, NetworkType, error) {
	, , ,  := parseAddr()
	if  != nil {
		return netip.Addr{}, 0, 0, 
	}
	if ,  := .(*net.IPNet);  {
		// net.IPNet does not have a Zone but we provide it from the interface
		 = addrWithOptionalZone(, )
	}

	return , , , nil
}

func parseAddr( net.Addr) (netip.Addr, int, NetworkType, error) { //nolint:cyclop
	switch addr := .(type) {
	case *net.IPNet:
		,  := ipAddrToNetIP(.IP, "")
		if  != nil {
			return netip.Addr{}, 0, 0, 
		}

		return , 0, 0, nil
	case *net.IPAddr:
		,  := ipAddrToNetIP(.IP, .Zone)
		if  != nil {
			return netip.Addr{}, 0, 0, 
		}

		return , 0, 0, nil
	case *net.UDPAddr:
		,  := ipAddrToNetIP(.IP, .Zone)
		if  != nil {
			return netip.Addr{}, 0, 0, 
		}
		var  NetworkType
		if .Is4() {
			 = NetworkTypeUDP4
		} else {
			 = NetworkTypeUDP6
		}

		return , .Port, , nil
	case *net.TCPAddr:
		,  := ipAddrToNetIP(.IP, .Zone)
		if  != nil {
			return netip.Addr{}, 0, 0, 
		}
		var  NetworkType
		if .Is4() {
			 = NetworkTypeTCP4
		} else {
			 = NetworkTypeTCP6
		}

		return , .Port, , nil
	default:
		return netip.Addr{}, 0, 0, addrParseError{}
	}
}

type addrParseError struct {
	addr net.Addr
}

func ( addrParseError) () string {
	return fmt.Sprintf("do not know how to parse address type %T", .addr)
}

type ipConvertError struct {
	ip []byte
}

func ( ipConvertError) () string {
	return fmt.Sprintf("failed to convert IP '%s' to netip.Addr", .ip)
}

func ipAddrToNetIP( []byte,  string) (netip.Addr, error) {
	,  := netip.AddrFromSlice()
	if ! {
		return netip.Addr{}, ipConvertError{}
	}
	// we'd rather have an IPv4-mapped IPv6 become IPv4 so that it is usable.
	 = .Unmap()
	 = addrWithOptionalZone(, )

	return , nil
}

func createAddr( NetworkType,  netip.Addr,  int) net.Addr {
	switch {
	case .IsTCP():
		return &net.TCPAddr{IP: .AsSlice(), Port: , Zone: .Zone()}
	default:
		return &net.UDPAddr{IP: .AsSlice(), Port: , Zone: .Zone()}
	}
}

func addrEqual(,  net.Addr) bool {
	, , ,  := parseAddr()
	if  != nil {
		return false
	}

	, , ,  := parseAddr()
	if  != nil {
		return false
	}

	return  ==  && .Compare() == 0 &&  == 
}

// AddrPort is  an IP and a port number.
type AddrPort [18]byte

func toAddrPort( net.Addr) AddrPort {
	var  AddrPort
	switch addr := .(type) {
	case *net.UDPAddr:
		copy([:16], .IP.To16())
		[16] = uint8(.Port >> 8) //nolint:gosec // G115  false positive
		[17] = uint8(.Port)      //nolint:gosec // G115  false positive
	case *net.TCPAddr:
		copy([:16], .IP.To16())
		[16] = uint8(.Port >> 8) //nolint:gosec // G115 false positive
		[17] = uint8(.Port)      //nolint:gosec // G115 false positive
	}

	return 
}