/*****************************************************************************
 *
 *  encoderwidget.cpp -- Container for encoder controls.
 *
 *  Copyright (C) 2010 Starbuck <starbuck@labdv.com>
 *  All rights reserved.
 *
 *  This file is part of the Gui4 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 "../video/video_struct.h"
#include "../video/video_constants.h"
#include "../video/video_functions.h"

#include "encoderwidget.h"
#include "progresswidget.h"
#include "encoderconfiguredialog.h"
#include "playerdialog.h"
#include "mediainfodatadialog.h"
#include "infoboxdialog.h"
#include "macros.h"
#include "widgetbridge.h"
#include <QFileInfo>
#include <QProcess>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QGridLayout>
#include <QLayoutItem>
#include <QWidget>
#include <QSizePolicy>
#include <QPushButton>
#include <QPalette>
#include <QTextEdit>
#include <QLineEdit>
#include <QSlider>
#include <QCloseEvent>
#include <QString>
#include <QTimer>
#include <QTime>
#include <QString>
#include <QStringList>
#include <QRegExp>
#include <QDebug>
#include <QSettings>
#include <QCoreApplication>
#include <QDir>

Q_DECLARE_METATYPE( QLayout * );

EncoderWidget::EncoderWidget(QWidget *refConsole, JobManager *jobManager,
		const QString &ffmpegPath, const QString &mencoderPath,
		QTextEdit *stdIn, QTextEdit *stdOut, QTextEdit *stdErr,
		QTextEdit *defLog, const int verbosityLevel, Preferences *prefs, QWidget *parent)
	: QWidget(parent),
	encoder(0),
	controllerButtonIsActivable(false), 
	isEncoding(false), isEncodingStartable(false), isAbortedByUser(false)
{	
	ffmpegBinPath = ffmpegPath;
	mencoderBinPath = mencoderPath;

	this->jobManager = jobManager;

	// Default
	if ( prefs != 0 )
	{
		preferences = *prefs;
	}

	// Set IO channels from parent channels
	standardInput = stdIn;
	standardOutput = stdOut;
	standardError = stdErr;
	log = defLog;
	verbosity = verbosityLevel;

	// Encoder
	encoder = new Encoder(stdIn, stdOut, stdErr, defLog, verbosity); 

	// Configurator
	configurator = new Configurator(encoder);

	setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding );

	// Background for the whole widget
	backFrame = new QFrame(this);
	backFrame->setAutoFillBackground(true);
	backFrame->setFrameShape(QFrame::Panel);
	backFrame->setFrameShadow(QFrame::Sunken);
	backFrame->setLineWidth(0);
	backFrame->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored));
	QPalette palette;
	palette.setColor(QPalette::Background, Qt::lightGray);
	backFrame->setPalette(palette);
	backFrame->resize( size() );

	// Control widgets
	controllerButton = new QPushButton();
	controllerButton->setDisabled(true);

	// Volume control
	volumeSpin = new QSpinBox();
	volumeSpin->setToolTip(tr("Audio correction"));
	volumeSpin->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Maximum);
	volumeSpin->setMinimumWidth(50);
	volumeSpin->setRange(-48, 48);
	volumeSpin->setSingleStep(3);
	volumeSpin->setPrefix(" ");
	volumeSpin->setSuffix(" dB");

	// Configuration
	muxerSelectCombo = new ActiveComboBox();
	muxerSelectCombo->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Maximum );
	muxerSelectCombo->setToolTip(tr("Select output format"));
	configureButton = new QPushButton(tr("Configure..."));
	configureButton->setToolTip(tr("Configure video picture size, aspect ratio, bitrate, deinterlacing..."));
	configureButton->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Ignored));
	codecButton = new QPushButton(tr("Codecs"));
	codecButton->setToolTip(tr("Show/Hide codec selection bar"));
	codecButton->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Ignored));

	// Selections
	videoSelectCombo = new ActiveComboBox();
	videoSelectCombo->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Maximum );
	videoSelectCombo->setToolTip(tr("Select video codec"));
	audioSelectCombo = new ActiveComboBox();
	audioSelectCombo->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Maximum );
	audioSelectCombo->setToolTip(tr("Select audio codec"));
	subtitleSelectCombo = new ActiveComboBox();
	subtitleSelectCombo->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Maximum );
	subtitleSelectCombo->setToolTip(tr("Select subtitle codec"));

	// Output file
	targetFolderLabel = new QLabel(tr("Path:"));
	targetFolderLine = new QLineEdit(this);
	targetFolderLine->setFrame(false);
	targetFolderLine->setReadOnly(true);
	targetFolderLine->setToolTip(tr("Current output folder"));
	targetNameLabel = new QLabel(tr("File:"));
	targetNameLine = new QLineEdit(this);
	targetNameLine->setFrame(false);
	targetNameLine->setReadOnly(true);
	targetNameLine->setToolTip(tr("Current output file name"));
	targetFolderLabel->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Maximum );
	targetFolderLine->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Maximum );
	targetNameLabel->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Maximum );
	targetNameLine->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Maximum );
	testCheckBox = new QCheckBox(tr("Preview"));
	testCheckBox->setStatusTip(tr("Check to encode an input excerpt and preview encoding output"));

	// Settings
	settingsLabel = new QLabel(tr("No encoding profile selected"), this);
	settingsLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);

	// The single job handling (interactive encoder with job tools)
	previewJobManager = new JobManager(standardInput, standardOutput, standardError, log, verbosity,
				preferences.latency_start, 
				preferences.latency_finished, 
				preferences.latency_kill);

	// The job manager widget
	previewJobManagerWidget = new JobManagerWidget(this);
	previewJobManagerWidget->setJobManager(previewJobManager);
	previewJobManagerWidget->setNoStateDisplay(true);
	previewJobManagerWidget->setNoConsoleButton(!showConsoleButton);
	previewJobManagerWidget->setNoMiniConsoleGroup(true);
	previewJobManagerWidget->setIdleLabel( tr("No encoding profile selected") );

	// The preview dialog
	encoderPreviewDialog = new EncoderPreviewDialog( tr("%1 - Encoder : preview job").arg(APPTITLE), 
		previewJobManagerWidget, &playerPreferences, refConsole );

	// Control layouts
	QHBoxLayout *buttons = new QHBoxLayout;
	buttons->addWidget(controllerButton);
	buttons->addStretch(1);
	buttons->addWidget(volumeSpin);

	QHBoxLayout *configuration = new QHBoxLayout;
	configuration->addWidget(muxerSelectCombo);
	configuration->addWidget(codecButton);
	configuration->addWidget(configureButton);
	
	codecSelectionWidget = new QWidget();
	codecSelectionWidget->setVisible (false);

	QHBoxLayout *selection = new QHBoxLayout(codecSelectionWidget);
	selection->setContentsMargins(0, 0, 0, 0);
	selection->addWidget(videoSelectCombo);
	selection->addWidget(audioSelectCombo);
	selection->addWidget(subtitleSelectCombo);

	QHBoxLayout *path = new QHBoxLayout;
	path->addWidget(targetFolderLabel);
	path->addWidget(targetFolderLine);
	path->addWidget(targetNameLabel);
	path->addWidget(targetNameLine);
	path->addWidget(testCheckBox);

	QVBoxLayout *target = new QVBoxLayout;
	target->addLayout(configuration);
	target->addWidget(codecSelectionWidget);
	target->addLayout(path);
	
	targetGroup = new QGroupBox(tr("Target"));
	targetGroup->setLayout(target);

	QVBoxLayout *settings = new QVBoxLayout;
	settings->addWidget(settingsLabel);
	settings->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
	
	settingsGroup = new QGroupBox(tr("Settings"));
	settingsGroup->setLayout(settings);

	controls = new QVBoxLayout;
	controls->addLayout(buttons);
	controls->addWidget(targetGroup);
	controls->addWidget(settingsGroup);

	setLayout(controls);

	// Feed some control data
	testCheckBox->setChecked(preferences.test_mode);
	switchTestMode(preferences.test_mode);
	volumeSpin->setValue(preferences.volume_correction);
	adjustAudioCorrection(preferences.volume_correction);

	// Until a movie is loaded
	enableControls(false);

	// Slots/signals
	connect(muxerSelectCombo, SIGNAL(activated(int)), this, SLOT(selectMuxer()));
	connect(videoSelectCombo, SIGNAL(activated(int)), this, SLOT(selectVideo()));
	connect(audioSelectCombo, SIGNAL(activated(int)), this, SLOT(selectAudio()));
	connect(subtitleSelectCombo, SIGNAL(activated(int)), this, SLOT(selectSubtitle()));
	connect(controllerButton, SIGNAL(clicked()), this, SLOT(switchState()));
	connect(codecButton, SIGNAL(clicked()), this, SLOT(codecBarFlipFlop()));
	connect(configureButton, SIGNAL(clicked()), this, SLOT(configure()));
	connect(testCheckBox, SIGNAL(stateChanged(int)), this, SLOT(switchTestMode(int)));
	connect(volumeSpin, SIGNAL(valueChanged(int)), this, SLOT(adjustAudioCorrection(int)));
	
	connect(this, SIGNAL(optionsChanged()), this, SLOT(loadProfileSettings()));

	connect(encoderPreviewDialog, SIGNAL(abortedByUser()), this, SLOT(abortedByUser()));

	connect(previewJobManager, SIGNAL(processStarted(Task *)), 
		this, SLOT(startProcessConsole(Task *)));

	connect(previewJobManager, SIGNAL(processStdOutReady(Process *)), 
		this, SLOT(readJobProcessStdOut(Process *)));
	connect(previewJobManager, SIGNAL(processStdErrReady(Process *)), 
		this, SLOT(readJobProcessStdErr(Process *)));
	connect(previewJobManager, SIGNAL(processFinished(Process *,int, QProcess::ExitStatus )), 
		this, SLOT(finishJobProcess(Process *, int, QProcess::ExitStatus)));

	connect(this, SIGNAL(encodingComplete(Process *)), 
		this, SLOT(finalizeEncoding(Process *)));

	// Must be done when codecButton is created
	readSettings();
}


EncoderWidget::~EncoderWidget()
{
	QSettings settings;

	if (saveSettingsOnClose) 
	{
		encoder->writeSettings();

		// Save last selected format
		int item = muxerSelectCombo->currentIndex();
		QString muxer =  muxerSelectCombo->itemData( item ).toString();

		// Common options
		settings.beginGroup("Options");

		// Encoder options
		settings.beginGroup("Encoder");
	    settings.setValue("last format", muxer);
		settings.endGroup();

		settings.endGroup(); // Common options
	}
}

void EncoderWidget::setPlayerPreferences(QWidget *refConsole,
	const QString &mplayerPath, const QString &mediainfoPath,  const QString &ffprobePath,
	QTextEdit *stdIn, QTextEdit *stdOut, QTextEdit *stdErr,	
	QTextEdit *defLog, const int verbosityLevel,
	PlayerWidget::Preferences *playerWidgetPreferences)
{
	playerPreferences.referenceConsole = refConsole;
	playerPreferences.mplayerPath = mplayerPath;
	playerPreferences.mediainfoPath = mediainfoPath;
	playerPreferences.ffprobePath = ffprobePath;
	playerPreferences.inputChannel = stdIn;
	playerPreferences.outputChannel = stdOut;
	playerPreferences.errorChannel = stdErr;
	playerPreferences.logChannel = defLog;
	playerPreferences.verbosity = verbosityLevel;
	playerPreferences.playerPreferences = *playerWidgetPreferences;
}

void EncoderWidget::initialize()
{
	encoder->initialize();

	encoder->activateInterface(Video::Encoder::FFmpeg, ffmpegBinPath);
	encoder->activateInterface(Video::Encoder::MEncoder, mencoderBinPath);

	adjustAudioCorrection(preferences.volume_correction);

	// Settings to reflect widget selections
#ifdef WIN32
#pragma message (ToBeDone "default target muxer should not be hard-coded - " __FUNCTION__)
#endif
	QString recommendedMuxer, recommendedVideo, recommendedAudio, recommendedSubtitle;

	recommendedMuxer = encoder->getOutputMuxer();
	if ( recommendedMuxer.isEmpty() ) recommendedMuxer = "avi";

	encoder->recommendedMuxerCodecs( recommendedMuxer,
				&recommendedVideo,
				&recommendedAudio,
				&recommendedSubtitle );
	
	populateOptions( recommendedMuxer,
				recommendedVideo,
				recommendedAudio,
				recommendedSubtitle );

	selectMuxer();

	// Update file extension and target controls
	encoder_target_t target = encoder->getOutputFile();
	setOutputFile( target.dir, target.name );
}

void EncoderWidget::abortedByUser()
{
	isAbortedByUser = true;
}

void EncoderWidget::getStatus(bool *ffmpegReady, bool *mencoderReady)
{
	*ffmpegReady = isReady(Video::Encoder::FFmpeg) ;
	*mencoderReady = isReady(Video::Encoder::MEncoder);
}

void EncoderWidget::setBinPath(const int engine,  const QString &path)
{
	encoder->setBinPath( engine, path );
}

void EncoderWidget::setPreferences(Preferences *prefs)
{
	preferences = *prefs;

	encoder->setLatency(Video::Encoder::FFmpeg, preferences.latency_start, preferences.latency_finished, preferences.latency_kill);
	encoder->setLatency(Video::Encoder::MEncoder, preferences.latency_start, preferences.latency_finished, preferences.latency_kill);
}

void EncoderWidget::resetPreferences()
{
	preferences.reset();
}

void EncoderWidget::enableControls(bool yes)
{
	controllerButton->setEnabled( yes && controllerButtonIsActivable );
	testCheckBox->setEnabled( yes );
	volumeSpin->setEnabled( yes );
	targetGroup->setEnabled( yes );
	settingsGroup->setEnabled( yes );
}

void EncoderWidget::switchState()
{
	if (isEncoding)
	{
		// Confirm to kill encoding process
		InfoBoxDialog infoBox(this);

		infoBox.setText(tr("Abort encoding process?"));

		infoBox.addButton(tr("Abort encoding"), QMessageBox::AcceptRole);
		infoBox.addButton(tr("Continue encoding"), QMessageBox::RejectRole);

		if (infoBox.exec() == QMessageBox::AcceptRole) 
		{
			previewJobManager->abortProcesses();
			isEncoding = false;
			isEncodingStartable = false;
			isAbortedByUser = true;

			printLog( tr("Encoding aborted."), VERBOSITY_QUIET );
		}
	}
	else if (!isEncoding && isEncodingStartable)
	{
		if (runEncoding() && preferences.test_mode)
		{
			encoderPreviewDialog->setEncoding();
			encoderPreviewDialog->show();
		
			isEncoding = true;
			isEncodingStartable = false;
			isAbortedByUser = false;
			enableControls(false);
		}
	}
	else if (previewJobManager->isIdle())
	{
		encoderPreviewDialog->hide();
		encoderPreviewDialog->setEncoding();
		previewJobManagerWidget->reset();
		
		isEncoding = false;
		isEncodingStartable = true;

		loadProfileSettings();
		enableControls(true);
	}
}

QString EncoderWidget::newFileNameInFolder(const QString &folder, const QString &filename)
{
	// Names are "filename (1)", "filename (2)" etc... whatever extension
	QString newSlot;
	QStringList list;

	QDir dir(folder);

	list = dir.entryList( QStringList( QString("%1.*").arg(filename) ),
		QDir::Files | QDir::Hidden | QDir::NoDotAndDotDot );

	if (list.isEmpty()) return filename;
	
	list = dir.entryList( QStringList( QString("%1 (*).*").arg(filename) ),
		QDir::Files | QDir::Hidden | QDir::NoDotAndDotDot );

	int next(0);

	QRegExp paranthesis("[\\(\\)]");
	while ( !list.isEmpty() )
	{
		QString str = list.takeFirst();
		QString strIndex = str.section( paranthesis, -2, -2 );
		int index = strIndex.toInt();

		if (index > next) next = index;
	}

	next++;

	newSlot = QString("%1 (%2)").arg(filename).arg(next);

	return newSlot;
}

bool EncoderWidget::runEncoding()
{
	encoder_target_t target = encoder->getOutputFile();

	QString jobOutput = newFileNameInFolder( target.dir, target.name ) 
		+ Encoder::recommendsFileExtension( encoder->getOutputMuxer() );
	
	QString output = target.dir + "/" + jobOutput;

	double display_aspect = encoder->getInputSettings()->video.display_aspect;
	double length = encoder->getInputSettings()->video.length;

	QString cmd;
	QStringList args;
	int engine;

	// Test mode stuff
	int frames = preferences.test_mode ? preferences.test_frames : 0;  // number of frames to encode (0: no limit)
	int chapter = preferences.test_mode ? preferences.test_chapter : 0;  // chapter to start from (0: very beginning)
	int seek = preferences.test_mode ? preferences.test_position : 0;  // chapter to start from (0: very beginning)

	// Ensure seek position for FFmpeg
	if (seek >= (length - 10.0)) seek = qMax(0.0, length - 10.0);

	encoder->cliChooseCmdEncode(&engine, &cmd, &args, output, frames, chapter, seek);

	qDebug() << "EncoderWidget::runEncoding command line" << cmd << args;

	QString testStdIn, testStdOut, testStdErr; // test encoder command
	
	QString input = encoder->getInputFilePath();

	if ( !encoder->isCmdEncodeValid( engine, &cmd, &args, &output,
		&testStdIn, &testStdOut, &testStdErr, preferences.validation_frames ) )
	{
		printLog( tr("Failed to start encoding."), VERBOSITY_QUIET );

		QString description = tr(	"- input: %1\n"
									"- output: %2\n"
									"- command: %3 %4\n"
									"- response:\n"
									"%5\n"
									"%6")
									.arg(input)
									.arg(output).arg(cmd).arg(args.join(" "))
									.arg(testStdOut)
									.arg(testStdErr);
		
		InfoBoxDialog errorBox(this);
		errorBox.setText(tr("Encoding has failed to start."));
		errorBox.setDetailedText(description);
		errorBox.exec();

		return false;
	}

	// We're quiet sure to succeed so create a new job
	QString inputBaseName;

	if ( encoder->getInputSettings()->source.media == Video::Media::DVD )
	{
		inputBaseName = QString("%1 Title_%2").arg(target.name)
			.arg(QString::number(encoder->getInputSettings()->video.title));
	}
	else
	{
		inputBaseName = target.name;
	}
	
	// Job and task name
	QString format = encoder->muxerShortName(encoder->getOutputMuxer());
	if ( !encoder->getInputSettings()->video.standard.isEmpty() ) 
	{
		format.append( " (" + encoder->getInputSettings()->video.standard + ")" );
	}
	
	QString name = tr("Encoding %1 to %2 format")
					.arg(inputBaseName).arg(format);
	QString title = tr("Encoding");

	// We set expected aspect ratio to correct encoder issue
	// - MEncoder lavc encodes OK but miswrites in output file
	double aspect_ratio(0.0);

	if (display_aspect > 1.31 && display_aspect < 1.35)
	{
		aspect_ratio = 1.33;
	}
	else if (display_aspect > 1.75 && display_aspect < 1.79)
	{
		aspect_ratio = 1.78;
	}

	Task *task = new Task;

	QList<QVariant> *data = new QList<QVariant>;
	*data << QVariant(engine);
	*data << QVariant(encoder->getInputSettings()->video.length);
	if (engine == Video::Encoder::FFmpeg)
	{
		*data << QVariant(encoder->getEstimatedOutputFileSize(engine, frames));
	}
	else
	{
		*data << QVariant(frames);
	}
	*data << QVariant(aspect_ratio);
	*data << QVariant(0);  // run_count
	*data << QVariant(output);
	*data << QVariant(encoder->getOutputMuxer());
	*data << QVariant(preferences.test_mode);  // true for preview, false for job manager
	*data << QVariant();  // slot for layout

	task->setCommandLine( data, cmd, args, encoder->getOutputFile().dir, name, title); 

	// Now we can safely create and process a new job
	Job *job = new Job;

	if (preferences.test_mode)
	{
		QString id("Preview");

		encoderPreviewDialog->setOutput(output);
		encoderPreviewDialog->setNoConsoleButton(!showConsoleButton);
				
		job->setTask( task, id, name, jobOutput, standardInput, standardOutput, standardError, log, verbosity);
	
		previewJobManagerWidget->reset();
		previewJobManager->addJob( job );

		printLog( tr("Preview encoding started."), VERBOSITY_VERBOSE );
	}
	else
	{
		QDateTime stamp( QDateTime::currentDateTime() );
		QString id = stamp.toString("yyyyMMdd-hhmmss-zzz");

		job->setTask( task, id, name, jobOutput, standardInput, standardOutput, standardError, log, verbosity);

		jobManager->addJob( job );

		printLog( tr("Encoding queued in job manager."), VERBOSITY_VERBOSE );
	}

	return true;
}

bool EncoderWidget::runFinalizing(const QString &input, const QString &output, 
	double aspect_ratio, int total_time, bool preview, QLayout *taskLayout)
{
	// Correct aspect ratio with FFmpeg

	QString cmd;
	QStringList args;
	
	int engine = Video::Encoder::FFmpeg;

	QString intermediate(input);
	
	if ( input == output )
	{
		QFile file(input);
		QFileInfo fi(input);	
		intermediate = fi.path() + "/" + fi.completeBaseName() + " (step 0)." + fi.suffix();

		if (!file.rename(intermediate))
		{
			printLog( tr("Failed to finalize encoding."), VERBOSITY_QUIET );

			InfoBoxDialog errorBox(this);
			errorBox.setText(tr("Failed to finalize encoding."));
			errorBox.setDetailedText(tr("Failed to rename intermediate file (%1).").arg(input));
			errorBox.exec();

			return false;
		}

		printLog( tr("Rename intermediate encoded file [%1].").arg(input), VERBOSITY_NORMAL );
	}
	else
	{
		intermediate = input;
	}

	encoder->cliCorrectAspectRatio(engine, &cmd, &args, intermediate, output, aspect_ratio);

	QString jobId("Finalizing");
	QString jobIdStr( tr("Current finalizing process") );
	QString jobOuput("");

	QString testStdIn, testStdOut, testStdErr; // test encoder command
	
	// We're quiet sure to succeed so create the finalization job
	QFileInfo fi2(encoder->getInputFilePath());
	QString name = tr("Finalizing %1").arg(fi2.completeBaseName());
	QString title = tr("Finalizing");

	Task *task = new Task;

	QList<QVariant> *data = new QList<QVariant>;
	*data << QVariant(engine);
	*data << QVariant(total_time);
	*data << QVariant(0);
	*data << QVariant(aspect_ratio);
	*data << QVariant(1);		// run_count
	*data << QVariant(intermediate);	// intermediate file to delete...
	*data << QVariant(output);	// ...only if output file exists
	*data << QVariant(preview);
	*data << qVariantFromValue(taskLayout);

	task->setCommandLine( data, cmd, args, encoder->getOutputFile().dir, name, title); 

	// Now we can safely create and process a new job
	Job *job = new Job;
	job->setTask( task, jobId, jobIdStr, jobOuput, standardInput, standardOutput, standardError, log, verbosity);

	if (preview)
	{
		previewJobManager->addJob( job );
	}
	else
	{
		jobManager->addJob( job );
	}

	printLog( tr("Finalizing started."), VERBOSITY_VERBOSE );

	return true;
}

bool EncoderWidget::runFinalizingAudio(const QString &input, const QString &output, 
	int total_time, bool preview, QLayout *taskLayout)
{
	// Extract MP3 stream from AVI intermediate file
	
	QString cmd;
	QStringList args;
	
	int engine = Video::Encoder::FFmpeg;

	encoder->cliExtractAudioFromAvi(engine, &cmd, &args, input, output);

	QString jobId("Finalizing");
	QString jobIdStr( tr("Current finalizing process") );
	QString jobOuput("");

	QString testStdIn, testStdOut, testStdErr; // test encoder command
	
	// We're quiet sure to succeed so create the finalization job
	QFileInfo fi2(encoder->getInputFilePath());
	QString name = tr("Finalizing %1").arg(fi2.completeBaseName());
	QString title = tr("Finalizing");

	Task *task = new Task;

	QList<QVariant> *data = new QList<QVariant>;
	*data << QVariant(engine);
	*data << QVariant(total_time);
	*data << QVariant(0);
	*data << QVariant(0);
	*data << QVariant(1);		// run_count
	*data << QVariant(input);	// intermediate file to delete...
	*data << QVariant(output);	// ...only if output file exists
	*data << QVariant(preview);
	*data << qVariantFromValue(taskLayout);

	task->setCommandLine( data, cmd, args, encoder->getOutputFile().dir, name, title); 

	// Now we can safely create and process a new job
	Job *job = new Job;
	job->setTask( task, jobId, jobIdStr, jobOuput, standardInput, standardOutput, standardError, log, verbosity);

	if (preview)
	{
		previewJobManager->addJob( job );
	}
	else
	{
		jobManager->addJob( job );
	}

	printLog( tr("Finalizing started."), VERBOSITY_VERBOSE );

	return true;
}

void EncoderWidget::printLog(const QString &message, const int verbosityLevel)
{
	if ( log == 0 )
		qDebug() << "EncoderWidget Log:" << message;
	else if ( verbosityLevel <= verbosity )
	{
		log->append( message );
		QCoreApplication::processEvents();  // we want to show message immediately
	}
}

void EncoderWidget::printIn(const QString &message, const int verbosityLevel)
{
	if ( standardInput == 0)
		qDebug() << "EncoderWidget stdIn:" << message;
	else if ( verbosityLevel <= verbosity )
	{
		standardInput->append( message );
		QCoreApplication::processEvents();  // we want to show message immediately
	}
}

void EncoderWidget::printOut(const QString &message, const int verbosityLevel)
{
	if ( standardOutput == 0)
		qDebug() << "EncoderWidget stdOut:" << message;
	else if ( verbosityLevel <= verbosity )
	{
		standardOutput->append( message );
		QCoreApplication::processEvents();  // we want to show message immediately
	}
}

void EncoderWidget::printErr(const QString &message, const int verbosityLevel)
{
	if ( standardError == 0)
		qDebug() << "EncoderWidget stdErr:" << message;
	else if ( verbosityLevel <= verbosity )
	{
		standardError->append( message );
		QCoreApplication::processEvents();  // we want to show message immediately
	}
}

void EncoderWidget::clearLog()
{
	if ( log != 0)
		log->clear();
}

void EncoderWidget::clearIn()
{
	if ( standardInput != 0)
		standardInput->clear();
}

void EncoderWidget::clearOut()
{
	if ( standardOutput != 0)
		standardOutput->clear();
}

void EncoderWidget::clearErr()
{
	if ( standardError != 0)
		standardError->clear();
}

void EncoderWidget::resizeEvent(QResizeEvent *ev)
{
	backFrame->resize( ev->size() );
}

void EncoderWidget::setInput(const QString &path, const int media)
{
	controllerButtonIsActivable = false;
	controllerButton->setEnabled(false);

	encoder->setInputPath(path, media);

	populateOptions( "", "", "", "" );
	emit optionsChanged();

	if ( !controllerButtonIsActivable) controllerButton->setEnabled(false);
	isEncodingStartable = !isEncoding;
}

void EncoderWidget::updateInputTracks(video_t v, bool nv, audio_t a, bool na, subtitle_t s, bool ns, bool ready)
{
	encoder->updateInputTracks(v, nv, a, na, s, ns);

	populateOptions( "", "", "", "" );
	emit optionsChanged();

	controllerButton->setEnabled( controllerButtonIsActivable && ready );
}

void EncoderWidget::updateIdleLabel(const QString &muxer, 
	const QString &profileName, const QString &profileDescription)
{
	QString str;
	encoder_settings_t::input_t *inputSettings = encoder->getInputSettings();
	
	str = tr("%1: %2")
			.arg(profileName).arg(profileDescription);

	settingsLabel->setText( str );
}

void EncoderWidget::setVerbosity(const int verbose)
{
	verbosity = verbose;

	encoder->setVerbosity(verbose);
	previewJobManager->setVerbosity(verbose);
}

void EncoderWidget::setSaveSettingsOnClose(const bool save)
{
	saveSettingsOnClose = save;

	encoder->setSaveSettingsOnClose( save );
}

void EncoderWidget::setOutputFile(const QString &folder, const QString &name)
{
	QString ext = encoder->setOutputFile(folder, name);

	targetFolderLine->setText(folder);
	targetNameLine->setText( QString( "%1%2" ).arg(name).arg(ext) );
}

void EncoderWidget::loadProfileSettings()
{
	QString muxer = muxerSelectCombo->itemData( muxerSelectCombo->currentIndex() ).toString();
	QString video = videoSelectCombo->itemData( videoSelectCombo->currentIndex() ).toString();
	QString audio = audioSelectCombo->itemData( audioSelectCombo->currentIndex() ).toString();

	if (controllerButtonIsActivable)
	{
		int engine = encoder->selectConvenientEngine();
		encoder_settings_t::input_t *input = encoder->getInputSettings();
		encoder_settings_t *interfaceSettings = encoder->getSettings(engine);
		interfaceSettings->input = *input;
		interfaceSettings->output.muxer = encoder->getOutputMuxer();
		QString standard = encoder->getOutputStandard();

		Configurator *config = new Configurator(encoder);

		int profileLevel = config->readProfileFromSettings(muxer); // last profile

		if (profileLevel == Video::Profile::None) profileLevel = Video::Profile::Medium;  // default profile

		if (input->source.media == Video::Media::DVD && muxer == "ac3") profileLevel = Video::Profile::Copy; 
		
		// Load profile data in encoder settings (so is ready to encode)
		interfaceSettings->output.profile = config->getProfile(muxer, video, audio, profileLevel);

		// Disable configure button if AC3 output for DVD input
		configureButton->setDisabled(input->source.media == Video::Media::DVD && muxer == "ac3");
	
		// Update output format description
		updateIdleLabel( muxer, 
			config->getProfileName(profileLevel, standard, muxer, video, audio), 
			encoder->profileDescription(&interfaceSettings->output.profile) );
	}
}

void EncoderWidget::populateOptions(QString muxer, QString video, QString audio, QString subtitle)
{
	if (muxer.isEmpty())
	{
		if (previousSessionMuxer.isEmpty())
		{
			muxer = encoder->getOutputMuxer();
		}
		else
		{
			muxer = previousSessionMuxer;
			previousSessionMuxer.clear();
		}
	}

	QStringList codeList = configurator->getApplicableMuxers();  // available and applicable to current input

	QMap<QString, QString> itemList;
	for (int i = 0; i < codeList.count(); i++) 
	{
		itemList.insert(encoder->muxerLongName(codeList[i]), codeList[i]);
	}

	QStringList names = itemList.keys();
	names.sort();

	muxerSelectCombo->clear();
	for (int i = 0; i < names.count(); i++) 
	{
		muxerSelectCombo->addItem( names[i], itemList.value(names[i]) );
	}

	// Find and select current muxer or its synonym
	QStringListIterator mux(encoder->getMuxerSynonyms(muxer));
	int index(-1);
	while (mux.hasNext())
	{
		index = muxerSelectCombo->findData( mux.next() );
		if ( index > -1 ) break;
	}
	if ( index == -1 ) index = 0;

	muxer = muxerSelectCombo->itemData( index ).toString();
	encoder->setOutputMuxer(muxer);
	muxerSelectCombo->setCurrentIndex( index );

	// Populate options related to muxer
	populateVideoOptions(muxer, video);
	populateAudioOptions(muxer, audio);
	populateSubtitleOptions(muxer, subtitle);

	int item;
	item = videoSelectCombo->currentIndex();
	video = videoSelectCombo->itemData( item ).toString();
	item = audioSelectCombo->currentIndex();
	audio = audioSelectCombo->itemData( item ).toString();
	item = subtitleSelectCombo->currentIndex();
	subtitle = subtitleSelectCombo->itemData( item ).toString();

	controllerButtonIsActivable = (video != "novideo" || audio != "noaudio");
}

void EncoderWidget::populateVideoOptions(QString muxer, QString video)
{
	if (video.isEmpty()) video = encoder->getOutputVideoCodec();

	QStringList codeList = configurator->getApplicableVideos(muxer);  // available and applicable to current input

	QMap<QString, QString> itemList;
	for (int i = 0; i < codeList.count(); i++) 
	{
		itemList.insert(encoder->videoName(codeList[i]), codeList[i]);
	}

	QStringList names = itemList.keys();
	names.sort();

	videoSelectCombo->clear();
	for (int i = 0; i < names.count(); i++) 
	{
		videoSelectCombo->addItem( names[i], itemList.value(names[i]) );
	}

	QStringListIterator vid(encoder->getVideoSynonyms(video));
	int index(-1);
	while (vid.hasNext())
	{
		index = videoSelectCombo->findData( vid.next() );
		if ( index > -1 ) break;
	}
	if (index == -1) index = 0;

	video = videoSelectCombo->itemData( index ).toString();
	encoder->setOutputVideoCodec(video);
	videoSelectCombo->setCurrentIndex( index );
}

void EncoderWidget::populateAudioOptions(QString muxer, QString audio)
{
	if (audio.isEmpty()) audio = encoder->getOutputAudioCodec();

	QStringList codeList = configurator->getApplicableAudios(muxer);  // available and applicable to current input

	QMap<QString, QString> itemList;
	for (int i = 0; i < codeList.count(); i++) 
	{
		itemList.insert(encoder->audioName(codeList[i]), codeList[i]);
	}

	QStringList names = itemList.keys();
	names.sort();
	
	audioSelectCombo->clear();
	for (int i = 0; i < names.count(); i++) 
	{
		audioSelectCombo->addItem( names[i], itemList.value(names[i]) );
	}

	int index = audioSelectCombo->findData( audio );
	if ( index == -1 )
	{
		QString preferred = encoder->getPreferredAudio(muxer, "");
		index = audioSelectCombo->findData( preferred );
		
		if ( index > -1 ) audio = preferred;
	}

	if (index == -1) index = 0;

	audio = audioSelectCombo->itemData( index ).toString();
	encoder->setOutputAudioCodec(audio);
	audioSelectCombo->setCurrentIndex( index );
}

void EncoderWidget::populateSubtitleOptions(QString muxer, QString subtitle)
{
	if (subtitle.isEmpty())	subtitle = encoder->getOutputSubtitleCodec();

	QStringList codeList = configurator->getApplicableSubtitles(muxer);  // available and applicable to current input

	QMap<QString, QString> itemList;
	for (int i = 0; i < codeList.count(); i++) 
	{
		itemList.insert(encoder->subtitleName(codeList[i]), codeList[i]);
	}

	QStringList names = itemList.keys();
	names.sort();

	subtitleSelectCombo->clear();
	for (int i = 0; i < names.count(); i++) 
	{
		subtitleSelectCombo->addItem( names[i], itemList.value(names[i]) );
	}

	QStringListIterator sub(encoder->getSubtitleSynonyms(subtitle));
	int index(-1);
	while (sub.hasNext())
	{
		index = subtitleSelectCombo->findData( sub.next() );
		if ( index > -1 ) break;
	}
	if (index == -1) index = 0;

	subtitle = subtitleSelectCombo->itemData( index ).toString();
	encoder->setOutputSubtitleCodec(subtitle);
	subtitleSelectCombo->setCurrentIndex( index );
}

QString EncoderWidget::audio()
{
	QString audio = audioSelectCombo->itemData( audioSelectCombo->currentIndex() ).toString();
	
	encoder->setOutputAudioCodec( audio );
	
	return audio; 
}

QString EncoderWidget::codec()
{
	QString codec = videoSelectCombo->itemData( videoSelectCombo->currentIndex() ).toString();
	
	encoder->setOutputVideoCodec( codec );
	
	return codec; 
}

void EncoderWidget::selectVideo()
{	
	int item = videoSelectCombo->currentIndex();
	QString codec = videoSelectCombo->itemData( item ).toString();
	QString muxer, audio, subtitle;

	encoder->setOutputVideoCodec( codec );

	muxer = encoder->getOutputMuxer();
	encoder->recommendedMuxerVideoCodecs( muxer, codec, &audio, &subtitle );

	populateOptions( muxer, codec, audio, subtitle );
	emit optionsChanged();
}

void EncoderWidget::selectAudio()
{
	int item = audioSelectCombo->currentIndex();
	QString audio = audioSelectCombo->itemData( item ).toString();

	encoder->setOutputAudioCodec( audio );
	emit optionsChanged();
}

void EncoderWidget::selectSubtitle()
{
	int item = subtitleSelectCombo->currentIndex();
	QString sub = subtitleSelectCombo->itemData( item ).toString();

	encoder->setOutputSubtitleCodec( sub );
	emit optionsChanged();
}

void EncoderWidget::selectMuxer()
{
	QString muxer, video, audio, subtitle;

	int item = muxerSelectCombo->currentIndex();

	muxer =  muxerSelectCombo->itemData( item ).toString();
	
	encoder->setOutputMuxer( muxer );
	encoder->recommendedMuxerCodecs( muxer, &video, &audio, &subtitle );

	encoder_target_t outputFile = encoder->getOutputFile();
	outputFile.ext = Encoder::recommendsFileExtension( muxer, video == "novideo" );

	targetNameLine->setText( QString( "%1%2" )
								.arg(outputFile.name)
								.arg(outputFile.ext) );

	populateOptions( muxer, video, audio, subtitle );
	emit optionsChanged();
}

void EncoderWidget::adjustAudioCorrection(int gain)
{
	preferences.volume_correction = gain;

	encoder->setOutputAudioVolume( gain );

	WidgetBridge::getIt()->sendData( MSG_CHANGEVOLUME, gain );
}

void EncoderWidget::codecBarFlipFlop()
{
	codecSelectionWidget->setVisible ( !codecSelectionWidget->isVisible() );

	writeShowCodecsSettings();
}

void EncoderWidget::switchTestMode(int state)
{
	if ( state == Qt::Checked )
	{
		preferences.test_mode = true;

		testCheckBox->setToolTip(tr("Uncheck this box to encode the whole movie"));
		controllerButton->setText(tr("Start encoding preview"));
		controllerButton->setToolTip(tr("Start encoding a short extract"));
	}
	else
	{
		preferences.test_mode = false;

		testCheckBox->setStatusTip(tr("Check to encode an input excerpt and preview encoding output"));
		controllerButton->setText(tr("Start encoding"));
		controllerButton->setToolTip(tr("Start encoding process as a new job"));
	}

	WidgetBridge::getIt()->sendData( MSG_CHANGETESTMODE, preferences.test_mode );
}

void EncoderWidget::readJobProcessStdOut(Process *process)
{
	//qDebug() << "EncoderWidget::readJobProcessStdOut";

	QString strOutput;
	ConsoleDialog *console = (ConsoleDialog*)process->getTask()->getConsole();

	// Wait the console to be created!
	if (!console) return;

	QList<QVariant> *data = (QList<QVariant> *) process->getTask()->getData();
	int engine = data->value(0).toInt();

	// Read stdOut
	strOutput = process->readAllStandardOutput();
	if ( strOutput.isEmpty() ) return;

	// in InterfaceMEncoder:
	//		extract last (\d+%) (\d+\.\d+)fps Trem: (\d+)min 
	//		send to a progressBarwith textField: FPS and Time Remaining
	// in InterfaceX264
	//		exract last \[(\d+\.\d+)%\] (\d+\.\d+)fps eta (\d+:\d+:\d+) estimated time remaining
	// in InterfaceMkvMerge
	//		extract Progress: (\d+)%

	if ( engine == Video::Encoder::FFmpeg )
	{
		// Progress is sent to stdErr by FFmpeg, so stdOut to console error display
		console->displayOutput( &strOutput );
	}
	else
	{
		int frame(-1), percent(-1), remain(-1), size(-1);
		double time(-1.0), speed(-1.0);
		QString warning;
		
		if ( Encoder::parseEngineProgress( engine, &strOutput, &warning, 
			&frame, &time, &percent, &remain, &speed, &size ) )
		{
			console->displayProgress( &frame, &time, &percent, &size );

			if ( !warning.isEmpty() ) console->displayOutput( &warning );
		}
		else
		{
			qDebug() << "EncoderWidget::readJobProcessStdOut encoder parse output failed";
		}
	}
}

void EncoderWidget::readJobProcessStdErr(Process *process)
{
	//qDebug() << "EncoderWidget::readJobProcessStdErr";

	QString strError;
	ConsoleDialog *console = (ConsoleDialog*)process->getTask()->getConsole();

	// Wait the console to be created!
	if (!console) return;

	QList<QVariant> *data = (QList<QVariant> *) process->getTask()->getData();
	int engine = data->value(0).toInt();

	// Read stdErr
	strError = process->readAllStandardError();
	if ( strError.isEmpty() ) return;

	// in InterfaceFFmpeg:
	//		extract last frame=(\d+) fps=(\d+) time=(\d+\.\d+) time/totalTime = %

	if ( engine == Video::Encoder::FFmpeg )
	{
		int frame(-1), percent(-1), remain(-1), size(-1);
		double time(-1.0), speed(-1.0);
		QString warning;

		if ( Encoder::parseEngineProgress( engine, &strError, &warning, 
			&frame, &time, &percent, &remain, &speed, &size ) )
		{
			console->displayProgress( &frame, &time, &percent, &size );

			if ( !warning.isEmpty() ) console->displayError( &warning );
		}
		else
		{
			qDebug() << "EncoderWidget::readJobProcessStdErr encoder parse error failed";
		}
	}
	else
	{
		console->displayError( &strError );
	}
}

void EncoderWidget::finishJobProcess(Process *process, int exitCode, QProcess::ExitStatus exitStatus)
{
	//qDebug() << "EncoderWidget::finishJobProcess exit code:" << exitCode;

	QString strOutput, strError;
	ConsoleDialog *console = (ConsoleDialog*)process->getTask()->getConsole();

	// Wait the console to be created!
	if (!console) return;

	QList<QVariant> *data = (QList<QVariant> *) process->getTask()->getData();
	int engine = data->value(0).toInt();

	// Read stdOut
	strOutput = process->readAllStandardOutput();

	// in InterfaceMEncoder:
	//		extract last (\d+%) (\d+\.\d+)fps Trem: (\d+)min 
	//		send to a progressBarwith textField: FPS and Time Remaining
	// in InterfaceX264
	//		exract last \[(\d+\.\d+)%\] (\d+\.\d+)fps eta (\d+:\d+:\d+) estimated time remaining
	// in InterfaceMkvMerge
	//		extract Progress: (\d+)%

	if ( engine == Video::Encoder::FFmpeg )
	{
		// Progress is sent to stdErr by FFmpeg, so stdOut to console error display
		console->displayOutput( &strOutput );
	}
	else if ( !strOutput.isEmpty() )
	{
		int frame(-1), percent(-1), remain(-1), size(-1);
		double time(-1.0), speed(-1.0);
		QString warning;
		
		if ( Encoder::parseEngineProgress( engine, &strOutput, &warning,
			&frame, &time, &percent, &remain, &speed, &size ) )
		{
			console->displayProgress( &frame, &time, &percent, &size );

			if ( !warning.isEmpty() ) console->displayOutput( &warning );
		}
		else
		{
			qDebug() << "EncoderWidget::finishJobProcess encoder parse output failed";
		}
	}

	// Read stdErr
	strError = process->readAllStandardError();

	// in InterfaceFFmpeg:
	//		extract last frame=(\d+) fps=(\d+) time=(\d+\.\d+) time/totalTime = %

	if ( engine != Video::Encoder::FFmpeg )
	{
		console->displayError( &strError );
	}
	else if ( !strError.isEmpty() )
	{
		int frame(-1), percent(-1), remain(-1), size(-1);
		double time(-1.0), speed(-1.0);
		QString warning;

		if ( Encoder::parseEngineProgress( engine, &strError, &warning,
			&frame, &time, &percent, &remain, &speed, &size ) )
		{
			console->displayProgress( &frame, &time, &percent, &size );

			if ( !warning.isEmpty() ) console->displayError( &warning );
		}
		else
		{
			qDebug() << "EncoderWidget::finishJobProcess encoder parse error failed";
		}
	}

	previewJobManager->setProcessDone( process );

	emit encodingComplete(process);
}


void EncoderWidget::finalizeEncoding(Process *process)
{
	bool needFinalization(false);

	QList<QVariant> *data = (QList<QVariant> *) process->getTask()->getData();
	int engine = data->value(0).toInt();
	double aspect_ratio = data->value(3).toDouble();
	int run_count = data->value(4).toInt();
	QString output = data->value(5).toString();
	QString muxer = data->value(6).toString();
	bool preview = data->value(7).toBool();
	QLayout *taskLayout = data->value(8).value<QLayout *>();

	if ( !isAbortedByUser && run_count == 0 && engine == Video::Encoder::MEncoder
		&& !process->isAbortedByUser() )
	{
		// Read MediaInfo data to proceed a few check-up
		interfacemediainfo_data_t mediaInfoData;
		double display_aspect(0.0);
		QString intermediate;

		if ( muxer == "ac3" || muxer == "au" || muxer == "mp3" || muxer == "wav" || muxer == "m4a" )
		{
			QFileInfo fi(output);
			intermediate = fi.path() + "/" + fi.completeBaseName() + " (step 0).avi";
		}
		else
		{
			intermediate = output;
		}
		
		// Use a PlayerDialog object to get MediaInfo data
		PlayerDialog playerDlg( "No display", &playerPreferences, 0);		
		playerDlg.setInfoMode(true);
		playerDlg.setCurrentFilePath(intermediate);
		mediaInfoData = playerDlg.readMediaInfoData();

		//qDebug() << mediaInfoData.general;
		//qDebug() << mediaInfoData.video;
		//qDebug() << mediaInfoData.audio[0];

		// Check ouput if we've to extract audio stream from noframe avi from MEncoder
		if ( mediaInfoData.general.contains("Format")
			&& mediaInfoData.general.value("Format") == "AVI"
			&& mediaInfoData.video.contains("Format")
			&& mediaInfoData.video.value("Format") == "FrNo"
			&& mediaInfoData.audio.count() == 1 )
			
		{
			int totalTime(0);
			
			if ( mediaInfoData.audio[0].contains("Duration") )
			{
				QString str = mediaInfoData.audio[0].value("Duration"); // 1h 16 mn 40s 542ms

				str.replace( QRegExp("(\\d+ms)"), "" );

				QRegExp rx("(\\d+)");
				int pos(0), ncap(0);
				QList<int> caps;
				while ((pos = rx.indexIn(str, pos)) != -1)
				{
					caps << rx.cap(1).toInt();
					pos += rx.matchedLength();
					++ncap;
				}
				while (ncap < 3)
				{
					caps.prepend(0);
					++ncap;
				}
				totalTime = 3600 * caps[0] + 60 * caps[1] + caps[2];
			}

			runFinalizingAudio(intermediate, output, totalTime, preview, taskLayout);
			needFinalization = true;
		}
		// Check ouput aspect ratio and start FFmpeg remux if necessary
		else if ( mediaInfoData.video.contains("Display aspect ratio")
				&& muxer == "avi" )
		{
			QString str = mediaInfoData.video.value("Display aspect ratio");
		
			if ( str.toDouble() >= 1.0 )
			{
				display_aspect = str.toDouble();
			}
			else if ( str.contains(":") ) 
			{
				QRegExp rx("(\\d+):(\\d+)");
				rx.setCaseSensitivity(Qt::CaseInsensitive);
				rx.setMinimal(false);
				rx.indexIn(str);
				if (rx.numCaptures() == 2 && rx.cap(1).toInt() > 0 && rx.cap(2).toInt() > 0)
				{
					QStringList operandes = str.split(":");		
					display_aspect = operandes[0].toDouble() / operandes[1].toDouble();
				}
			}

			if ( qAbs(display_aspect - aspect_ratio) > 0.02 ) 
			{
				int totalTime(0);

				if ( mediaInfoData.video.contains("Duration") )
				{
					QString str = mediaInfoData.video.value("Duration"); // 1h 26mn 32s 542ms

					str.replace( QRegExp("(\\d+ms)"), "" );
		
					QRegExp rx("(\\d+)");
					int pos(0), ncap(0);
					QList<int> caps;
					while ((pos = rx.indexIn(str, pos)) != -1)
					{
						caps << rx.cap(1).toInt();
						pos += rx.matchedLength();
						++ncap;
					}
					while (ncap < 3)
					{
						caps.prepend(0);
						++ncap;
					}

					totalTime = 3600 * caps[0] + 60 * caps[1] + caps[2];
				}

				runFinalizing(intermediate, output, aspect_ratio, totalTime, preview, taskLayout);
				needFinalization = true;
			}
		}
	}
	else if ( !isAbortedByUser && run_count == 1 && engine == Video::Encoder::FFmpeg
			&& !process->isAbortedByUser() )
	{
		// Delete intermediate file
		QString input = data->value(5).toString();
		QString output = data->value(6).toString();

		if (QFile::exists(output)) QFile::remove(input);

		printLog( tr("Finalizing done."), VERBOSITY_VERBOSE );
	}

	if ( !needFinalization )
	{
		// No more processing needed	
		if (isAbortedByUser)
		{
			encoderPreviewDialog->hide();
			encoderPreviewDialog->setEncoding();
			previewJobManagerWidget->reset();
		}
		else
		{
			encoderPreviewDialog->setEncodingDone();
		}

		isEncoding = false;
		isEncodingStartable = true;
		enableControls(true);

		if ( run_count == 0 )
		{
			printLog( tr("Encoding completed."), VERBOSITY_QUIET );
		}
		else
		{
			printLog( tr("Finalizing completed."), VERBOSITY_QUIET );
		}
	}
}

void EncoderWidget::startProcessConsole(Task *task)
{
	previewJobManagerWidget->addTaskConsole(task);
}

void EncoderWidget::configure()
{
	if ( !controllerButtonIsActivable ) return;

	int engine = encoder->selectConvenientEngine();
	encoder_settings_t::input_t *input = encoder->getInputSettings();
	encoder_settings_t *interfaceSettings = encoder->getSettings(engine);
	interfaceSettings->input = *input;
	interfaceSettings->output.muxer = encoder->getOutputMuxer();

	EncoderConfigureDialog dlg(configurator, this);

	if ( configurator->guessUserInterface() )
	{
		dlg.setUserInterface( configurator->getProfileInterface() );

		dlg.exec();

		if (dlg.result() == QDialog::Accepted)
		{
			// Save inputs
			configurator->save();
		}

		loadProfileSettings();
	}
	else
	{
		InfoBoxDialog errorBox(this);
		errorBox.setText(tr("This encoding configuration is not supported."));
		errorBox.setInformativeText(tr(	"Change audio/video codec selection to find a supported "
										"encoder configuration."));
		errorBox.exec();
	}
}

void EncoderWidget::writeShowCodecsSettings()
{
	QSettings settings;

	// Common options
	settings.beginGroup("Options");

	// Encoder options
	settings.beginGroup("Encoder");
    settings.setValue("show codecs", codecSelectionWidget->isVisible());
	settings.endGroup();

	settings.endGroup(); // Common options

	settings.sync();
}

void EncoderWidget::readSettings()
{
	QSettings settings;

	// Common options
	settings.beginGroup("Options");

	// Encoder options
	settings.beginGroup("Encoder");
	codecSelectionWidget->setVisible( settings.value("show codecs", false).toBool() );
    previousSessionMuxer = settings.value("last format", "").toString();
	settings.endGroup();

	settings.endGroup(); // Common options
}

void EncoderWidget::setNoConsoleButton(const bool no)
{
	showConsoleButton = !no;

	previewJobManagerWidget->setNoConsoleButton(!showConsoleButton);
}

bool EncoderWidget::caseInsensitiveLessThan(const QString &s1, const QString &s2)
{
     return s1.toLower() < s2.toLower();
}
