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

package vnet

import (
	
	
	
)

var (
	errAddressAlreadyInUse       = errors.New("address already in use")
	errNoSuchUDPConn             = errors.New("no such UDPConn")
	errCannotRemoveUnspecifiedIP = errors.New("cannot remove unspecified IP by the specified IP")
)

type udpConnMap struct {
	portMap map[int][]*UDPConn
	mutex   sync.RWMutex
}

func newUDPConnMap() *udpConnMap {
	return &udpConnMap{
		portMap: map[int][]*UDPConn{},
	}
}

func ( *udpConnMap) ( *UDPConn) error {
	.mutex.Lock()
	defer .mutex.Unlock()

	 := .LocalAddr().(*net.UDPAddr) //nolint:forcetypeassert

	// check if the port has a listener
	,  := .portMap[.Port]
	if  {
		if .IP.IsUnspecified() {
			return errAddressAlreadyInUse
		}

		for ,  := range  {
			 := .LocalAddr().(*net.UDPAddr) //nolint:forcetypeassert
			if .IP.IsUnspecified() || .IP.Equal(.IP) {
				return errAddressAlreadyInUse
			}
		}

		 = append(, )
	} else {
		 = []*UDPConn{}
	}

	.portMap[.Port] = 
	return nil
}

func ( *udpConnMap) ( net.Addr) (*UDPConn, bool) {
	.mutex.Lock() // could be RLock, but we have delete() op
	defer .mutex.Unlock()

	 := .(*net.UDPAddr) //nolint:forcetypeassert

	if ,  := .portMap[.Port];  {
		if .IP.IsUnspecified() {
			// pick the first one appears in the iteration
			if len() == 0 {
				// This can't happen!
				delete(.portMap, .Port)
				return nil, false
			}
			return [0], true
		}

		for ,  := range  {
			 := .LocalAddr().(*net.UDPAddr) //nolint:forcetypeassert
			if .IP.IsUnspecified() || .IP.Equal(.IP) {
				return , 
			}
		}
	}

	return nil, false
}

func ( *udpConnMap) ( net.Addr) error {
	.mutex.Lock()
	defer .mutex.Unlock()

	 := .(*net.UDPAddr) //nolint:forcetypeassert

	,  := .portMap[.Port]
	if ! {
		return errNoSuchUDPConn
	}

	if .IP.IsUnspecified() {
		// remove all from this port
		delete(.portMap, .Port)
		return nil
	}

	 := []*UDPConn{}

	for ,  := range  {
		 := .LocalAddr().(*net.UDPAddr) //nolint:forcetypeassert
		if .IP.IsUnspecified() {
			// This can't happen!
			return errCannotRemoveUnspecifiedIP
		}

		if .IP.Equal(.IP) {
			continue
		}

		 = append(, )
	}

	if len() == 0 {
		delete(.portMap, .Port)
	} else {
		.portMap[.Port] = 
	}

	return nil
}

// size returns the number of UDPConns (UDP listeners)
func ( *udpConnMap) () int {
	.mutex.RLock()
	defer .mutex.RUnlock()

	 := 0
	for ,  := range .portMap {
		 += len()
	}

	return 
}