package manet

import (
	
	
	
	
	
	

	ma 
)

var errIncorrectNetAddr = fmt.Errorf("incorrect network addr conversion")
var errNotIP = fmt.Errorf("multiaddr does not start with an IP address")

// FromNetAddr converts a net.Addr type to a Multiaddr.
func ( net.Addr) (ma.Multiaddr, error) {
	return defaultCodecs.FromNetAddr()
}

// FromNetAddr converts a net.Addr to Multiaddress.
func ( *CodecMap) ( net.Addr) (ma.Multiaddr, error) {
	if  == nil {
		return nil, fmt.Errorf("nil multiaddr")
	}
	,  := .getAddrParser(.Network())
	if  != nil {
		return nil, 
	}

	return ()
}

// ToNetAddr converts a Multiaddr to a net.Addr
// Must be ThinWaist. acceptable protocol stacks are:
// /ip{4,6}/{tcp, udp}
func ( ma.Multiaddr) (net.Addr, error) {
	return defaultCodecs.ToNetAddr()
}

// ToNetAddr converts a Multiaddress to a standard net.Addr.
func ( *CodecMap) ( ma.Multiaddr) (net.Addr, error) {
	 := .Protocols()
	 := [len()-1]

	,  := .getMaddrParser(.Name)
	if  != nil {
		return nil, 
	}

	return ()
}

// MultiaddrToIPNet converts a multiaddr to an IPNet. Useful for seeing if another IP address is contained within this multiaddr network+mask
func ( ma.Multiaddr) (*net.IPNet, error) {
	var  string
	var  string

	for ,  := range  {
		if .Protocol().Code == ma.P_IP4 || .Protocol().Code == ma.P_IP6 {
			 = .Value()
		}
		if .Protocol().Code == ma.P_IPCIDR {
			 = .Value()
		}
		if  != "" &&  != "" {
			break
		}
	}

	if  == "" {
		return nil, errors.New("no ip protocol found")
	}

	if  == "" {
		return nil, errors.New("no mask found")
	}

	, ,  := net.ParseCIDR( + "/" + string())
	return , 
}

func parseBasicNetMaddr( ma.Multiaddr) (net.Addr, error) {
	, ,  := DialArgs()
	if  != nil {
		return nil, 
	}

	switch  {
	case "tcp", "tcp4", "tcp6":
		return net.ResolveTCPAddr(, )
	case "udp", "udp4", "udp6":
		return net.ResolveUDPAddr(, )
	case "ip", "ip4", "ip6":
		return net.ResolveIPAddr(, )
	case "unix":
		return net.ResolveUnixAddr(, )
	}

	return nil, fmt.Errorf("network not supported: %s", )
}

func ( net.IP,  string) (ma.Multiaddr, error) {
	switch {
	case .To4() != nil:
		,  := ma.NewComponent("ip4", .String())
		if  != nil {
			return nil, 
		}
		return .Multiaddr(), nil
	case .To16() != nil:
		,  := ma.NewComponent("ip6", .String())
		if  != nil {
			return nil, 
		}
		 := .Multiaddr()
		if  == "" {
			return , nil
		} else {
			,  := ma.NewComponent("ip6zone", )
			if  != nil {
				return nil, 
			}
			return .Encapsulate(), nil
		}
	default:
		return nil, errIncorrectNetAddr
	}
}

// FromIP converts a net.IP type to a Multiaddr.
func ( net.IP) (ma.Multiaddr, error) {
	return FromIPAndZone(, "")
}

// ToIP converts a Multiaddr to a net.IP when possible
func ( ma.Multiaddr) (net.IP, error) {
	var  net.IP
	for ,  := range  {
		switch .Protocol().Code {
		case ma.P_IP6ZONE:
			// we can't return these anyways.
			continue
		case ma.P_IP6, ma.P_IP4:
			 = net.IP(.RawValue())
			return , nil
		}
		return nil, errNotIP
	}
	return nil, errNotIP
}

// DialArgs is a convenience function that returns network and address as
// expected by net.Dial. See https://godoc.org/net#Dial for an overview of
// possible return values (we do not support the unixpacket ones yet). Unix
// addresses do not, at present, compose.
func ( ma.Multiaddr) (string, string, error) {
	, , , , ,  := dialArgComponents()
	if  != nil {
		return "", "", 
	}

	// If we have a hostname (dns*), we don't want any fancy ipv6 formatting
	// logic (zone, brackets, etc.).
	if  {
		switch  {
		case "ip", "ip4", "ip6":
			return , , nil
		case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6":
			return ,  + ":" + , nil
		}
		// Hostname is only true when network is one of the above.
		return "", "", errors.New("no hostname") // should be unreachable
	}

	switch  {
	case "ip6":
		if  != "" {
			 += "%" + 
		}
		fallthrough
	case "ip4":
		return , , nil
	case "tcp4", "udp4":
		return ,  + ":" + , nil
	case "tcp6", "udp6":
		if  != "" {
			 += "%" + 
		}
		return , "[" +  + "]" + ":" + , nil
	case "unix":
		if runtime.GOOS == "windows" {
			// convert /c:/... to c:\...
			 = filepath.FromSlash(strings.TrimLeft(, "/"))
		}
		return , , nil
	default:
		return "", "", fmt.Errorf("%s is not a 'thin waist' address", )
	}
}

