/*
NAME
  MpegTs.go - provides a data structure intended to encapsulate the properties
  of an MpegTs packet and also functions to allow manipulation of these packets.

DESCRIPTION
  See Readme.md

AUTHOR
  Saxon A. Nelson-Milton <saxon.milton@gmail.com>

LICENSE
  MpegTs.go is Copyright (C) 2017 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
  along with revid in gpl.txt.  If not, see [GNU licenses](http://www.gnu.org/licenses).
*/

package mpegts

import (
	//"bitbucket.org/ausocean/av/tools"
	"../tools"

	"errors"
	//"fmt"
)

const (
	maxMpegTsSize = 188
	mpegtsPayloadSize = 176
)

/*
The below data struct encapsulates the fields of an MPEG-TS packet. Below is
the formatting of an MPEG-TS packet for reference!

													MPEG-TS Packet Formatting
============================================================================
| octet no | bit 0 | bit 1 | bit 2 | bit 3 | bit 4 | bit 5 | bit 6 | bit 7 |
============================================================================
| octet 0  | sync byte (0x47)                                              |
----------------------------------------------------------------------------
| octet 1  | TEI   | PUSI  | Prior | PID                                   |
----------------------------------------------------------------------------
| octet 2  | PID cont.                                                     |
----------------------------------------------------------------------------
| octet 3  | TSC           | AFC           | CC                            |
----------------------------------------------------------------------------
| octet 4  | AFL                                                           |
----------------------------------------------------------------------------
| octet 5  | DI    | RAI   | ESPI  | PCRF  | OPCRF | SPF   | TPDF  | AFEF  |
----------------------------------------------------------------------------
| optional | PCR (48 bits => 6 bytes)                                      |
----------------------------------------------------------------------------
| -        | PCR cont.                                                     |
----------------------------------------------------------------------------
| -        | PCR cont.                                                     |
----------------------------------------------------------------------------
| -        | PCR cont.                                                     |
----------------------------------------------------------------------------
| -        | PCR cont.                                                     |
----------------------------------------------------------------------------
| -        | PCR cont.                                                     |
----------------------------------------------------------------------------
| optional | OPCR (48 bits => 6 bytes)                                     |
----------------------------------------------------------------------------
| -        | OPCR cont.                                                    |
----------------------------------------------------------------------------
| -        | OPCR cont.                                                    |
----------------------------------------------------------------------------
| -        | OPCR cont.                                                    |
----------------------------------------------------------------------------
| -        | OPCR cont.                                                    |
----------------------------------------------------------------------------
| -        | OPCR cont.                                                    |
----------------------------------------------------------------------------
| optional | SC                                                            |
----------------------------------------------------------------------------
| optional | TPDL                                                          |
----------------------------------------------------------------------------
| optional | TPD (variable length)                                         |
----------------------------------------------------------------------------
| -        | ...                                                           |
----------------------------------------------------------------------------
| optional | Extension (variable length)                                   |
----------------------------------------------------------------------------
| -        | ...                                                           |
----------------------------------------------------------------------------
| optional | Stuffing (variable length)                                    |
----------------------------------------------------------------------------
| -        | ...                                                           |
----------------------------------------------------------------------------
| optional | Payload (variable length)                                     |
----------------------------------------------------------------------------
| -        | ...                                                           |
----------------------------------------------------------------------------
*/
type MpegTsPacket struct {
	TEI      bool   // Transport Error Indicator
	PUSI     bool   // Payload Unit Start Indicator
	Priority bool   // Tranposrt priority indicator
	PID      uint16 // Packet identifier
	TSC      byte   // Transport Scrambling Control
	AFC      byte   // Adaption Field Control
	CC       byte   // Continuity Counter
	DI       bool   // Discontinouty indicator
	RAI      bool   // random access indicator
	ESPI     bool   // Elementary stream priority indicator
	PCRF     bool   // PCR flag
	OPCRF    bool   // OPCR flag
	SPF      bool   // Splicing point flag
	TPDF     bool   // Transport private data flag
	AFEF     bool   // Adaptation field extension flag
	PCR      uint64 // Program clock reference
	OPCR     uint64 // Original program clock reference
	SC       byte   // Splice countdown
	TPDL     byte   // Tranposrt private data length
	TPD      []byte // Private data
	Ext      []byte // Adaptation field extension
	Payload  []byte // Mpeg ts payload
}

// TODO: make payload private considering we now have FillPayload method

func (p *MpegTsPacket) FillPayload(channel chan byte){
	p.Payload = make([]byte,0,mpegtsPayloadSize)
	currentPktLength := 6 + int(tools.BoolToByte(p.PCRF))*6+int(tools.BoolToByte(p.OPCRF))*6+
		int(tools.BoolToByte(p.SPF))*1+int(tools.BoolToByte(p.TPDF))*1+len(p.TPD)
	for (currentPktLength+len(p.Payload)) < 188 {
		//fmt.Printf("len(channel): %v\n", len(channel))
		select {
		case nextByte := <-channel:
			p.Payload = append(p.Payload,nextByte)
		default:
			return
		}
	}
}

func (p *MpegTsPacket) ToByteSlice() (output []byte, err error) {
	stuffingLength := 182-len(p.Payload)-len(p.TPD)-int(tools.BoolToByte(p.PCRF))*6-
		int(tools.BoolToByte(p.OPCRF))*6 - int(tools.BoolToByte(p.SPF))
	var stuffing []byte
	if stuffingLength > 0 {
		stuffing = make([]byte,stuffingLength)
	}
	for i := range stuffing {
		stuffing[i] = 0xFF
	}
	afl := 1+int(tools.BoolToByte(p.PCRF))*6+int(tools.BoolToByte(p.OPCRF))*
		6+int(tools.BoolToByte(p.SPF))*1+int(tools.BoolToByte(p.TPDF))*1+len(p.TPD)+len(stuffing)
	output = make([]byte,0,maxMpegTsSize)
	output = append(output, []byte{
		0x47,
		(tools.BoolToByte(p.TEI)<<7 | tools.BoolToByte(p.PUSI)<<6 | tools.BoolToByte(p.Priority)<<5 |
			byte((p.PID&0xFF00)>>8)),
		byte(p.PID & 0x00FF),
		(p.TSC<<6 | p.AFC<<4 | p.CC),}...)

	if p.AFC == 3 || p.AFC == 2 {
		output = append(output, []byte{
			byte(afl), (tools.BoolToByte(p.DI)<<7 | tools.BoolToByte(p.RAI)<<6 | tools.BoolToByte(p.ESPI)<<5 |
			tools.BoolToByte(p.PCRF)<<4 | tools.BoolToByte(p.OPCRF)<<3 | tools.BoolToByte(p.SPF)<<2 |
			tools.BoolToByte(p.TPDF)<<1 | tools.BoolToByte(p.AFEF)),
		}...)
		for i := 40; p.PCRF && i >= 0; i-=8 {
			output = append(output, byte((p.PCR<<15)>>uint(i)))
		}
		for i := 40; p.OPCRF && i >= 0; i-=8 {
			output = append(output, byte(p.OPCR>>uint(i)))
		}
		if p.SPF {
			output = append(output, p.SC)
		}
		if p.TPDF {
			output = append(output, append([]byte{p.TPDL}, p.TPD...)...)
		}
		output = append(output, p.Ext...)
		output = append(output, stuffing...)
	}
	output = append(output, p.Payload...)

	if len(output) != 188 {
		err = errors.New("Length of MPEG-TS packet is not 188! Something is wrong!")
	}
	return
}