/*
 * 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 3 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 <stdint.h>
#include "Source.h"
#include "Stream.h"
#include "TsPacket.h"



uint32_t CStream::m_nInstCnt = 0;




CStream::CStream()
    : CRingBuffer(CTsPacket::TS_PACKET_LEN, CTsPacket::TS_PACKET_LEN, NUM_PACKETS_BUFFERED)
    , m_nInst(m_nInstCnt++)
    , m_MacAddr(NULL)
    , m_pCurrSource(NULL)
    , m_pNextSource(NULL)
    , m_nBytesSent(0)
    , m_nErrBufOvfl(0)
{
}




CStream::~CStream()
{
    if (NULL != m_MacAddr) {
        delete m_MacAddr;
        m_MacAddr = NULL;
    }
    
    if (NULL != m_pCurrSource) {
        m_pCurrSource->RemoveStream(this);
    }
}




bool CStream::ReadHdd()
{
    if (NULL != m_pNextSource) {                            //Switch sources, if there is a new one stored.
        if (NULL != m_pCurrSource)                          // release old source
            m_pCurrSource->RemoveStream(this);
        
        m_pCurrSource = m_pNextSource;                      // store new source
        m_pNextSource = NULL;
        m_pCurrSource->AddStream(this);                     // start listening to this source
    }
    
    return (NULL == m_pCurrSource) ? false : m_pCurrSource->FillBuffer();
}




void CStream::SendPmt()
{
    if ( !GetCanWrite() )
    {
        m_nErrBufOvfl++;
        return;
    }
        
    m_TsPmt[3] = (m_TsPmt[3] & 0xF0) |                      // update continuity counter
                 (((m_TsPmt[3] & 0x0F) + 1) & 0x0F);

    memcpy(GetWritePos(), m_TsPmt, CTsPacket::TS_PACKET_LEN);
    WriteDone();
}




void CStream::SendAudio(uint8_t *pTs)
{
    if ( !GetCanWrite() )
    {
        m_nErrBufOvfl++;
        return;
    }
        
    pTs[1] = (pTs[1] & 0xE0) | (mPID_AUDIO >> 8);           // replace Audio PID
    pTs[2] = mPID_AUDIO & 0xFF;
    
    memcpy(GetWritePos(), pTs, CTsPacket::TS_PACKET_LEN);
    WriteDone();
}




void CStream::SendVideo(uint8_t *pTs)
{
    if ( !GetCanWrite() )
    {
        m_nErrBufOvfl++;
        return;
    }
        
    pTs[1] = (pTs[1] & 0xE0) | (mPID_VIDEO >> 8);           // replace video PID
    pTs[2] = mPID_VIDEO & 0xFF;
    
    memcpy(GetWritePos(), pTs, CTsPacket::TS_PACKET_LEN);
    WriteDone();
}



bool CStream::GetTsPacket(uint8_t *pTs)
{
    if ( NULL == pTs        ||                              // pointer check
         NULL == m_MacAddr  )                               // we have a target address?
        return false;
        
    if ( !GetCanRead() )                                    // ring buffer empty?
        if ( m_pCurrSource )
            m_pCurrSource->SendTsPacket(this);              // get data from source
       
    if ( !GetCanRead() )                                    // ring buffer still empty?
        return false;

    memcpy(pTs, GetReadPos(), CTsPacket::TS_PACKET_LEN);
    ReadDone();
    m_nBytesSent += CTsPacket::TS_PACKET_LEN;
    return true;
}




void CStream::SetMacAddr(CMacAddr *pMacAddr)
{
    if (NULL != m_MacAddr) {
        delete m_MacAddr;
        m_MacAddr = NULL;
    }
    m_MacAddr = new CMacAddr(pMacAddr);

    if (NULL != m_MacAddr)
        CTsPacket::CreatePmt(m_TsPmt, mPID_AUDIO, mPID_VIDEO, mPID_PMT);
}



uint64_t CStream::GetBytesRead()
{
    return (NULL == m_pCurrSource) ? 0 : m_pCurrSource->GetBytesRead();
}



uint64_t CStream::GetErrBufUnderflow()
{
    return m_pCurrSource ? m_pCurrSource->GetErrBuf() : 0;
}



uint32_t CStream::GetErrBufOverflow()
{
    uint32_t nRet =  m_nErrBufOvfl;
    m_nErrBufOvfl = 0;
    return nRet;
}



uint64_t CStream::GetErrPcr()
{
    return m_pCurrSource ? m_pCurrSource->GetErrPcr() : 0;
};



uint64_t CStream::GetErrTs()
{
    return m_pCurrSource ? m_pCurrSource->GetErrTs() : 0;
};



void CStream::SetPause(bool bPause) 
{
    if (NULL != m_pCurrSource)
        m_pCurrSource->SetPause(bPause);
}



void CStream::SetRepetition(bool bRepetition) {	
    if (m_pCurrSource)
        m_pCurrSource->SetRepetition(bRepetition);
}



void CStream::SetPos(uint16_t nPos) {
    if ( m_pCurrSource )
        m_pCurrSource->SetPos(nPos);
}


void CStream::PrintStream() {
    ConsolePrintf( PRIO_HIGH, "   %2u    %3d       %3u   %s\r\n"
                 , GetInstId()
                 , NULL == m_pCurrSource ? (-1) : (m_pCurrSource->GetInstId())
                 , m_nErrBufOvfl
                 , NULL == m_MacAddr ? "NULL             " : m_MacAddr->ToString()
                 );  
}