package socket

import (
	

	
)

// CustomDatagram is one datagram received by a [CustomTransport].
type CustomDatagram struct {
	Remote   netaddr.CustomAddr
	Local    netaddr.CustomAddr
	HasLocal bool
	Data     []byte
}

// Packet is one custom transport packet whose buffer is owned by the transport.
type Packet struct {
	Remote   netaddr.CustomAddr
	Local    netaddr.CustomAddr
	HasLocal bool
	Data     []byte
	Free     func()
}

// CustomTransport is a pluggable transport backend for custom addresses. It is
// intentionally small: the transport owns its wire format and reports datagrams
// as iroh custom addresses for the magic socket to map into qng paths.
type CustomTransport interface {
	// Serve runs the transport until ctx is done. Each received datagram should
	// be passed to recv. recv reports false when the magic socket is shutting
	// down or its receive queue is full.
	Serve(ctx context.Context, recv func(CustomDatagram) bool)

	// Send sends p to remote. local is nil when qng did not select a specific
	// local custom address for the path.
	Send(remote netaddr.CustomAddr, local *netaddr.CustomAddr, p []byte) bool
}

// PacketTransport is a custom transport that owns received packet buffers.
type PacketTransport interface {
	CustomTransport

	// ServePackets runs the transport until ctx is done. Received packets are
	// owned by the transport until their Free callback runs.
	ServePackets(ctx context.Context, recv func(Packet) bool)

	// SendPacket sends p to remote. The transport must not retain p after the
	// call returns.
	SendPacket(remote netaddr.CustomAddr, local *netaddr.CustomAddr, p []byte) bool
}

type customTransport struct {
	transport CustomTransport
	recvCh    chan<- recvBatch
}

func newCustomTransport( CustomTransport,  chan<- recvBatch) *customTransport {
	return &customTransport{transport: , recvCh: }
}

func ( *customTransport) ( context.Context) {
	if ,  := .transport.(PacketTransport);  {
		.servePackets(, )
		return
	}
	.transport.Serve(, func( CustomDatagram) bool {
		 := make([]byte, len(.Data))
		copy(, .Data)
		select {
		case .recvCh <- recvBatch{
			data: ,
			info: RecvInfo{Remote: CustomAddr(.Remote), Local: .Local, HasLocal: .HasLocal},
		}:
			return true
		case <-.Done():
			return false
		default:
			return false
		}
	})
}

func ( *customTransport) ( context.Context,  PacketTransport) {
	.ServePackets(, func( Packet) bool {
		select {
		case .recvCh <- recvBatch{
			data:      .Data,
			info:      RecvInfo{Remote: CustomAddr(.Remote), Local: .Local, HasLocal: .HasLocal},
			releaseFn: .Free,
		}:
			return true
		case <-.Done():
			if .Free != nil {
				.Free()
			}
			return false
		default:
			if .Free != nil {
				.Free()
			}
			return false
		}
	})
}

func ( *customTransport) ( netaddr.CustomAddr,  *netaddr.CustomAddr,  []byte) bool {
	if ,  := .transport.(PacketTransport);  {
		return .SendPacket(, , )
	}
	 := make([]byte, len())
	copy(, )
	return .transport.Send(, , )
}