package pb

import (
	"fmt"
	"github.com/seaweedfs/seaweedfs/weed/pb/master_pb"
	"github.com/seaweedfs/seaweedfs/weed/util"
	"net"
	"strconv"
	"strings"
)

type ServerAddress string
type ServerAddresses string

func NewServerAddress(host string, port int, grpcPort int) ServerAddress {
	if grpcPort == 0 || grpcPort == port+10000 {
		return ServerAddress(util.JoinHostPort(host, port))
	}
	return ServerAddress(util.JoinHostPort(host, port) + "." + strconv.Itoa(grpcPort))
}

func NewServerAddressWithGrpcPort(address string, grpcPort int) ServerAddress {
	if grpcPort == 0 {
		return ServerAddress(address)
	}
	_, port, _ := hostAndPort(address)
	if uint64(grpcPort) == port+10000 {
		return ServerAddress(address)
	}
	return ServerAddress(address + "." + strconv.Itoa(grpcPort))
}

func NewServerAddressFromDataNode(dn *master_pb.DataNodeInfo) ServerAddress {
	return NewServerAddressWithGrpcPort(dn.Id, int(dn.GrpcPort))
}

func NewServerAddressFromLocation(dn *master_pb.Location) ServerAddress {
	return NewServerAddressWithGrpcPort(dn.Url, int(dn.GrpcPort))
}

func (sa ServerAddress) String() string {
	return sa.ToHttpAddress()
}

func (sa ServerAddress) ToHttpAddress() string {
	portsSepIndex := strings.LastIndex(string(sa), ":")
	if portsSepIndex < 0 {
		return string(sa)
	}
	if portsSepIndex+1 >= len(sa) {
		return string(sa)
	}
	ports := string(sa[portsSepIndex+1:])
	sepIndex := strings.LastIndex(string(ports), ".")
	if sepIndex >= 0 {
		host := string(sa[0:portsSepIndex])
		return net.JoinHostPort(host, ports[0:sepIndex])
	}
	return string(sa)
}

func (sa ServerAddress) ToGrpcAddress() string {
	portsSepIndex := strings.LastIndex(string(sa), ":")
	if portsSepIndex < 0 {
		return string(sa)
	}
	if portsSepIndex+1 >= len(sa) {
		return string(sa)
	}
	ports := string(sa[portsSepIndex+1:])
	sepIndex := strings.LastIndex(ports, ".")
	if sepIndex >= 0 {
		host := string(sa[0:portsSepIndex])
		return net.JoinHostPort(host, ports[sepIndex+1:])
	}
	return ServerToGrpcAddress(string(sa))
}

func (sa ServerAddresses) ToAddresses() (addresses []ServerAddress) {
	parts := strings.Split(string(sa), ",")
	for _, address := range parts {
		if address != "" {
			addresses = append(addresses, ServerAddress(address))
		}
	}
	return
}

func (sa ServerAddresses) ToAddressMap() (addresses map[string]ServerAddress) {
	addresses = make(map[string]ServerAddress)
	for _, address := range sa.ToAddresses() {
		addresses[string(address)] = address
	}
	return
}

func (sa ServerAddresses) ToAddressStrings() (addresses []string) {
	parts := strings.Split(string(sa), ",")
	for _, address := range parts {
		addresses = append(addresses, address)
	}
	return
}

func ToAddressStrings(addresses []ServerAddress) []string {
	var strings []string
	for _, addr := range addresses {
		strings = append(strings, string(addr))
	}
	return strings
}
func ToAddressStringsFromMap(addresses map[string]ServerAddress) []string {
	var strings []string
	for _, addr := range addresses {
		strings = append(strings, string(addr))
	}
	return strings
}
func FromAddressStrings(strings []string) []ServerAddress {
	var addresses []ServerAddress
	for _, addr := range strings {
		addresses = append(addresses, ServerAddress(addr))
	}
	return addresses
}

func ParseUrl(input string) (address ServerAddress, path string, err error) {
	if !strings.HasPrefix(input, "http://") {
		return "", "", fmt.Errorf("url %s needs prefix 'http://'", input)
	}
	input = input[7:]
	pathSeparatorIndex := strings.Index(input, "/")
	hostAndPorts := input
	if pathSeparatorIndex > 0 {
		path = input[pathSeparatorIndex:]
		hostAndPorts = input[0:pathSeparatorIndex]
	}
	commaSeparatorIndex := strings.Index(input, ":")
	if commaSeparatorIndex < 0 {
		err = fmt.Errorf("port should be specified in %s", input)
		return
	}
	address = ServerAddress(hostAndPorts)
	return
}
