/*****************************************************************************
 *
 *  mediasplitter.cpp -- Audio/Video file splitter.
 *
 *  Copyright (C) 2010 Starbuck <starbuck@labdv.com>
 *  All rights reserved.
 *
 *  This file is part of the JobManager module of the DVDx 4 application.
 *
 *  GNU Lesser General Public License Usage
 *  This file may be used under the terms of the GNU Lesser General
 *  Public License version 2.1 as published by the Free Software
 *  Foundation and appearing in the file LICENSE.LGPL included in the
 *  packaging of this file.  Please review the following information to
 *  ensure the GNU Lesser General Public License version 2.1 requirements
 *  will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
 *
 *  GNU General Public License Usage
 *  Alternatively, this file may be used under the terms of the GNU
 *  General Public License version 3.0 as published by the Free Software
 *  Foundation and appearing in the file LICENSE.GPL included in the
 *  packaging of this file.  Please review the following information to
 *  ensure the GNU General Public License version 3.0 requirements will be
 *  met: http://www.gnu.org/copyleft/gpl.html.
 *
 *****************************************************************************/

#include "mediasplitter.h"
#include <QDebug>
#include <QFile>
#include <QFileInfo>
#include <QTextEdit>
#include <QTime>

extern FFmpeg *ffmpeg;
extern FFprobe *ffprobe;
extern MEncoder *mencoder;

MediaSplitter::MediaSplitter(JobManager *jobManager)
	: MediaEditor(jobManager)
{
	m_ffmpeg = ffmpeg;
	m_ffprobe = ffprobe;
	m_mencoder = mencoder;
	m_partString = ".part";  // for "movie.part1.avi"
}

void MediaSplitter::start(const QString &inputFileName, const int partNumber, const qint64 partSize)
{
	qDebug() << "Start split jobs:" << partNumber << partSize << inputFileName;

	if (!m_ffmpeg->interfaceStart())
	{
		m_console->log(Console::Quiet, tr("Failed to start split: could not start FFmpeg interface."));
		return;
	}

	if (!m_ffprobe->interfaceStart())
	{
		m_console->log(Console::Quiet, tr("Failed to start split: could not start FFprobe interface."));
		return;
	}

	// Initialize input
	m_inputPath = inputFileName;
	m_volumeNumber = partNumber;
	m_volumeSize = partSize;

	// Analyze input
	QString probeDuration;

	if (!m_ffprobe->load(m_inputPath))
	{
		m_console->log(Console::Quiet, tr("Failed to start split: could not analyze input media file"));
		return;
	}

	if (!m_ffprobe->getDataFormat(&probeDuration, "duration"))
	{
		m_console->log(Console::Quiet, tr("Failed to start split: could not get input media duration"));
		return;
	}

	m_inputDuration = probeDuration.toDouble();
	
	// Initialize output
	m_currentPart = 0;
	m_currentPosition = 0.0;

	// From "/tada/bla/movie.avi" 
	// we do --> "movie.part" for "movie.part1.avi" in output folder
	//    or --> "movie (1).part" if movie.part*.avi exists in output folder
	//    or --> "movie (2).part" if movie (1).part*.avi exists in output folder

	QFileInfo fi(inputFileName);
	QString baseName = fi.completeBaseName();
	QString outputDir = m_outputFolder.isEmpty() ? fi.canonicalPath() : m_outputFolder;

	m_inputSize = fi.size();
	m_outputExtension = fi.suffix();

	int newSlot = 0;
	bool freeSlot(false);
	while (newSlot < 99 && !freeSlot)  // limit to reasonable 99 avoids infinite loop
	{
		bool allFree(true);

		for (int part = 1; part <= m_volumeNumber; part++)
		{
			if (newSlot == 0)
			{
				allFree &= !QFile::exists(QString("%1/%2%3%4.%5")
					.arg(outputDir).arg(baseName)
					.arg(m_partString).arg(part).arg(m_outputExtension));
			}
			else
			{
				allFree &= !QFile::exists(QString("%1/%2 (%6)%3%4.%5")
					.arg(outputDir).arg(baseName)
					.arg(m_partString).arg(part).arg(m_outputExtension).arg(newSlot));
			}
		}

		if (allFree)
		{
			freeSlot = true;
		}
		else
		{
			newSlot++;
		}
	}

	if (newSlot == 0)
	{
		m_outputBaseName =  QString("%1%2").arg(baseName).arg(m_partString);
	}
	else
	{
		m_outputBaseName =  QString("%1 (%2)%3").arg(baseName).arg(newSlot).arg(m_partString);
	}
	
	startNextJob();
}

