/***************************************************************************
   This file is part of LinTV
   lintvrecord.cpp
   This is the mencoder interface for LinTV to record TV.

   Author: Jesse L. Zamora <xtremek2009@gmail.com>

   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, write to the
   Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA 02110-1301, USA.
 ***************************************************************************/

#include "lintvrecord.h"
#include "lintvprocess.h"
#include "lintvsettings.h"
#include "lintvtools.h"
#include "ivtvutils.h"
#include "lintvdevicectl.h"

#include <kdatetime.h>
#include <kapplication.h>
#include <kdebug.h>

#include <QTimer>
#include <QDir>
#include <QDateTime>
#include <QThread>

LinTVRecord::LinTVRecord(const QString & filename)
{
    m_deviceStr = filename;
    m_deviceType = LinTVDeviceInfo::File;
    m_mencoderProcess = new LinTVProcess(this);
    connectSignalsSlots();
    loadDefaultValues();
}

LinTVRecord::LinTVRecord(LinTVDeviceInfo::DeviceType deviceType,
                         const QString & deviceStr, int input)
{
    m_deviceStr = deviceStr;
    m_input = input;
    m_deviceType = deviceType;
    m_mencoderProcess = new LinTVProcess(this);
    connectSignalsSlots();
    loadDefaultValues();
}

void LinTVRecord::connectSignalsSlots()
{
    connect (m_mencoderProcess, SIGNAL(receivedStdout(KProcess *, char *, int)),
             this, SLOT(receivedStdout(KProcess *, char *, int)));
    connect (m_mencoderProcess, SIGNAL(finished(int, QProcess::ExitStatus)),
             this, SLOT(processExited(int, QProcess::ExitStatus)));
}

void LinTVRecord::loadDefaultValues()
{
    m_started = false;
    m_done = false;
    m_minutes = 0;

    setRecordDirectory(LinTVSettings::recordDirectory());
    m_mencoderProcess->setOutputChannelMode(KProcess::ForwardedChannels);
    m_recordTimer = new QTimer(this);
    m_recordTimer->setSingleShot(true);
    connect(m_recordTimer, SIGNAL(timeout()),
            this, SLOT(stop()));
}


void LinTVRecord::setRecordDirectory(QString dir)
{
    m_recordDir = LinTVTools::fullFilePath(dir);
    QDir directory(m_recordDir);
    if (!directory.exists()) {
        directory.mkdir(m_recordDir);
    }
}

void LinTVRecord::loadDefaultFilename()
{
    QDateTime dateTime = QDateTime::currentDateTime();
    QDate date = dateTime.date();
    QTime time = dateTime.time();

    m_filename = m_recordDir + "/" + date.toString("MMddyy") 
                  + "_" + time.toString("hhmmss");

    m_filename.append(videoMimeType());

    
    QFile file(m_filename);
    if (file.exists()) {
        for (int i = 0; i < 20; ++i) {
            m_filename = m_recordDir + "/" + date.toString("MMddyy")
                         + "_" + time.toString("hhmmss") + videoMimeType()
                         + "_" + QString("%1").arg(i, 3, 10, QChar('0'));
            file.setFileName(m_filename);
            if (!file.exists()) {
                return;
            }
        }
    }
}

QString LinTVRecord::videoMimeType()
{
    QString mimeType;
    QString videoFormat = LinTVSettings::videoFormat();
    if (videoFormat == "mp1" || videoFormat == "mjpeg" || videoFormat == "mp4") {
        mimeType = ".avi";
    } else if (videoFormat == "mp2") {
        mimeType = ".mpg";
    } else if (videoFormat == "wmv") {
        mimeType = ".wmv";
    } else {
        mimeType = ".avi";
    }

    return mimeType;
}

void LinTVRecord::setFilename(QString name)
{
    if (name == QString()) {
        m_filename = name;
    } else {
        m_filename = m_recordDir + "/" + name + videoMimeType();
    }
}

