/*
NAME
  rtcp.go

DESCRIPTION
  rtcp.go contains structs to describe RTCP packets, and functionality to form
  []bytes of these structs.

AUTHORS
  Saxon A. Nelson-Milton <saxon@ausocean.org>

LICENSE
  This is Copyright (C) 2019 the Australian Ocean Lab (AusOcean)

  It is free software: you can redistribute it and/or modify them
  under the terms of the GNU General Public License as published by the
  Free Software Foundation, either version 3 of the License, or (at your
  option) any later version.

  It is distributed in the hope that it will be useful, but WITHOUT
  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  for more details.

  You should have received a copy of the GNU General Public License
  in gpl.txt.  If not, see http://www.gnu.org/licenses.
*/

// Package RTCP provides RTCP data structures and a client for communicating
// with an RTCP service.
package rtcp

import (
	"encoding/binary"
)

// RTCP packet types.
const (
	typeSenderReport   = 200
	typeReceiverReport = 201
	typeDescription    = 202
)

// Source Description Item types.
const (
	typeCName = 1
)

const (
	reportBlockSize  = 6
	senderReportSize = 28
)

// ReceiverReport describes an RTCP receiver report packet.
type ReceiverReport struct {
	Header                   // Standard RTCP packet header.
	SenderSSRC uint32        // SSRC of the sender of this report.
	Blocks     []ReportBlock // Report blocks.
	Extensions [][4]byte     // Contains any extensions to the packet.
}

// Bytes returns a []byte of the ReceiverReport r.
func (r *ReceiverReport) Bytes(buf []byte) []byte {
	l := 8 + 4*reportBlockSize*len(r.Blocks) + 4*len(r.Extensions)
	if buf == nil || cap(buf) < l {
		buf = make([]byte, l)
	}
	buf = buf[:l]
	l = 1 + reportBlockSize*len(r.Blocks) + len(r.Extensions)
	r.writeHeader(buf, l)
	binary.BigEndian.PutUint32(buf[4:], r.SenderSSRC)

	idx := 8
	for _, b := range r.Blocks {
		binary.BigEndian.PutUint32(buf[idx:], b.SourceIdentifier)
		binary.BigEndian.PutUint32(buf[idx+4:], b.PacketsLost)
		buf[idx+4] = b.FractionLost
		binary.BigEndian.PutUint32(buf[idx+8:], b.HighestSequence)
		binary.BigEndian.PutUint32(buf[idx+12:], b.Jitter)
		binary.BigEndian.PutUint32(buf[idx+16:], b.SenderReportTs)
		binary.BigEndian.PutUint32(buf[idx+20:], b.SenderReportDelay)
		idx += 24
	}

	for _, e := range r.Extensions {
		copy(buf[idx:], e[:])
		idx += 4
	}

	return buf
}

// ReportBlock describes an RTCP report block used in Sender/Receiver Reports.
type ReportBlock struct {
	SourceIdentifier  uint32 // Source identifier.
	FractionLost      uint8  // Fraction of packets lost.
	PacketsLost       uint32 // Cumulative number of packets lost.
	HighestSequence   uint32 // Extended highest sequence number received.
	Jitter            uint32 // Interarrival jitter.
	SenderReportTs    uint32 // Last sender report timestamp.
	SenderReportDelay uint32 // Delay since last sender report.
}

// Description describes a source description RTCP packet.
type Description struct {
	Header         // Standard RTCP packet header.
	Chunks []Chunk // Chunks to describe items of each SSRC.
}

// Bytes returns an []byte of the Description d.
func (d *Description) Bytes(buf []byte) []byte {
	bodyLen := d.bodyLen()
	rem := bodyLen % 4
	if rem != 0 {
		bodyLen += 4 - rem
	}

	l := 4 + bodyLen
	if buf == nil || cap(buf) < l {
		buf = make([]byte, l)
	}
	buf = buf[:l]

	d.writeHeader(buf, bodyLen/4)
	idx := 4
	for _, c := range d.Chunks {
		binary.BigEndian.PutUint32(buf[idx:], c.SSRC)
		idx += 4
		for _, i := range c.Items {
			buf[idx] = i.Type
			buf[idx+1] = byte(len(i.Text))
			idx += 2
			copy(buf[idx:], i.Text)
			idx += len(i.Text)
		}
	}
	return buf
}

// bodyLen calculates the body length of a source description packet in bytes.
func (d *Description) bodyLen() int {
	var l int
	for _, c := range d.Chunks {
		l += c.len()
	}
	return l
}

// SenderReport describes an RTCP sender report.
type SenderReport struct {
	Header              // Standard RTCP header.
	SSRC         uint32 // SSRC of sender.
	TimestampMSW uint32 // Most significant word of timestamp.
	TimestampLSW uint32 // Least significant word of timestamp.
	RTPTimestamp uint32 // Current RTP timestamp.
	PacketCount  uint32 // Senders packet count.
	OctetCount   uint32 // Senders octet count.

	// Report blocks (unimplemented)
	// ...
}

// Bytes returns a []byte of the SenderReport.
func (r *SenderReport) Bytes() []byte {
	buf := make([]byte, senderReportSize)
	r.writeHeader(buf, senderReportSize-1)
	for i, w := range []uint32{
		r.SSRC,
		r.TimestampMSW,
		r.TimestampLSW,
		r.RTPTimestamp,
		r.PacketCount,
		r.OctetCount,
	} {
		binary.BigEndian.PutUint32(buf[i+4:], w)
	}
	return buf
}

// Header describes a standard RTCP packet header.
type Header struct {
	Version     uint8 // RTCP version.
	Padding     bool  // Padding indicator.
	ReportCount uint8 // Number of reports contained.
	Type        uint8 // Type of RTCP packet.
}

// SDESItem describes a source description item.
type SDESItem struct {
	Type uint8  // Type of item.
	Text []byte // Item text.
}

// Chunk describes a source description chunk for a given SSRC.
type Chunk struct {
	SSRC  uint32     // SSRC of the source being described by the below items.
	Items []SDESItem // Items describing the source.
}

// len returns the len of a chunk in bytes.
func (c *Chunk) len() int {
	tot := 4
	for _, i := range c.Items {
		tot += 2 + len(i.Text)
	}
	return tot
}

// writeHeader writes the standard RTCP header given a buffer to write to and l
// the RTCP body length that needs to be encoded into the header.
func (h Header) writeHeader(buf []byte, l int) {
	buf[0] = h.Version<<6 | asByte(h.Padding)<<5 | 0x1f&h.ReportCount
	buf[1] = h.Type
	binary.BigEndian.PutUint16(buf[2:], uint16(l))
}

func asByte(b bool) byte {
	if b {
		return 0x01
	}
	return 0x00
}