// Package statute provides functionality for handling SOCKS5 protocol messages and addresses.
package statute

import (
	"fmt"
	"net"
	"strconv"
)

// AddrSpec is used to return the target AddrSpec
// which may be specified as IPv4, IPv6, or a FQDN
type AddrSpec struct {
	FQDN string
	IP   net.IP
	Port int
	// private stuff set when Request parsed
	AddrType byte
}

// String returns a string suitable to internet; prefer returning IP-based
// address, fallback to FQDN
func (sf *AddrSpec) String() string {
	if len(sf.IP) != 0 {
		return net.JoinHostPort(sf.IP.String(), strconv.Itoa(sf.Port))
	}
	return net.JoinHostPort(sf.FQDN, strconv.Itoa(sf.Port))
}

// Address returns a string which may be specified
// if IPv4/IPv6 will return < ip:port >
// if FQDN will return < domain ip:port >
// Note: do not used to internet, Please use String
func (sf AddrSpec) Address() string {
	if sf.FQDN != "" {
		return fmt.Sprintf("%s (%s):%d", sf.FQDN, sf.IP, sf.Port)
	}
	return fmt.Sprintf("%s:%d", sf.IP, sf.Port)
}

// ParseAddrSpec parse addr(host:port) to the AddrSpec address
func ParseAddrSpec(addr string) (as AddrSpec, err error) {
	var host, port string

	host, port, err = net.SplitHostPort(addr)
	if err != nil {
		return
	}
	as.Port, err = strconv.Atoi(port)
	if err != nil {
		return
	}

	ip := net.ParseIP(host)
	if ip4 := ip.To4(); ip4 != nil {
		as.AddrType, as.IP = ATYPIPv4, ip
	} else if ip6 := ip.To16(); ip6 != nil {
		as.AddrType, as.IP = ATYPIPv6, ip
	} else {
		as.AddrType, as.FQDN = ATYPDomain, host
	}
	return
}
