/*
 * 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 <stdint.h>
#include "Console.h"
#include "SourceFile.h"

uint32_t CSource::m_nInstCnt = 0;

CSourceFile::CSourceFile(const char *szFile, bool autoDestroy)
{
    m_bAutoDestroy = autoDestroy;
    m_hFile = fopen64(szFile, "rb"); // try to open file
    if (NULL == m_hFile) {
        ConsolePrintf( PRIO_ERROR, RED"CSourceFile: unable to open file %s"RESETCOLOR"\n", szFile);
        return;
    }
    strncpy(m_szFileName, szFile, sizeof(m_szFileName));

    fseek(m_hFile, 0, SEEK_END); // get file's length
    fpos64_t pos;
    int result = fgetpos64(m_hFile, &pos);
    if (result == 0)
        m_nLength = pos.__pos;
    else
        m_nLength = -1;
    fseek(m_hFile, 0, SEEK_SET); // start reading from start of file

    uint8_t Ts[READ_LEN]; // get PIDs for video and audio
    uint32_t bytesRead = fread(// read a chunk from file and write it into ring buffer
                            Ts,
                            1,
                            READ_LEN,
                            m_hFile);

    if (READ_LEN != bytesRead) { // no complete block available?
        Close();
        return;
    }

    for (uint8_t *pTs = Ts; pTs < Ts + READ_LEN; pTs += 188) {
        if (PID_INVALID == m_nPPid) {
            if (GetIsPat(pTs))
                m_nPPid = GetPmtPidFromPat(pTs, 0);
        } else if (GetHasPid(pTs, m_nPPid)) {
            m_nAPid = GetAudioPidFromPmt(pTs);
            m_nVPid = GetVideoPidFromPmt(pTs);
            break;
        }
    }

    if (PID_INVALID == m_nPPid) { // PMT found ?
        ConsolePrintf( PRIO_ERROR, RED"CSourceFile: no PMT found in file %s"RESETCOLOR"\n", szFile);
        Close();
        return;
    }

    uint64_t nBlocks = (m_nLength / READ_LEN) -
                       1; // number of read blocks in file

    for (; (nBlocks) && (PCR_INVALID == m_nLastPcr); nBlocks--) {
        fseek(m_hFile, nBlocks * READ_LEN,
              SEEK_SET); // read backwards from end of file
        if (READ_LEN !=
            fread(                    // read a chunk from file and write it into ring buffer
                    Ts,
                    1,
                    READ_LEN,
                    m_hFile)) {                     // no complete block available?
            Close();
            return;
        }
        for (uint8_t *pTs = Ts; (pTs < Ts + READ_LEN)
             && (PCR_INVALID == m_nLastPcr); pTs += 188) {
            m_nLastPcr = GetPcr(pTs);
        }
    }

    fseek(m_hFile, 0, SEEK_SET); // start reading from start of file
    ConsolePrintf( PRIO_MEDIUM, "Stream started file: %s\n", m_szFileName);
}




CSourceFile::~CSourceFile()
{
    Close();
}




void CSourceFile::Close()
{
    if (NULL != m_hFile) { 
        fclose(m_hFile);
        m_hFile = NULL;
        ConsolePrintf( PRIO_MEDIUM, "Stream closed file: %s\n", m_szFileName);
    }
}




bool CSourceFile::FillBuffer()
{
    if (true == m_bPause      ||                            // source paused?
        0 == m_Stream.Size()  ||                            // no receiving streams?
        NULL == m_hFile       ||                            // file already closed?
        !GetCanWrite()        )                             // ring buffer full?
    {
        return false;
    }
    

    if ( INVALID_POS != m_fReqPos) {            // user requested to play from other position?
        int64_t nPos = ((m_nLength * (int64_t)m_fReqPos / (int64_t)1000) /
                       (int64_t)TS_PACKET_LEN) * (int64_t)TS_PACKET_LEN;       // stay TS packet aligned

        ConsolePrintf( PRIO_MEDIUM, "Stream seek to %lld (%lld per mil) for file: %s\r\n", nPos,((int64_t)1000 * nPos / m_nLength), m_szFileName);

        m_fReqPos = INVALID_POS;

        if (PID_INVALID != GetVPid()) {          // if there is a video ES, seek to previous I frame
            uint8_t Ts[TS_PACKET_LEN];

            for (; nPos; nPos -= TS_PACKET_LEN) {
                fseeko64(m_hFile, nPos, SEEK_SET);
                if ( TS_PACKET_LEN != fread(Ts, 1, TS_PACKET_LEN, m_hFile) )
                    break;

                if (GetIsStartOfIFrame(Ts))                 // start of I frame?
                    break;
            }
        }
        fseeko64(m_hFile, nPos, SEEK_SET);                     // seek to requested position
    }


    if ( READ_LEN != fread(GetWritePos(),1,READ_LEN,m_hFile) ) { // no complete chunk available?
        ConsolePrintf( PRIO_MEDIUM, "CSourceFile: EOF for file: %s\r\n", m_szFileName);

        //TODO: remove quick hack:
        if (true || m_bRepetition)
            m_fReqPos = 0;
        else
            Close();
            
        return false;
    }

    m_nBytesRead += READ_LEN;
    WriteDone();
    return true;
}