package peer

import (
	"net/netip"
)

const (
	packetTypeSyn           = 1
	packetTypeInit          = 2
	packetTypeAck           = 3
	packetTypeProbe         = 4
	packetTypeAddrDiscovery = 5
)

// ----------------------------------------------------------------------------

type packetInit struct {
	TraceID uint64
	Direct  bool
	Version uint64
}

func (p packetInit) Marshal(buf []byte) []byte {
	return newBinWriter(buf).
		Byte(packetTypeInit).
		Uint64(p.TraceID).
		Bool(p.Direct).
		Uint64(p.Version).
		Build()
}

func parsePacketInit(buf []byte) (p packetInit, err error) {
	err = newBinReader(buf[1:]).
		Uint64(&p.TraceID).
		Bool(&p.Direct).
		Uint64(&p.Version).
		Error()
	return
}

// ----------------------------------------------------------------------------

type packetSyn struct {
	TraceID       uint64   // TraceID to match response w/ request.
	SharedKey     [32]byte // Our shared key.
	Direct        bool
	PossibleAddrs [8]netip.AddrPort // Possible public addresses of the sender.
}

func (p packetSyn) Marshal(buf []byte) []byte {
	return newBinWriter(buf).
		Byte(packetTypeSyn).
		Uint64(p.TraceID).
		SharedKey(p.SharedKey).
		Bool(p.Direct).
		AddrPort8(p.PossibleAddrs).
		Build()
}

func parsePacketSyn(buf []byte) (p packetSyn, err error) {
	err = newBinReader(buf[1:]).
		Uint64(&p.TraceID).
		SharedKey(&p.SharedKey).
		Bool(&p.Direct).
		AddrPort8(&p.PossibleAddrs).
		Error()
	return
}

// ----------------------------------------------------------------------------

type packetAck struct {
	TraceID       uint64
	ToAddr        netip.AddrPort
	PossibleAddrs [8]netip.AddrPort // Possible public addresses of the sender.
}

func (p packetAck) Marshal(buf []byte) []byte {
	return newBinWriter(buf).
		Byte(packetTypeAck).
		Uint64(p.TraceID).
		AddrPort(p.ToAddr).
		AddrPort8(p.PossibleAddrs).
		Build()
}

func parsePacketAck(buf []byte) (p packetAck, err error) {
	err = newBinReader(buf[1:]).
		Uint64(&p.TraceID).
		AddrPort(&p.ToAddr).
		AddrPort8(&p.PossibleAddrs).
		Error()
	return
}

// ----------------------------------------------------------------------------

// A probeReqPacket is sent from a client to a server to determine if direct
// UDP communication can be used.
type packetProbe struct {
	TraceID uint64
}

func (p packetProbe) Marshal(buf []byte) []byte {
	return newBinWriter(buf).
		Byte(packetTypeProbe).
		Uint64(p.TraceID).
		Build()
}

func parsePacketProbe(buf []byte) (p packetProbe, err error) {
	err = newBinReader(buf[1:]).
		Uint64(&p.TraceID).
		Error()
	return
}

// ----------------------------------------------------------------------------

type packetLocalDiscovery struct{}