void LinTVRecord::loadArgs()
{
    int keyint;

    m_mencoderArgs.clear();
    
    m_mencoderArgs << "mencoder";
    if (m_deviceType == LinTVDeviceInfo::File) {
        m_mencoderArgs << QString("%1").arg(m_deviceStr);
    } else if (m_deviceType == LinTVDeviceInfo::TV) {
        if (m_channel != -1) {
            m_mencoderArgs << QString("tv://%1").arg(m_channel);
        } else {
            m_mencoderArgs << "tv://";
        }
        m_mencoderArgs << "-tv";
        QString tvStr = QString("device=%1:input=%2:norm=%3:immediatemode=0:buffersize=20"
                                  ":audiorate=%4")
                                      .arg(m_deviceStr)
                                      .arg(m_input)
                                      .arg(LinTVSettings::videoStandard())
                                      .arg(LinTVSettings::audioFrequency());
        if (LinTVSettings::audioChannels() == 0) {
            tvStr.append(":noaudio");
        } else {
            tvStr.append(QString(":amode=%1").arg(LinTVSettings::audioChannels() - 1));
        }

        m_mencoderArgs << tvStr;
        
    } else if (m_deviceType == LinTVDeviceInfo::PVR) {
        m_mencoderArgs << m_deviceStr;
    }

    // Set the framerate parameter
    m_mencoderArgs << "-ofps";
    if (LinTVSettings::videoFramerate() == 2997) {
        m_mencoderArgs << QString("30000/1001");
    } else {
        m_mencoderArgs << QString("%1").arg(LinTVSettings::videoFramerate());
    }

    QString videoFormat = LinTVSettings::videoFormat();
    QString videoCodec;
    QString audioCodec;

    keyint = 132;
    audioCodec = LinTVSettings::audioFormat();
    if (videoFormat == "mp1") {
        videoCodec = "mpeg1video";
        keyint = 0;
    } else if (videoFormat == "mp2") {
        videoCodec = "mpeg2video";
        keyint = 0;
    } else if (videoFormat == "mp4") {
        videoCodec = "msmpeg4";
    } else if (videoFormat == "mjpeg") {
        videoCodec = "mjpeg";
    } else if (videoFormat == "wmv") {
        videoCodec = "wmv2";
        audioCodec = "wmav2";
    }
  
    if (keyint == 0) {
        m_mencoderArgs << "-of";
        m_mencoderArgs << "mpeg";
        m_mencoderArgs << "-mpegopts";
        m_mencoderArgs << "muxrate=20000";
    }

    m_mencoderArgs << "-oac";
    if (audioCodec == "mp3") {
        m_mencoderArgs << "mp3lame";
    } else {
        m_mencoderArgs << "lavc";
    }
    m_mencoderArgs << "-ovc";
    m_mencoderArgs << "lavc";
    m_mencoderArgs << "-lavcopts";
    m_mencoderArgs << QString("acodec=%1:abitrate=%2:vcodec=%3:vbitrate=%4:autoaspect=1:mbd=1:keyint=%5")
                                          .arg(audioCodec).arg(LinTVSettings::audioBitrate())
                                          .arg(videoCodec).arg(LinTVSettings::videoBitrate())
                                          .arg(keyint);

    // Calculate the crop area...
    int x, y;
    int width, height;

    m_mencoderArgs << "-vf";

    x = LinTVSettings::videoCropLeft();
    y = LinTVSettings::videoCropTop();
    width = LinTVSettings::videoWidth() - LinTVSettings::videoCropRight() - x;
    height = LinTVSettings::videoHeight() - LinTVSettings::videoCropBottom() - y;
    m_mencoderArgs << QString("scale=%1:%2,crop=%1:%2:%3:%4").arg(width).arg(height).arg(x).arg(y);

    m_mencoderArgs << "-o";
    m_mencoderArgs << QString("%1").arg(m_filename);

    m_mencoderArgs << "-quiet";
    kDebug() << m_mencoderArgs;

    m_mencoderProcess->setProgram(m_mencoderArgs);
}

void LinTVRecord::start()
{
    if (m_filename == QString()) {
        loadDefaultFilename();
    }
    loadArgs();

    kDebug() << "Started recording";

    m_mencoderProcess->start();
    QTimer::singleShot(5000, this, SIGNAL(startedRecording()));
    if (m_minutes > 0) {
        kDebug() << "Let's record for" << m_minutes << "minutes!";
        // from minutes to -> seconds -> milli-seconds
        m_recordTimer->start(m_minutes * 61 * 1000);
    }
}

void LinTVRecord::stop()
{    
    m_recordTimer->stop();
    m_mencoderProcess->terminate();
    m_mencoderProcess->waitForFinished();
    m_done = true;
}

bool LinTVRecord::isRecording()
{
    return m_mencoderProcess->isRunning();
}

void LinTVRecord::receivedStdout(KProcess *, char * buffer, int buflen)
{
    QString buf = QString::fromLatin1(buffer, buflen);
}

void LinTVRecord::processExited(int exitCode, QProcess::ExitStatus exitStatus)
{
    if (exitStatus == QProcess::NormalExit) {
    } else {
        kDebug() << "Handle this with an error message to the user...";
    }
    emit finished(exitCode, exitStatus);
}

void LinTVRecord::setInput(int input)
{
    LinTVDeviceCtl::setInput(m_deviceStr, input);

    m_input = input;
}

void LinTVRecord::setChannel(int channel)
{
    if (m_deviceType == LinTVDeviceInfo::PVR) {
        IVTV_Utils::setChannel(channel, m_deviceStr);
    }

    m_channel = channel;
}
void LinTVRecord::setDeviceStr(QString deviceStr)
{
    m_deviceStr = deviceStr;
}

void LinTVRecord::setDeviceType(LinTVDeviceInfo::DeviceType deviceType)
{
    m_deviceType = deviceType;
}

#include "lintvrecord.moc"