package astits

import (
	"github.com/pkg/errors"
)

// PIDs
const (
	PIDPAT  = 0x0    // Program Association Table (PAT) contains a directory listing of all Program Map Tables.
	PIDCAT  = 0x1    // Conditional Access Table (CAT) contains a directory listing of all ITU-T Rec. H.222 entitlement management message streams used by Program Map Tables.
	PIDTSDT = 0x2    // Transport Stream Description Table (TSDT) contains descriptors related to the overall transport stream
	PIDNull = 0x1fff // Null Packet (used for fixed bandwidth padding)
)

// Data represents a data
type Data struct {
	EIT         *EITData
	FirstPacket *Packet
	NIT         *NITData
	PAT         *PATData
	PES         *PESData
	PID         uint16
	PMT         *PMTData
	SDT         *SDTData
	TOT         *TOTData
}

// parseData parses a payload spanning over multiple packets and returns a set of data
func parseData(ps []*Packet, prs PacketsParser, pm programMap) (ds []*Data, err error) {
	// Use custom parser first
	if prs != nil {
		var skip bool
		if ds, skip, err = prs(ps); err != nil {
			err = errors.Wrap(err, "astits: custom packets parsing failed")
			return
		} else if skip {
			return
		}
	}

	// Reconstruct payload
	var l int
	for _, p := range ps {
		l += len(p.Payload)
	}
	var payload = make([]byte, l)
	var c int
	for _, p := range ps {
		c += copy(payload[c:], p.Payload)
	}

	// Parse PID
	var pid = ps[0].Header.PID

	// Parse payload
	if pid == PIDCAT {
		// Information in a CAT payload is private and dependent on the CA system. Use the PacketsParser
		// to parse this type of payload
	} else if isPSIPayload(pid, pm) {
		var psiData *PSIData
		if psiData, err = parsePSIData(payload); err != nil {
			err = errors.Wrap(err, "astits: parsing PSI data failed")
			return
		}
		ds = psiData.toData(ps[0], pid)
	} else if isPESPayload(payload) {
		d, err := parsePESData(payload)
		if err == nil {
			ds = append(ds, &Data{
				FirstPacket: ps[0],
				PES:         d,
				PID:         pid,
			})
		}
	}
	return
}

// isPSIPayload checks whether the payload is a PSI one
func isPSIPayload(pid uint16, pm programMap) bool {
	return pid == PIDPAT || // PAT
		pm.exists(pid) || // PMT
		((pid >= 0x10 && pid <= 0x14) || (pid >= 0x1e && pid <= 0x1f)) //DVB
}

// isPESPayload checks whether the payload is a PES one
func isPESPayload(i []byte) bool {
	// Packet is not big enough
	if len(i) < 3 {
		return false
	}

	// Check prefix
	return uint32(i[0])<<16|uint32(i[1])<<8|uint32(i[2]) == 1
}
