// Copyright 2026 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package nettest

import (
	
	
	
	
	
	
	
	
)

// A PacketNet is a group of communicating [PacketConn]s.
type PacketNet struct {
	mu    sync.Mutex
	conns map[netAddr]*PacketConn
}

type netAddr struct {
	network string
	addr    string
}

// NewPacketNet returns a new PacketNet.
func () *PacketNet {
	return &PacketNet{
		conns: make(map[netAddr]*PacketConn),
	}
}

// NewConn returns a new [PacketConn] listening on the given address.
// It returns an error if there is an existing listener on this address.
func ( *PacketNet) ( net.Addr) (*PacketConn, error) {
	.mu.Lock()
	defer .mu.Unlock()
	 := netAddr{.Network(), .String()}
	if ,  := .conns[];  {
		return nil, &net.OpError{
			Op:   "listen",
			Net:  "udp",
			Addr: ,
			Err:  errors.New("address is in use"),
		}
	}
	 := &PacketConn{
		gate: gate.New(false),
		addr: ,
		net:  ,
	}
	.conns[] = 
	return , nil
}

type PacketConn struct {
	gate         gate.Gate
	queue        queue[*packet]
	closed       bool
	readErr      error
	writeErr     error
	closeErr     error
	readDeadline connDeadline

	net  *PacketNet
	addr net.Addr
}

type packet struct {
	b   []byte
	src net.Addr
}

// ReadFrom reads a packet from the connection, copying the payload into b.
func ( *PacketConn) ( []byte) ( int,  net.Addr,  error) {
	.gate.WaitAndLock(context.Background())
	defer .unlock()

	switch {
	case .closed:
		 = net.ErrClosed
	case .readDeadline.expired:
		 = os.ErrDeadlineExceeded
	case .queue.len() == 0 && .readErr != nil:
		 = .readErr
	}
	if  != nil {
		return 0, nil, &net.OpError{
			Op:   "read",
			Net:  "udp",
			Addr: .addr,
			Err:  ,
		}
	}
	 := .queue.pop()
	 = copy(, .b)
	return , .src, nil
}

// WriteTo writes a packet with payload b to addr.
// addr must be a [*net.UDPAddr].
//
// WriteTo appends the packet to the recipient's receive buffer.
// If no recipient is listening on addr or if the recipient's
// receive buffer is full, the packet is silently discarded.
func ( *PacketConn) ( []byte,  net.Addr) ( int,  error) {
	.gate.Lock()
	switch {
	case .closed:
		 = net.ErrClosed
	case .writeErr != nil:
		 = .writeErr
	}
	.unlock()
	if  != nil {
		return 0, &net.OpError{
			Op:     "write",
			Net:    "udp",
			Source: .addr,
			Addr:   ,
			Err:    ,
		}
	}

	.net.mu.Lock()
	 := .net.conns[netAddr{.Network(), .String()}]
	.net.mu.Unlock()

	if  == nil {
		// There is no PacketConn listening on the destination address,
		// and the packet falls silently into the void.
		return len(), nil
	}

	.lock()
	if !.closed {
		.queue.push(&packet{b: slices.Clone(), src: .addr})
	}
	.unlock()
	return len(), nil
}

// Close closes the connection.
func ( *PacketConn) () error {
	.net.mu.Lock()
	delete(.net.conns, netAddr{.addr.Network(), .addr.String()})
	.net.mu.Unlock()

	.lock()
	defer .unlock()
	 := .closeErr
	.closed = true
	.readErr = net.ErrClosed
	.writeErr = net.ErrClosed
	.closeErr = net.ErrClosed
	if  != nil {
		return &net.OpError{
			Op:   "close",
			Net:  "udp",
			Addr: .addr,
			Err:  ,
		}
	}
	return 
}

// LocalAddr returns the (fake) local network address.
func ( *PacketConn) () net.Addr {
	.lock()
	defer .unlock()
	return .addr
}

// SetReadDeadline sets the read deadline for the connection.
// PacketConns have no write deadline.
func ( *PacketConn) ( time.Time) error {
	return .SetReadDeadline()
}

// SetReadDeadline sets the read deadline for the connection.
func ( *PacketConn) ( time.Time) error {
	.readDeadline.setDeadline(, )
	return nil
}

// SetWriteDeadline has no effect.
// Writes to PacketConns never block.
func ( *PacketConn) ( time.Time) error {
	return nil
}

// SetReadError causes any currently blocked and future ReadFrom calls to return
// a net.OpError wrapping err. It does not affect the other half of the connection.
// Reads will return any buffered data before returning the error,
// including data written after the error is set.
// A nil error restores the usual behavior.
func ( *PacketConn) ( error) {
	.lock()
	defer .unlock()
	.readErr = 
}

// SetWriteError causes any currently blocked and future WriteTo calls to return
// a net.OpError wrapping err. It does not affect the other half of the connection.
// Writes will not write data while an error is set.
// A nil error restores the usual behavior.
func ( *PacketConn) ( error) {
	.lock()
	defer .unlock()
	.writeErr = 
}

// SetCloseError sets the error returned by Close.
// Close still closes the connection.
// A nil error restores the usual behavior.
func ( *PacketConn) ( error) {
	.lock()
	defer .unlock()
	.closeErr = 
}

// CanRead reports whether [ReadFrom] can return at least one byte or an error.
// If [ReadFrom] would block, CanRead returns false.
func ( *PacketConn) () bool {
	.lock()
	defer .unlock()
	return .canReadLocked()
}

func ( *PacketConn) () bool {
	return .queue.len() > 0 || .readDeadline.expired || .closed || .readErr != nil
}

// IsClosed reports whether the connection has been closed.
func ( *PacketConn) () bool {
	.lock()
	defer .unlock()
	return .closed
}

func ( *PacketConn) () {
	.gate.Lock()
}

func ( *PacketConn) () {
	.gate.Unlock(.canReadLocked())
}