package multiaddr

import (
	
	
	
	
	
	

	
)

var errNilPtr = errors.New("nil ptr")

// Multiaddr is the data structure representing a Multiaddr
type Multiaddr []Component

func ( Multiaddr) () Multiaddr {
	if  == nil {
		return nil
	}
	 := make(Multiaddr, len())
	copy(, )
	return 
}

// NewMultiaddr parses and validates an input string, returning a *Multiaddr
func ( string) ( Multiaddr,  error) {
	defer func() {
		if  := recover();  != nil {
			log.Printf("Panic in NewMultiaddr on input %q: %s", , )
			 = fmt.Errorf("%v", )
		}
	}()
	,  := stringToBytes()
	if  != nil {
		return nil, 
	}
	return NewMultiaddrBytes()
}

// NewMultiaddrBytes initializes a Multiaddr from a byte representation.
// It validates it as an input string.
func ( []byte) ( Multiaddr,  error) {
	defer func() {
		if  := recover();  != nil {
			log.Printf("Panic in NewMultiaddrBytes on input %q: %s", , )
			 = fmt.Errorf("%v", )
		}
	}()
	, ,  := readMultiaddr()
	if  != nil {
		return nil, 
	}
	if  != len() {
		return nil, fmt.Errorf("unexpected extra data. %v bytes leftover", len()-)
	}
	if len() == 0 {
		return nil, nil
	}
	return , nil
}

// Equal tests whether two multiaddrs are equal
func ( Multiaddr) ( Multiaddr) bool {
	if len() != len() {
		return false
	}
	for ,  := range  {
		if !.Equal(&[]) {
			return false
		}
	}
	return true
}

func ( Multiaddr) ( Multiaddr) int {
	for  := 0;  < len() &&  < len(); ++ {
		if  := [].Compare(&[]);  != 0 {
			return 
		}
	}
	return cmp.Compare(len(), len())
}

// Bytes returns the []byte representation of this Multiaddr
func ( Multiaddr) () []byte {
	 := 0
	for ,  := range  {
		 += len(.bytes)
	}

	 := make([]byte, 0, )
	for ,  := range  {
		 = append(, .bytes...)
	}

	return 
}

// String returns the string representation of a Multiaddr
func ( Multiaddr) () string {
	var  strings.Builder

	for ,  := range  {
		.writeTo(&)
	}
	return .String()
}

func ( Multiaddr) () ([]byte, error) {
	return .Bytes(), nil
}

func ( *Multiaddr) ( []byte) error {
	if  == nil {
		return errNilPtr
	}
	,  := NewMultiaddrBytes()
	if  != nil {
		return 
	}
	* = 
	return nil
}

func ( Multiaddr) () ([]byte, error) {
	return []byte(.String()), nil
}

func ( *Multiaddr) ( []byte) error {
	if  == nil {
		return errNilPtr
	}

	,  := NewMultiaddr(string())
	if  != nil {
		return 
	}
	* = 
	return nil
}

func ( Multiaddr) () ([]byte, error) {
	return json.Marshal(.String())
}

func ( *Multiaddr) ( []byte) error {
	if  == nil {
		return errNilPtr
	}
	var  string
	if  := json.Unmarshal(, &);  != nil {
		return 
	}
	,  := NewMultiaddr()
	* = 
	return 
}

// Protocols returns the list of protocols this Multiaddr has.
// will panic in case we access bytes incorrectly.
func ( Multiaddr) () []Protocol {
	 := make([]Protocol, 0, len())
	for ,  := range  {
		 = append(, .Protocol())
	}
	return 
}

type Multiaddrer interface {
	// Multiaddr returns the Multiaddr representation
	Multiaddr() Multiaddr
}

func ( Multiaddr) () Multiaddr {
	return 
}

// AppendComponent is the same as using `append(m, *c)`, but with a safety check
// for a nil Component.
func ( Multiaddr) ( ...*Component) Multiaddr {
	for ,  := range  {
		if  == nil {
			continue
		}
		 = append(, *)
	}
	return 
}

// Encapsulate wraps a given Multiaddr, returning the resulting joined Multiaddr
func ( Multiaddr) ( Multiaddrer) Multiaddr {
	return Join(, )
}

// Decapsulate unwraps Multiaddr up until the given Multiaddr is found.
func ( Multiaddr) ( Multiaddrer) Multiaddr {
	if  == nil {
		return 
	}
	 := .Multiaddr()
	 := 

	 := -1
	for  := range  {
		 := false
		for ,  := range  {
			if len() <= + {
				 = false
				break
			}

			 = .Equal(&[+])
			if ! {
				break
			}
		}

		if  {
			 = 
		}
	}

	if  == 0 {
		return nil
	}

	if  < 0 {
		return 
	}
	return [:]
}

var ErrProtocolNotFound = fmt.Errorf("protocol not found in multiaddr")

func ( Multiaddr) ( int) ( string,  error) {
	for ,  := range  {
		if .Protocol().Code ==  {
			return .Value(), nil
		}
	}
	return "", ErrProtocolNotFound
}

// FilterAddrs is a filter that removes certain addresses, according to the given filters.
// If all filters return true, the address is kept.
func ( []Multiaddr,  ...func(Multiaddr) bool) []Multiaddr {
	 := make([]Multiaddr, 0, len())
:
	for ,  := range  {
		for ,  := range  {
			if !() {
				continue 
			}
		}
		 = append(, )
	}
	return 
}

// Contains reports whether addr is contained in addrs.
func ( []Multiaddr,  Multiaddr) bool {
	for ,  := range  {
		if .Equal() {
			return true
		}
	}
	return false
}

// Unique deduplicates addresses in place, leave only unique addresses.
// It doesn't allocate.
func ( []Multiaddr) []Multiaddr {
	if len() == 0 {
		return 
	}
	// Use the new slices package here, as the sort function doesn't allocate (sort.Slice does).
	slices.SortFunc(, func(,  Multiaddr) int { return .Compare() })
	 := 1
	for  := 1;  < len(); ++ {
		if ![-1].Equal([]) {
			[] = []
			++
		}
	}
	for  := ;  < len(); ++ {
		[] = nil
	}
	return [:]
}