void MediaSplitter::startNextJob()
{
	if (m_currentPart == m_volumeNumber)
	{
		emit finished();
		return;
	}

	qDebug() << "Start next split:" << m_currentPart << m_inputPath << m_outputBaseName;

	double previousDuration(0.0);
	
	m_currentPart++;

	if (!m_currentOutputPath.isEmpty())
	{
		QString probeDuration;
	
		if (!m_ffprobe->load(m_currentOutputPath))
		{
			m_console->log(Console::Quiet, tr("Failed to start split part #%1 of %2: could not analyze input media file"));
			emit finished();
			return;
		}

		if (!m_ffprobe->getDataFormat(&probeDuration, "duration"))
		{
			m_console->log(Console::Quiet, tr("Failed to start split part #%1 of %2: could not get input media duration"));
			emit finished();
			return;
		}

		previousDuration = probeDuration.toDouble();
	}

	m_currentPosition += previousDuration;

	if (m_currentPosition == 0 && m_currentPart > 1) 
	{
			m_console->log(Console::Quiet, tr("Failed (position error) to continue split of %1")
				.arg(QFileInfo(m_inputPath).fileName()));
			emit finished();
			return;
	}

	if (m_currentPosition >= m_inputDuration) 
	{
			m_console->log(Console::Quiet, tr("Warning: done in %1 part instead of %2, terminating split of %3")
				.arg(int(m_currentPart - 1)).arg(m_volumeNumber).arg(QFileInfo(m_inputPath).fileName()));
			emit finished();
			return;
	}

	QString cmd, error;
	QStringList args;
	
	// Current goes next output
	m_currentOutputPath = QString("%1/%2%3.%4")
		.arg(m_outputFolder).arg(m_outputBaseName).arg(m_currentPart).arg(m_outputExtension);
	
	QString start = QTime().addMSecs(m_currentPosition * 1000).toString("hh:mm:ss");

	qint64 currentPartSize = (m_currentPart == m_volumeNumber) ? 0 : m_volumeSize;

	// Example:
	// pos = 00:00:00
	// ffmpeg -vcodec copy -acodec copy -ss pos -fs partSize -i infile.mpg infile.part1.mpg
	// pos = duration of infile.part1.mpg
	// ffmpeg -vcodec copy -acodec copy -ss pos -fs partSize -i infile.mpg infile.part2.mpg
	// pos = pos + duration of infile.part2.mpg
	// ffmpeg -vcodec copy -acodec copy -ss pos -fs partSize -i infile.mpg infile.part3.mpg
	// etc...
	if (!m_ffmpeg->getCommandSplit(&cmd, &args, &error, 
						m_inputPath, m_currentOutputPath, start, currentPartSize))
	{
		m_console->log(Console::Quiet, tr("Failed to start split %1: %2")
			.arg(QFileInfo(m_inputPath).fileName()).arg(error));
		return;
	}

	// Start job
	m_console->log(Console::Verbose, tr("Start FFmpeg job with %1").arg(args.join(" ")));

	QString title = tr("Splitting");
	QString description = tr("Extract volume %1/%2 from %3")
		.arg(m_currentPart).arg(m_volumeNumber).arg(QFileInfo(m_inputPath).fileName());

	qint64 remainingSize = m_inputSize - (m_volumeSize * (m_currentPart - 1));
	qint64 jobFileSize = (remainingSize <  m_volumeSize) ? remainingSize : m_volumeSize;

	Job *currentJob = m_jobManager->addJob( &cmd, &args, 
		m_currentOutputPath, title, description, m_ffmpeg, jobFileSize );

	if (currentJob)
	{
		if (m_currentPart == 1) emit started();

		if (currentJob->isFinished())
		{
			startNextJob();  // for ultra fast jobs!
		}
		else
		{
			connect(currentJob, SIGNAL(finished()), this, SLOT(startNextJob()));
		}
	}
	else
	{
		m_console->log(Console::Quiet, tr("Failed to start split part #%1 of %2")
			.arg(m_currentPart).arg(QFileInfo(m_inputPath).fileName()));
		emit finished();
	}
}
