/*
 * Video On Demand Samples
 *
 * Copyright (C) 2016 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 <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <errno.h>
#include "Console.h"
#include "SourceFileConverted.h"


CSourceFileConverted::CSourceFileConverted(const char *szFile, bool autoDestroy)
    : m_hFile(-1)
    , m_nLength(0)
    , m_nChildPid(0)
{
    m_bAutoDestroy = autoDestroy;
    strncpy(m_szFileName, szFile, sizeof(m_szFileName));
    m_nLength = 6*60;       // TODO: determine actual stream length, assume 6 minutes for now...

    Start(0);

    m_nPPid = 0x1000;       // avconv default / -mpegts_pmt_start_pid X
    m_nAPid = 0x101;        // avconv default / -streamid 1:X
    m_nVPid = 0x100;        // avconv default / -streamid 0:X

    m_nLastPcr = PCR_INVALID;
}




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




void CSourceFileConverted::Start(int64_t nPos)
{
    // fork() and start avconv in child:
    int fd[2];
    if (pipe(fd)) {
        ConsolePrintf(PRIO_ERROR, RED"CSourceFileConverted: failed to open pipe for %s"RESETCOLOR"\n", m_szFileName);
        return;
    }
    pid_t forkret = fork();
    char *ext;
    char chstr[20] = "";
    switch (forkret) {
    case 0:
        /* the child process: */
        close(fd[0]);
        dup2(fd[1], 1);		// close stdout, duplicate input side of the pipe to stdout
//        execl("/bin/cat", "/bin/cat", m_szFileName, NULL);
        sprintf(chstr, "%lli", nPos);
        ext = rindex(m_szFileName, '.');
        if (ext && !strcasecmp(ext, ".mp3"))
            execl("/usr/bin/avconv", "/usr/bin/avconv",
                "-ss", chstr, "-i", m_szFileName,
                "-f", "mpegts", "-codec", "copy",
                "-map", "0:a:0",
                "-streamid", "0:0x101", "-mpegts_pmt_start_pid", "0x1000",
                "pipe:1", NULL);
        else
            execl("/usr/bin/avconv", "/usr/bin/avconv",
                "-ss", chstr, "-i", m_szFileName,
                "-f", "mpegts", "-codec", "copy", "-bsf:v", "h264_mp4toannexb",
                "-map", "0:v:0", "-map", "0:a:0",
                "-streamid", "0:0x100", "-streamid", "1:0x101", "-mpegts_pmt_start_pid", "0x1000",
                "pipe:1", NULL);
        ConsolePrintf(PRIO_ERROR, RED"CSourceFileConverted: execl() call failed for %s"RESETCOLOR"\n", m_szFileName);
        exit(0);
    case -1:
        /* fork() failed */
        close(fd[0]);
        close(fd[1]);
        ConsolePrintf(PRIO_ERROR, RED"CSourceFileConverted: failed to fork for %s"RESETCOLOR"\n", m_szFileName);
        return;
    default:
        /* parent process after fork: */
        m_nChildPid = forkret;
        close(fd[1]);
        m_hFile = fd[0];   /* read from child via pipe */
    }

    m_nLastPcr = PCR_INVALID;

    ConsolePrintf( PRIO_MEDIUM, "Stream started file: %s\n", m_szFileName);
}




void CSourceFileConverted::Close()
{
    if (m_nChildPid > 0) {
        ConsolePrintf(PRIO_LOW, "sending SIGKILL to pid %u\n", m_nChildPid);
        kill(m_nChildPid, SIGKILL);
        ConsolePrintf(PRIO_LOW, "waiting for child to exit...\n");
        int childStatus = 0;
        waitpid(m_nChildPid, &childStatus, 0);
        m_nChildPid = 0;
        if (WIFEXITED(childStatus))
            ConsolePrintf(PRIO_LOW, "child terminated normally (exit status %u)\n", WEXITSTATUS(childStatus));
        else if (WIFSIGNALED(childStatus))
            ConsolePrintf(PRIO_LOW, "child terminated by signal number %u\n", WTERMSIG(childStatus));
    }
    if (m_hFile > 0) { 
        close(m_hFile);
        m_hFile = -1;
        ConsolePrintf( PRIO_MEDIUM, "Stream closed file: %s\n", m_szFileName);
    }
}




bool CSourceFileConverted::FillBuffer()
{
    if (true == m_bPause      ||                            // source paused?
        0 == m_Stream.Size()  ||                            // no receiving streams?
        m_hFile < 0           ||                            // 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 / 1000);

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

        m_fReqPos = INVALID_POS;

        Close();
        Start(nPos);
    }


    unsigned int readcnt = 0;
    while (readcnt < READ_LEN) {
        int readres = read(m_hFile, GetWritePos()+readcnt, READ_LEN-readcnt);
        if (readres <= 0) {     // remote pipe end closed, or other error?
            ConsolePrintf( PRIO_MEDIUM, "CSourceFileConverted: EOF for file: %s (result=%i, errno=%i)\r\n", m_szFileName, readres, errno);

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

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