/*
 * Video On Demand Samples
 *
 * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * You may also obtain this software under a propriety license from Microchip.
 * Please contact Microchip for further information.
 *
 */

#include <time.h>
#include "StreamList.h"
#include "TsPacket.h"


uint32_t CStreamList::m_nInstCnt = 0;

CStreamList::CStreamList()
    : m_nInst(m_nInstCnt++)
    , m_nPatVersion(1)
    , m_nPatCont(0)
    , m_nNextStreamSending(0)
{
}




bool CStreamList::ReadHdd()
{
    bool bRead = false;                                     // no read done yet

    for (uint32_t n = 0; n < MAX_STREAM_LIST_LEN; n++)      // run over all streams
        bRead |= m_Stream[n].ReadHdd();                     // read from HDD

    return bRead;                                           // return if there was a read
}




void CStreamList::SetSource(CSource *pSource)
{
    for (uint32_t n = 0; n < MAX_STREAM_LIST_LEN; n++)      // run over all streams
        if (NULL !=
            m_Stream[n].GetMacAddr())                       // only set in active streams
            m_Stream[n].SetSource(pSource);
}




void CStreamList::GetPat(uint8_t *pTs)
{
    // ---------------------------------------------------- //
    // create PAT packet									//
    // ---------------------------------------------------- //
    pTs[0] = 0x47;										    // packet start

    pTs[1] = (1 << 6);										// Payload Unit start indicator, PID = 0;
    pTs[2] = 0;

    pTs[3] = (1 << 4) | 									// no adaption field
             m_nPatCont;                                    // continuity counter

    m_nPatCont = (m_nPatCont + 1) & 0xF;                    // update 4 bit continuity counter

    pTs[4] = 0;											    // PointerField: new section

    uint8_t *pPat = pTs + 5;							    // pointer to PAT

    pPat[0] = 0;											// Table ID: Program Association Section

    uint16_t wSectionLength = 4 + 5 + 0;				    // 4 byte CRC, 5 bytes offset to entries, zero 4 byte entry,

    pPat[3] = 0;											// stream ID (HB)
    pPat[4] = 0;											// stream ID (LB)

    pPat[5] = (3 << 6) |                                    // reserved
              (m_nPatVersion << 1) |                        // version
              1;								            // current/next

    pPat[6] = 0;											// section number
    pPat[7] = 0;											// last section number

    for (uint32_t n = 0; n < MAX_STREAM_LIST_LEN; n++) {	// run over all streams
        CMacAddr *pMac = m_Stream[n].GetMacAddr();
        if (pMac) {
            pPat[wSectionLength - 4 + 3 + 0] = (PID_PMT((*pMac)[5]) >> 8); // program number
            pPat[wSectionLength - 4 + 3 + 1] = PID_PMT((*pMac)[5]) & 0xFF;

            pPat[wSectionLength - 4 + 3 + 2] = (7 << 5) |			      // reserved
                                               (PID_PMT((*pMac)[5]) >> 8);	// Pmt PID (HB)
            pPat[wSectionLength - 4 + 3 + 3] = PID_PMT((*pMac)[5]) & 0xFF;	// Pmt PID (LB)

            wSectionLength += 4;
        }
    }

    pPat[1] = (1 << 7) |									// section syntax indicator
              (3 << 4) |									// reserved
              (wSectionLength >> 8);						// section syntax 1, section length

    pPat[2] = wSectionLength & 0xFF;

    uint32_t nCrc = CTsPacket::GetCrc32(pPat,wSectionLength + 3 - 4); // CRC over entire PAT section without 4 CRC bytes
    pPat[wSectionLength + 3 - 4 + 0] = (nCrc >> 24) & 0xFF;
    pPat[wSectionLength + 3 - 4 + 1] = (nCrc >> 16) & 0xFF;
    pPat[wSectionLength + 3 - 4 + 2] = (nCrc >>  8) & 0xFF;
    pPat[wSectionLength + 3 - 4 + 3] = (nCrc) & 0xFF;

    // fill remaining packet with 0xFF
    for (uint8_t *p = 1 + &pPat[wSectionLength + 3 - 4 + 3]; p < pTs + 188; p++)
        *p = 0xFF;
}

bool CStreamList::GetPatPacket(uint8_t *pTs)
{
    GetPat(pTs);
    return true;
}

bool CStreamList::GetTsPacket(uint8_t *pTs)
{
    for (uint32_t n = 0; n < MAX_STREAM_LIST_LEN; n++) 	    // run over all streams
    {
        m_nNextStreamSending++;
        if ( m_nNextStreamSending >= MAX_STREAM_LIST_LEN )
            m_nNextStreamSending = 0;
        
        if ( m_Stream[m_nNextStreamSending].GetTsPacket(pTs) )
            return true;
    }

    return false;
}




uint32_t CStreamList::GetBytesRead()
{
    uint32_t nSum = 0;

    for (uint32_t n = 0; n < MAX_STREAM_LIST_LEN; n++)      // run over all streams
        nSum += m_Stream[n].GetBytesRead();

    return nSum;
}




uint32_t CStreamList::GetBytesSent()
{
    uint32_t nSum = 0;

    for (uint32_t n = 0; n < MAX_STREAM_LIST_LEN; n++)      // run over all streams
        nSum += m_Stream[n].GetBytesSent();

    return nSum;
}




uint32_t CStreamList::GetErrPcr()
{
    uint32_t nSum = 0;

    for (uint32_t n = 0; n < MAX_STREAM_LIST_LEN; n++)      // run over all streams
        nSum += m_Stream[n].GetErrPcr();

    return nSum;
}




uint32_t CStreamList::GetErrBufUnderflow()
{
    uint32_t nSum = 0;

    for (uint32_t n = 0; n < MAX_STREAM_LIST_LEN; n++)      // run over all streams
        nSum += m_Stream[n].GetErrBufUnderflow();

    return nSum;
}




uint32_t CStreamList::GetErrTs()
{
    uint32_t nSum = 0;

    for (uint32_t n = 0; n < MAX_STREAM_LIST_LEN; n++)      // run over all streams
        nSum += m_Stream[n].GetErrTs();

    return nSum;
}




CStream *CStreamList::GetStream(CMacAddr *pMacAddr)
{
    for (uint32_t n = 0; n < MAX_STREAM_LIST_LEN; n++) {
        CMacAddr *pSource =  m_Stream[n].GetMacAddr();
        if (NULL == pMacAddr) {
            //In case of a given NULL pointer, return the first unassigned stream
            if (NULL == pSource)
                return &m_Stream[n];
        } else if (NULL != pSource) {
            //Else search for the correct instance
            if (*pMacAddr == *pSource)
                return &m_Stream[n];
        }
    }
    return NULL;
}




void CStreamList::PrintStreamList()
{
    ConsolePrintfStart( PRIO_HIGH, "StreamListID: %u\n\n", m_nInst);
    ConsolePrintfContinue("StreamID SourceID Ovfl  MAC              \n");
    ConsolePrintfExit("-----------------------------------------------------------\n");
    for (uint32_t n = 0; n < MAX_STREAM_LIST_LEN; n++)
        m_Stream[n].PrintStream();
}