// dialArgComponents extracts the raw pieces used in dialing a Multiaddr
func dialArgComponents( ma.Multiaddr) (, , ,  string,  bool,  error) {
	for ,  := range  {
		switch  {
		case "":
			switch .Protocol().Code {
			case ma.P_IP6ZONE:
				if  != "" {
					 = fmt.Errorf("%s has multiple zones", )
					return
				}
				 = .Value()
				continue
			case ma.P_IP6:
				 = "ip6"
				 = .Value()
				continue
			case ma.P_IP4:
				if  != "" {
					 = fmt.Errorf("%s has ip4 with zone", )
					return
				}
				 = "ip4"
				 = .Value()
				continue
			case ma.P_DNS:
				 = "ip"
				 = true
				 = .Value()
				continue
			case ma.P_DNS4:
				 = "ip4"
				 = true
				 = .Value()
				continue
			case ma.P_DNS6:
				 = "ip6"
				 = true
				 = .Value()
				continue
			case ma.P_UNIX:
				 = "unix"
				 = .Value()
				return
			}
		case "ip":
			switch .Protocol().Code {
			case ma.P_UDP:
				 = "udp"
			case ma.P_TCP:
				 = "tcp"
			default:
				return
			}
			 = .Value()
		case "ip4":
			switch .Protocol().Code {
			case ma.P_UDP:
				 = "udp4"
			case ma.P_TCP:
				 = "tcp4"
			default:
				return
			}
			 = .Value()
		case "ip6":
			switch .Protocol().Code {
			case ma.P_UDP:
				 = "udp6"
			case ma.P_TCP:
				 = "tcp6"
			default:
				return
			}
			 = .Value()
		}
		// Done.
		return
	}
	return
}

func parseTCPNetAddr( net.Addr) (ma.Multiaddr, error) {
	,  := .(*net.TCPAddr)
	if ! {
		return nil, errIncorrectNetAddr
	}

	// Get IP Addr
	,  := FromIPAndZone(.IP, .Zone)
	if  != nil {
		return nil, errIncorrectNetAddr
	}

	// Get TCP Addr
	,  := ma.NewMultiaddr(fmt.Sprintf("/tcp/%d", .Port))
	if  != nil {
		return nil, errIncorrectNetAddr
	}

	// Encapsulate
	return .Encapsulate(), nil
}

func parseUDPNetAddr( net.Addr) (ma.Multiaddr, error) {
	,  := .(*net.UDPAddr)
	if ! {
		return nil, errIncorrectNetAddr
	}

	// Get IP Addr
	,  := FromIPAndZone(.IP, .Zone)
	if  != nil {
		return nil, errIncorrectNetAddr
	}

	// Get UDP Addr
	,  := ma.NewMultiaddr(fmt.Sprintf("/udp/%d", .Port))
	if  != nil {
		return nil, errIncorrectNetAddr
	}

	// Encapsulate
	return .Encapsulate(), nil
}

func parseIPNetAddr( net.Addr) (ma.Multiaddr, error) {
	,  := .(*net.IPAddr)
	if ! {
		return nil, errIncorrectNetAddr
	}
	return FromIPAndZone(.IP, .Zone)
}

func parseIPPlusNetAddr( net.Addr) (ma.Multiaddr, error) {
	,  := .(*net.IPNet)
	if ! {
		return nil, errIncorrectNetAddr
	}
	return FromIP(.IP)
}

func parseUnixNetAddr( net.Addr) (ma.Multiaddr, error) {
	,  := .(*net.UnixAddr)
	if ! {
		return nil, errIncorrectNetAddr
	}

	 := .Name
	if runtime.GOOS == "windows" {
		// Convert c:\foobar\... to c:/foobar/...
		 = filepath.ToSlash()
	}
	if len() == 0 || [0] != '/' {
		// convert "" and "c:/..." to "/..."
		 = "/" + 
	}

	,  := ma.NewComponent("unix", )
	if  != nil {
		return nil, 
	}
	return .Multiaddr(), nil
}