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

package stun

import (
	
	
	
	
)

// MappedAddress represents MAPPED-ADDRESS attribute.
//
// This attribute is used only by servers for achieving backwards
// compatibility with RFC 3489 clients.
//
// RFC 5389 Section 15.1
type MappedAddress struct {
	IP   net.IP
	Port int
}

// AlternateServer represents ALTERNATE-SERVER attribute.
//
// RFC 5389 Section 15.11
type AlternateServer struct {
	IP   net.IP
	Port int
}

// ResponseOrigin represents RESPONSE-ORIGIN attribute.
//
// RFC 5780 Section 7.3
type ResponseOrigin struct {
	IP   net.IP
	Port int
}

// OtherAddress represents OTHER-ADDRESS attribute.
//
// RFC 5780 Section 7.4
type OtherAddress struct {
	IP   net.IP
	Port int
}

// AddTo adds ALTERNATE-SERVER attribute to message.
func ( *AlternateServer) ( *Message) error {
	 := (*MappedAddress)()
	return .AddToAs(, AttrAlternateServer)
}

// GetFrom decodes ALTERNATE-SERVER from message.
func ( *AlternateServer) ( *Message) error {
	 := (*MappedAddress)()
	return .GetFromAs(, AttrAlternateServer)
}

func ( MappedAddress) () string {
	return net.JoinHostPort(.IP.String(), strconv.Itoa(.Port))
}

// GetFromAs decodes MAPPED-ADDRESS value in message m as an attribute of type t.
func ( *MappedAddress) ( *Message,  AttrType) error {
	,  := .Get()
	if  != nil {
		return 
	}
	if len() <= 4 {
		return io.ErrUnexpectedEOF
	}
	 := bin.Uint16([0:2])
	if  != familyIPv6 &&  != familyIPv4 {
		return newDecodeErr("xor-mapped address", "family",
			fmt.Sprintf("bad value %d", ),
		)
	}
	 := net.IPv4len
	if  == familyIPv6 {
		 = net.IPv6len
	}
	// Ensuring len(a.IP) == ipLen and reusing a.IP.
	if len(.IP) <  {
		.IP = .IP[:cap(.IP)]
		for len(.IP) <  {
			.IP = append(.IP, 0)
		}
	}
	.IP = .IP[:]
	for  := range .IP {
		.IP[] = 0
	}
	.Port = int(bin.Uint16([2:4]))
	copy(.IP, [4:])
	return nil
}

// AddToAs adds MAPPED-ADDRESS value to m as t attribute.
func ( *MappedAddress) ( *Message,  AttrType) error {
	var (
		 = familyIPv4
		     = .IP
	)
	if len(.IP) == net.IPv6len {
		if isIPv4() {
			 = [12:16] // like in ip.To4()
		} else {
			 = familyIPv6
		}
	} else if len() != net.IPv4len {
		return ErrBadIPLength
	}
	 := make([]byte, 128)
	[0] = 0 // first 8 bits are zeroes
	bin.PutUint16([0:2], )
	bin.PutUint16([2:4], uint16(.Port))
	copy([4:], )
	.Add(, [:4+len()])
	return nil
}

// AddTo adds MAPPED-ADDRESS to message.
func ( *MappedAddress) ( *Message) error {
	return .AddToAs(, AttrMappedAddress)
}

// GetFrom decodes MAPPED-ADDRESS from message.
func ( *MappedAddress) ( *Message) error {
	return .GetFromAs(, AttrMappedAddress)
}

// AddTo adds OTHER-ADDRESS attribute to message.
func ( *OtherAddress) ( *Message) error {
	 := (*MappedAddress)()
	return .AddToAs(, AttrOtherAddress)
}

// GetFrom decodes OTHER-ADDRESS from message.
func ( *OtherAddress) ( *Message) error {
	 := (*MappedAddress)()
	return .GetFromAs(, AttrOtherAddress)
}

func ( OtherAddress) () string {
	return net.JoinHostPort(.IP.String(), strconv.Itoa(.Port))
}

// AddTo adds RESPONSE-ORIGIN attribute to message.
func ( *ResponseOrigin) ( *Message) error {
	 := (*MappedAddress)()
	return .AddToAs(, AttrResponseOrigin)
}

// GetFrom decodes RESPONSE-ORIGIN from message.
func ( *ResponseOrigin) ( *Message) error {
	 := (*MappedAddress)()
	return .GetFromAs(, AttrResponseOrigin)
}

func ( ResponseOrigin) () string {
	return net.JoinHostPort(.IP.String(), strconv.Itoa(.Port))
}