package packet

// Structure of packets and functions for writing/reading them

import (
	"errors"
)

// AckPacket is a UDT packet acknowledging previously-received data packets and describing the state of the link
type AckPacket struct {
	ctrlHeader
	AckSeqNo  uint32   // ACK sequence number
	PktSeqHi  PacketID // The packet sequence number to which all the previous packets have been received (excluding)
	Rtt       uint32   // RTT (in microseconds)
	RttVar    uint32   // RTT variance
	BuffAvail uint32   // Available buffer size (in bytes)

	// the following data is optional (not sent more than SYN)
	IncludeLink bool
	PktRecvRate uint32 // Packets receiving rate (in number of packets per second)
	EstLinkCap  uint32 // Estimated link capacity (in number of packets per second)
}

// WriteTo writes this packet to the provided buffer, returning the length of the packet
func (p *AckPacket) WriteTo(buf []byte) (uint, error) {
	l := len(buf)
	if l < 32 {
		return 0, errors.New("ack packet too small")
	}

	if _, err := p.writeHdrTo(buf, ptAck, p.AckSeqNo); err != nil {
		return 0, err
	}

	endianness.PutUint32(buf[16:20], p.PktSeqHi.Seq)
	endianness.PutUint32(buf[20:24], p.Rtt)
	endianness.PutUint32(buf[24:28], p.RttVar)
	endianness.PutUint32(buf[28:32], p.BuffAvail)
	if p.IncludeLink {
		if l < 40 {
			return 0, errors.New("ack packet too small")
		}
		endianness.PutUint32(buf[32:36], p.PktRecvRate)
		endianness.PutUint32(buf[36:40], p.EstLinkCap)
		return 40, nil
	}

	return 32, nil
}

func (p *AckPacket) readFrom(data []byte) (err error) {
	l := len(data)
	if l < 32 {
		return errors.New("ack packet too small")
	}
	if p.AckSeqNo, err = p.readHdrFrom(data); err != nil {
		return err
	}
	p.PktSeqHi = PacketID{endianness.Uint32(data[16:20])}
	p.Rtt = endianness.Uint32(data[20:24])
	p.RttVar = endianness.Uint32(data[24:28])
	p.BuffAvail = endianness.Uint32(data[28:32])
	if l >= 36 {
		p.IncludeLink = true
		p.PktRecvRate = endianness.Uint32(data[32:36])
		if l >= 40 {
			p.EstLinkCap = endianness.Uint32(data[36:40])
		}
	}

	return nil
}

// PacketType returns the packetType associated with this packet
func (p *AckPacket) PacketType() PacketType {
	return ptAck
}
