/*****************************************************************************
 *
 *  mainwindow.cpp -- Top window which handles menus and user flow control.
 *
 *  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 "macros.h"
#include "mainwindow.h"

#include "../video/video_constants.h"
#include "../video/video_functions.h"
#include "../video/video_struct.h"

#include "../encoder/encoder.h"
#include "../encoder/mediasplitter.h"
#include "../job/job.h"

#include "encodercapabilitydialog.h"
#include "encoderstatusdialog.h"
#include "infoboxdialog.h"
#include "mediainfodatadialog.h"
#include "playercapabilitydialog.h"
#include "playerstatusdialog.h"
#include "preferencesdialog.h"
#include "softwareupdatedialog.h"
#include "splitmoviedialog.h"
#include "targetdialog.h"
#include "utilitystatusdialog.h"
#include "widgetbridge.h"

// Very ugly hack to fix in 4.1
#ifdef SMARTEDITION
#include "../../src-smart/main/smartpaths.h"
#else
#include "../main/paths.h"
#endif

#ifdef WIN32
#include "../../tools/buildnumber/buildnumberwin32.h"
#elif defined(Q_OS_MAC)
#include "../../tools/buildnumber/buildnumbermacx.h"
#else
#include "../../tools/buildnumber/buildnumberlinux.h"
#endif

// Platform string
#ifdef WIN32
#define PLATFORM_STR		"Windows"
#elif defined(Q_OS_MAC)
#define PLATFORM_STR		"Mac OS X"
#else
#define PLATFORM_STR		"Linux"
#endif

#include <QtGui>
#include <QAction>
#include <QApplication>
#include <QRegExp>
#include <QDateTime>
#include <QDebug>
#include <QFileInfo>
#include <QFont>
#include <QRegExp>
#include <QString>
#include <QDesktopServices>
#include <iostream>  // for console developer messages like: cout << "Set view:" << index << endl;

extern Console *console;

MainWindow::MainWindow()
	: playerWidget(0), encoderWidget(0), jobManager(0), jobManagerDialog(0)

{
	m_console = console;

	setAttribute(Qt::WA_DeleteOnClose);

#ifdef QT_NO_DEBUG
	appName = tr("%1").arg(APPTITLE);
#else
	appName = tr("%1 (Debug Build)").arg(APPTITLE);		
#endif
	
	setObjectName("MainWindow");
	setWindowTitle(appName);
	setSizePolicy( QSizePolicy::Expanding , QSizePolicy::Maximum );

    // Default
	showConsoleButton = false;
	verbosity = VERBOSITY_QUIET;
	saveSettingsOnClose = true;

	setPathsForExternals();

	// Read some settings before widget ceation
	readSettingsForWindow();
	readSettingsForOptions(); 
	readSettingsForFiles();
	readSettingsForPreferences();

	// Create widgets
	createActions();
	createMenus();
	createStatusBar();
	createControls();  // use MainWindow size and position
	changeZoom();

	// Start main GUI console
	m_console->start(inputChannel, outputChannel, errorChannel, logChannel);
	m_console->setVerbose(verbosity);

	// Recent path actions require actions and menus 
	updateRecentPathActions();

	connect(this, SIGNAL(movieLoaded(int)), this, SLOT(onMovieLoaded(int)));

	// Widget brige connections
	connect(WidgetBridge::getIt(), SIGNAL(sendMessage(QString)), this, SLOT(processBridgeMessage(QString)));
	connect(WidgetBridge::getIt(), SIGNAL(sendMessage(QString, bool)), this, SLOT(processBridgeMessage(QString, bool)));
	connect(WidgetBridge::getIt(), SIGNAL(sendMessage(QString, int)), this, SLOT(processBridgeMessage(QString, int)));
	connect(WidgetBridge::getIt(), SIGNAL(sendMessage(QString, void*)), this, SLOT(processBridgeMessage(QString, void*)));

	// Connect to player widget to disable video opening when playing
	connect(playerWidget, SIGNAL(stateChanged(bool)), this, SLOT(enableVideoOpenActions(bool)));

	// Connect to player widget to propagate input selection to encoder
	connect(playerWidget, SIGNAL(selectionChanged()), this, SLOT(updateEncoderInput()));
	connect(playerWidget, SIGNAL(selectionChanged()), this, SLOT(updateJobMenu()));

#ifdef Q_OS_MAC
	statusBar()->setSizeGripEnabled(true); // but still resizable on Windows
#else
	statusBar()->setSizeGripEnabled(false); // but still resizable on Windows
#endif

	applyVerbosity();
	updateVerbosity();
}

void MainWindow::initialize()
{
	encoderWidget->initialize();
	encoderWidget->setOutputFile( currentTargetDir, currentTargetName );

	showFFmpegCapabilityAct->setEnabled( encoderWidget->isReady(Video::Encoder::FFmpeg) );
	showMEncoderCapabilityAct->setEnabled( encoderWidget->isReady(Video::Encoder::MEncoder) );

	showMPlayerCapabilityAct->setEnabled( playerWidget->isReady(true) );
	showMediaInfoAct->setEnabled( false );
	showFFprobeAct->setEnabled( false );

	readSettingsForMonitor();   // may display the monitor

	// Update menu
	if (advancedMonitor->isHidden())
	{
		toggleAdvancedMonitorAct->setChecked(false);
		resetAdvancedMonitorAct->setEnabled(false);
		clearAdvancedMonitorAct->setEnabled(false);
	}
	else
	{
		toggleAdvancedMonitorAct->setChecked(true);
		resetAdvancedMonitorAct->setEnabled(true);
		clearAdvancedMonitorAct->setEnabled(true);
	}

	checkApplicationReady();
}

bool MainWindow::isApplicationReady()
{
	bool ffmpegReady, mencoderReady;
	bool mplayerReady, mediainfoReady, ffprobeReady;

	encoderWidget->getStatus(&ffmpegReady, &mencoderReady);
	playerWidget->getStatus(&mplayerReady, &mediainfoReady, &ffprobeReady);

	return ffmpegReady && mencoderReady
			&& mplayerReady && mediainfoReady && ffprobeReady;
}

void MainWindow::checkApplicationReady()
{
	bool ffmpegReady, mencoderReady;
	bool mplayerReady, mediainfoReady, ffprobeReady;

	encoderWidget->getStatus(&ffmpegReady, &mencoderReady);
	playerWidget->getStatus(&mplayerReady, &mediainfoReady, &ffprobeReady);

	bool applicationReady = ffmpegReady && mencoderReady
							&& mplayerReady && mediainfoReady &&ffprobeReady;

	QString ffmpegStr( Video::strEncoder(Video::Encoder::FFmpeg) );
	QString mencoderStr( Video::strEncoder(Video::Encoder::MEncoder) );
	QString mplayerStr( Video::strPlayer(Video::Player::MPlayer) );
	QString mediainfoStr( "MediaInfo" );
	QString ffprobeStr( "FFprobe" );
											
	// All engines and utilities must be ready
	if (!applicationReady)
	{
		QString ffmpegMsg( ffmpegStr );
		QString mencoderMsg( mencoderStr );
		QString mplayerMsg( mplayerStr );
		QString mediainfoMsg( mediainfoStr );
		QString ffprobeMsg( ffprobeStr );

		ffmpegMsg = ffmpegReady ? ffmpegMsg.prepend(" - ").append(" is ready<br>"):
			ffmpegMsg.prepend(" - <b>").append("</b> is NOT ready<br>");

		mencoderMsg = mencoderReady ? mencoderMsg.prepend(" - ").append(" is ready<br>"):
			mencoderMsg.prepend(" - <b>").append("</b> is NOT ready<br>");

		mplayerMsg = mplayerReady ? mplayerMsg.prepend(" - ").append(" is ready<br>"):
			mplayerMsg.prepend(" - <b>").append("</b> is NOT ready<br>");

		mediainfoMsg = mediainfoReady ? mediainfoMsg.prepend(" - ").append(" is ready<br>"):
			mediainfoMsg.prepend(" - <b>").append("</b> is NOT ready<br>");

		ffprobeMsg = ffprobeReady ? ffprobeMsg.prepend(" - ").append(" is ready<br>"):
			ffprobeMsg.prepend(" - <b>").append("</b> is NOT ready<br>");

		InfoBoxDialog alertBox(this);
		alertBox.setTextFormat(Qt::RichText);
		alertBox.setText(tr("<u>Player and encoder are NOT ready.</u>"));
		alertBox.setInformativeText(	tr(	"You must check encoder engine paths and status.<br><br>" 
											"Eventually you will have to re-install %1<br><br>"
											"%2%3%4%5%6" )
										.arg(APPTITLE)
										.arg(ffmpegMsg).arg(ffprobeMsg)
										.arg(mencoderMsg).arg(mplayerMsg).arg(mediainfoMsg) );
		alertBox.exec();
	}

	if (ffmpegReady)
	{
		printLog( tr("%1 is ready [version: %2]").arg(ffmpegStr)
			.arg(encoderWidget->engineVersion(Video::Encoder::FFmpeg)), VERBOSITY_NORMAL );
	}
	else
	{
		printLog( tr("%1 is NOT ready [path: %2]").arg(ffmpegStr)
			.arg(encoderWidget->enginePath(Video::Encoder::FFmpeg)), VERBOSITY_NORMAL );
	}

	if (mencoderReady)
	{
		printLog( tr("%1 is ready [version: %2]").arg(mencoderStr)
			.arg(encoderWidget->engineVersion(Video::Encoder::MEncoder)), VERBOSITY_NORMAL );
	}
	else
	{
		printLog( tr("%1 is NOT ready [path: %2]").arg(ffmpegStr)
			.arg(encoderWidget->enginePath(Video::Encoder::MEncoder)), VERBOSITY_NORMAL );
	}

	if (mplayerReady)
	{
		printLog( tr("%1 is ready [version: %2]").arg(mplayerStr)
			.arg(playerWidget->engineVersion(Video::Player::MPlayer)), VERBOSITY_NORMAL );
	}
	else
	{
		printLog( tr("%1 is NOT ready [path: %2]").arg(mplayerStr)
			.arg(playerWidget->enginePath(Video::Player::MPlayer)), VERBOSITY_NORMAL );
	}

	if (mediainfoReady)
	{
		printLog( tr("%1 is ready [version: %2]").arg(mediainfoStr)
			.arg(playerWidget->engineVersion(Video::Player::MediaInfo)), VERBOSITY_NORMAL );
	}
	else
	{
		printLog( tr("%1 is NOT ready [path: %2]").arg(mediainfoStr)
			.arg(playerWidget->enginePath(Video::Player::MediaInfo)), VERBOSITY_NORMAL );
	}

	if (ffprobeReady)
	{
		printLog( tr("%1 is ready [version: %2]").arg(ffprobeStr)
			.arg(playerWidget->engineVersion(Video::Player::FFprobe)), VERBOSITY_NORMAL );
	}
	else
	{
		printLog( tr("%1 is NOT ready [path: %2]").arg(ffprobeStr)
			.arg(playerWidget->enginePath(Video::Player::FFprobe)), VERBOSITY_NORMAL );
	}

	if (applicationReady)
	{
		printLog( tr("Application is ready."), VERBOSITY_QUIET );
		printLog( QString().fill('-', 76), VERBOSITY_QUIET );
	}
	else
	{
		printLog( tr("Application is NOT ready [check your installation]."), VERBOSITY_QUIET );
		printLog( QString().fill('-', 76), VERBOSITY_QUIET );
	}

	if ( !isApplicationReady() )
	{
		emit movieLoaded( -1 );

		playerWidget->enablePlaybackControls(false);
		playerWidget->enableTrackControls(false);
	}
}

void MainWindow::closeEvent(QCloseEvent *ev)
{
	if ( !jobManagerDialog->isIdle() )
	{
		InfoBoxDialog infoBox(this);
		infoBox.setText(tr(	"Abort all running jobs, please confirm ?"));
		infoBox.setInformativeText(tr("Encoding jobs still running, closing the application will abort them."));

		infoBox.addButton(tr("Abort all and close"), QMessageBox::AcceptRole);
		infoBox.addButton(tr("Don't close"), QMessageBox::RejectRole);

		if ( infoBox.exec() != QMessageBox::AcceptRole )
		{
			ev->ignore();

			if (encoderWidget->isIdle())
			{
				hide();
				jobManagerDialog->show();
			}
			return;
		}
	}

	encoderWidget->setSaveSettingsOnClose( saveSettingsOnClose );
	if (saveSettingsOnClose) writeSettings();
	advancedMonitor->close();

	jobManagerDialog->setFinished();
	jobManagerDialog->close();

	ev->accept();
}

void MainWindow::processBridgeMessage(const QString &message, const bool data)
{
	if ( message == MSG_CHANGETESTMODE )
	{
		preferences.encoderPreferences.test_mode = data;
		preferences.playerPreferences.test_mode = data;
	}
}

void MainWindow::processBridgeMessage(const QString &message, const int data)
{
	if ( message == MSG_CHANGEVOLUME )
	{
		preferences.encoderPreferences.volume_correction = data;
		preferences.playerPreferences.volume_correction = data;
	}
}

void MainWindow::processBridgeMessage(const QString &message, void *data)
{
	if ( message == MSG_CHANGEENCODERPREFERENCES )
	{
		EncoderWidget::Preferences *prefs = (EncoderWidget::Preferences*) data;
		preferences.encoderPreferences = *prefs;
	}
}

void MainWindow::createActions()
{
	openDvdAct = new QAction( tr("Open DVD"), this);
	openDvdAct->setStatusTip(tr("Open DVD root or VIDEO_TS folder or IFO file"));
	connect(openDvdAct, SIGNAL(triggered()), this, SLOT(openDvd()));

	openFileAct = new QAction(tr("Open media file"), this);
	openFileAct->setStatusTip(tr("Open movie or audio file (AVI/MP4/WMV/MOV/FLV/MP3/WAV etc.)"));
	connect(openFileAct, SIGNAL(triggered()), this, SLOT(openFile()));

	clearRecentAct = new QAction(tr("Clear recent file list"), this);
	connect(clearRecentAct, SIGNAL(triggered()), this, SLOT(clearRecent()));

	setTargetAct = new QAction(tr("Set target path"), this);
	setTargetAct->setStatusTip(tr("Set target file location"));
	connect(setTargetAct, SIGNAL(triggered()), this, SLOT(setTarget()));

	openTargetOnDesktopAct = new QAction(tr("Open target folder on desktop"), this);
	openTargetOnDesktopAct->setStatusTip(tr("Open target folder on desktop to access directly ouput files"));
	connect(openTargetOnDesktopAct, SIGNAL(triggered()), this, SLOT(openTargetOnDesktop()));

	for (int i = 0; i < MaxRecentMovies; ++i)
	{
		recentMoviesActs[i] = new QAction(this);
		recentMoviesActs[i]->setVisible(false);
		connect(recentMoviesActs[i], SIGNAL(triggered()), this, SLOT(openRecentMovie()));
	}

	exitAct = new QAction(tr("Exit"), this);
	exitAct->setStatusTip(tr("Exit the application"));
	connect(exitAct, SIGNAL(triggered()), qApp, SLOT(closeAllWindows()));

	toggleJobManagerAct = new QAction(tr("Show job manager"), this);
	toggleJobManagerAct->setCheckable(true);
	toggleJobManagerAct->setStatusTip(tr("Toggle job manager display"));
	connect(toggleJobManagerAct, SIGNAL(triggered()), this, SLOT(toggleJobManager()));

	encodeVcdAct = new QAction(tr("Encode movie to VideoCD"), this);
	encodeVcdAct->setStatusTip(tr("Start encoding current movie to a VideoCD (VCD/MPEG-1) compliant file"));
	connect(encodeVcdAct, SIGNAL(triggered()), this, SLOT(jobEncodeVcd()));
	encodeVcdAct->setDisabled(true);

	encodeSvcdAct = new QAction(tr("Encode movie to SuperVCD"), this);
	encodeSvcdAct->setStatusTip(tr("Start encoding current movie to a Super-VideoCD (SVCD/MPEG-2) compliant file"));
	connect(encodeSvcdAct, SIGNAL(triggered()), this, SLOT(jobEncodeSvcd()));
	encodeSvcdAct->setDisabled(true);

	encodeXvidAct = new QAction(tr("Encode movie to Xvid/DivX"), this);
	encodeXvidAct->setStatusTip(tr("Start 1-pass encoding current movie to one Xvid/DivX (AVI) file with constant quality"));
	connect(encodeXvidAct, SIGNAL(triggered()), this, SLOT(jobEncodeXvid()));
	encodeXvidAct->setDisabled(true);

	splitMovieAct = new QAction(tr("Split movie into multiple parts"), this);
	splitMovieAct->setStatusTip(tr("Split one video file into multiple smaller files"));
	connect(splitMovieAct, SIGNAL(triggered()), this, SLOT(jobSplitMovie()));
	splitMovieAct->setDisabled(true);

	showMEncoderCapabilityAct = new QAction(tr("Show capabilities"), this);
	showMEncoderCapabilityAct->setStatusTip(tr("Show MEncoder engine capabilities"));
	connect(showMEncoderCapabilityAct, SIGNAL(triggered()), this, SLOT(showMEncoderCapability()));

	showMEncoderStatusAct = new QAction(tr("Show status"), this);
	showMEncoderStatusAct->setStatusTip(tr("Show MEncoder engine status"));
	connect(showMEncoderStatusAct, SIGNAL(triggered()), this, SLOT(showMEncoderStatus()));

	showFFmpegCapabilityAct = new QAction(tr("Show capabilities"), this);
	showFFmpegCapabilityAct->setStatusTip(tr("Show FFmpeg engine capabilities"));
	connect(showFFmpegCapabilityAct, SIGNAL(triggered()), this, SLOT(showFFmpegCapability()));

	showFFmpegStatusAct = new QAction(tr("Show status"), this);
	showFFmpegStatusAct->setStatusTip(tr("Show FFmpeg engine status"));
	connect(showFFmpegStatusAct, SIGNAL(triggered()), this, SLOT(showFFmpegStatus()));

	showMEncoderCapabilityAct = new QAction(tr("Show capabilities"), this);
	showMEncoderCapabilityAct->setStatusTip(tr("Show MEncoder engine capabilities"));
	connect(showMEncoderCapabilityAct, SIGNAL(triggered()), this, SLOT(showMEncoderCapability()));

	showMEncoderStatusAct = new QAction(tr("Show status"), this);
	showMEncoderStatusAct->setStatusTip(tr("Show MEncoder engine status"));
	connect(showMEncoderStatusAct, SIGNAL(triggered()), this, SLOT(showMEncoderStatus()));

	showMPlayerCapabilityAct = new QAction(tr("Show MPlayer capabilities"), this);
	showMPlayerCapabilityAct->setStatusTip(tr("Show video and audio supported drivers by MPlayer backend"));
	connect(showMPlayerCapabilityAct, SIGNAL(triggered()), this, SLOT(showPlayerCapability()));

	showMPlayerStatusAct = new QAction(tr("Show MPlayer status"), this);
	showMPlayerStatusAct->setStatusTip(tr("Show MPlayer backend status"));
	connect(showMPlayerStatusAct, SIGNAL(triggered()), this, SLOT(showPlayerStatus()));

	showMediaInfoStatusAct = new QAction(tr("Show MediaInfo status"), this);
	showMediaInfoStatusAct->setStatusTip(tr("Show MediaInfo utility status"));
	connect(showMediaInfoStatusAct, SIGNAL(triggered()), this, SLOT(showMediaInfoStatus()));

	showMediaInfoAct = new QAction(tr("Media informations"), this);
	showMediaInfoAct->setStatusTip(tr("Show current movie detailed informations"));
	connect(showMediaInfoAct, SIGNAL(triggered()), this, SLOT(showMediaInfo()));

	showFFprobeStatusAct = new QAction(tr("Show FFprobe status"), this);
	showFFprobeStatusAct->setStatusTip(tr("Show FFprobe utility status"));
	connect(showFFprobeStatusAct, SIGNAL(triggered()), this, SLOT(showFFprobeStatus()));

	showFFprobeAct = new QAction(tr("FFprobe informations"), this);
	showFFprobeAct->setStatusTip(tr("Show current movie FFprobe informations"));
	connect(showFFprobeAct, SIGNAL(triggered()), this, SLOT(showFFprobe()));

	zoomAutoAct = new QAction(tr("Auto"), this);
	zoomAutoAct->setCheckable(true);
	zoomAutoAct->setChecked(true);
	connect(zoomAutoAct, SIGNAL(triggered()), this, SLOT(changeZoom()));

	zoomMaxAct = new QAction(tr("Max (1366x768)"), this);
	zoomMaxAct->setCheckable(true);
	connect(zoomMaxAct, SIGNAL(triggered()), this, SLOT(changeZoom()));

	zoom21Act = new QAction(tr("2:1"), this);
	zoom21Act->setCheckable(true);
	connect(zoom21Act, SIGNAL(triggered()), this, SLOT(changeZoom()));

	zoom11Act = new QAction(tr("1:1"), this);
	zoom11Act->setCheckable(true);
	connect(zoom11Act, SIGNAL(triggered()), this, SLOT(changeZoom()));

	zoom34Act = new QAction(tr("3:4"), this);
	zoom34Act->setCheckable(true);
	connect(zoom34Act, SIGNAL(triggered()), this, SLOT(changeZoom()));

	zoom12Act = new QAction(tr("1:2"), this);
	zoom12Act->setCheckable(true);
	connect(zoom12Act, SIGNAL(triggered()), this, SLOT(changeZoom()));

	zoom14Act = new QAction(tr("1:4"), this);
	zoom14Act->setCheckable(true);
	connect(zoom14Act, SIGNAL(triggered()), this, SLOT(changeZoom()));

	zoomActGroup = new QActionGroup(this);
	zoomActGroup->addAction(zoomAutoAct);
	zoomActGroup->addAction(zoomMaxAct);
	zoomActGroup->addAction(zoom21Act);
	zoomActGroup->addAction(zoom11Act);
	zoomActGroup->addAction(zoom34Act);
	zoomActGroup->addAction(zoom12Act);
	zoomActGroup->addAction(zoom14Act);
	zoomActGroup->setExclusive(true);

	toggleConsoleButtonAct = new QAction(tr("Show console buttons"), this);
	toggleConsoleButtonAct->setCheckable(true);
	toggleConsoleButtonAct->setChecked(showConsoleButton);
	toggleConsoleButtonAct->setStatusTip(tr("Toggle console buttons display"));
	connect(toggleConsoleButtonAct, SIGNAL(triggered()), this, SLOT(toggleConsoleButton()));

	toggleAdvancedMonitorAct = new QAction(tr("Show advanced monitor"), this);
	toggleAdvancedMonitorAct->setCheckable(true);
	toggleAdvancedMonitorAct->setStatusTip(tr("Toggle advanced monitor display"));
	connect(toggleAdvancedMonitorAct, SIGNAL(triggered()), this, SLOT(toggleAdvancedMonitor()));

	resetAdvancedMonitorAct = new QAction(tr("Restore monitor window"), this);
	resetAdvancedMonitorAct->setStatusTip(
		tr("Resize and move the advanced monitor beside the main window"));
	connect(resetAdvancedMonitorAct, SIGNAL(triggered()), this, SLOT(resetAdvancedMonitor()));
	resetAdvancedMonitorAct->setEnabled(false);

	clearAdvancedMonitorAct = new QAction(tr("Clear monitor channels"), this);
	clearAdvancedMonitorAct->setStatusTip(
		tr("Clear log and process channels"));
	connect(clearAdvancedMonitorAct, SIGNAL(triggered()), this, SLOT(clearAdvancedMonitor()));
	clearAdvancedMonitorAct->setEnabled(false);

	verbosityQuietAct = new QAction(tr("Quiet"), this);
	verbosityQuietAct->setStatusTip(tr("Show relevant information and error issues in log"));
	verbosityQuietAct->setCheckable(true);
	verbosityQuietAct->setChecked(true);
	connect(verbosityQuietAct, SIGNAL(triggered()), this, SLOT(changeVerbosity()));

	verbosityNormalAct = new QAction(tr("Normal"), this);
	verbosityNormalAct->setStatusTip(tr("More verbose and display input/output in monitor (slightly slow down reponsiveness)"));
	verbosityNormalAct->setCheckable(true);
	connect(verbosityNormalAct, SIGNAL(triggered()), this, SLOT(changeVerbosity()));

	verbosityVerboseAct = new QAction(tr("Verbose"), this);
	verbosityVerboseAct->setStatusTip(tr("Show all informations error issues (significantly slow down responsiveness)"));
	verbosityVerboseAct->setCheckable(true);
	connect(verbosityVerboseAct, SIGNAL(triggered()), this, SLOT(changeVerbosity()));

	verbosityActGroup = new QActionGroup(this);
	verbosityActGroup->addAction(verbosityQuietAct);
	verbosityActGroup->addAction(verbosityNormalAct);
	verbosityActGroup->addAction(verbosityVerboseAct);
	verbosityActGroup->setExclusive(true);

	showPreferencesAct = new QAction(tr("Preferences"), this);
	showPreferencesAct->setStatusTip(tr("Set application preferences"));
	connect(showPreferencesAct, SIGNAL(triggered()), this, SLOT(showPreferences()));

	clearAllSettingsAct = new QAction(tr("Clear all settings"), this);
	clearAllSettingsAct->setStatusTip(tr("Clear all settings and restart the application"));
	connect(clearAllSettingsAct, SIGNAL(triggered()), this, SLOT(clearAllSettings()));

	onlineHelpAct = new QAction(tr("On-line help"), this);
	onlineHelpAct->setStatusTip(tr("Open on-line help"));
	connect(onlineHelpAct, SIGNAL(triggered()), this, SLOT(onlineHelp()));

	onlineFaqAct = new QAction(tr("On-line FAQ"), this);
	onlineFaqAct->setStatusTip(tr("Frequently Asked Questions"));
	connect(onlineFaqAct, SIGNAL(triggered()), this, SLOT(onlineFaq()));

	checkUpdateAct = new QAction(tr("Check for updates"), this);
	checkUpdateAct->setStatusTip(tr("Check for new updates available on-line"));
	connect(checkUpdateAct, SIGNAL(triggered()), this, SLOT(checkUpdate()));

	creditsAct = new QAction(tr("Credits"), this);
	creditsAct->setStatusTip(tr("Show the %1 credits").arg(APPTITLE));
	connect(creditsAct, SIGNAL(triggered()), this, SLOT(credits()));

	aboutAct = new QAction(tr("About %1").arg(APPTITLE), this);
	aboutAct->setStatusTip(tr("Show the %1 about box").arg(APPTITLE));
	connect(aboutAct, SIGNAL(triggered()), this, SLOT(about()));
}

void MainWindow::createMenus()
{
	fileMenu = menuBar()->addMenu(tr("File"));
	fileMenu->addAction(openDvdAct);
	fileMenu->addAction(openFileAct);
	fileMenu->addAction(clearRecentAct);
	fileMenu->addSeparator();
	fileMenu->addAction(setTargetAct);
	fileMenu->addAction(openTargetOnDesktopAct);
	fileMenu->addSeparator();
	for (int i = 0; i < MaxRecentMovies; ++i)
		fileMenu->addAction(recentMoviesActs[i]);
	fileMenu->addAction(exitAct);

	menuBar()->addSeparator();
	
	jobMenu = menuBar()->addMenu(tr("Job"));
	jobMenu->addAction(toggleJobManagerAct);
	jobMenu->addSeparator();
	jobMenu->addAction(encodeVcdAct);
	jobMenu->addAction(encodeSvcdAct);
	jobMenu->addAction(encodeXvidAct);
	operationsSeperatorAct = jobMenu->addSeparator();
	jobMenu->addAction(splitMovieAct);

	menuBar()->addSeparator();
		
	encoderMenu = menuBar()->addMenu(tr("Encoder"));
	encoderFFmpegMenu = new QMenu(tr("FFmpeg engine..."), this);
	encoderFFmpegMenu->addAction(showFFmpegCapabilityAct);
	encoderFFmpegMenu->addAction(showFFmpegStatusAct);
	encoderMEncoderMenu = new QMenu(tr("MEncoder engine..."), this);
	encoderMEncoderMenu->addAction(showMEncoderCapabilityAct);
	encoderMEncoderMenu->addAction(showMEncoderStatusAct);
	encoderEnginesMenu = new QMenu(tr("Encoder engines..."), this);
	encoderFFmpegAct = encoderEnginesMenu->addMenu(encoderFFmpegMenu);
	encoderMEncoderAct = encoderEnginesMenu->addMenu(encoderMEncoderMenu);
	encoderEnginesAct = encoderMenu->addMenu(encoderEnginesMenu);

	menuBar()->addSeparator();
		
	playerMenu = menuBar()->addMenu(tr("Player"));
	zoomMenu = new QMenu(tr("Display zoom..."), this);
	zoomMenu->addAction(zoomAutoAct);
	zoomMenu->addAction(zoomMaxAct);
	zoomMenu->addAction(zoom21Act);
	zoomMenu->addAction(zoom11Act);
	zoomMenu->addAction(zoom34Act);
	zoomMenu->addAction(zoom12Act);
	zoomMenu->addAction(zoom14Act);
	zoomMenuAct = playerMenu->addMenu(zoomMenu);
	playerMenu->addAction(showMediaInfoAct);
	playerMenu->addSeparator();
	playerBackendMenu = new QMenu(tr("Player backend..."), this);
	playerBackendMenu->addAction(showMPlayerCapabilityAct);
	playerBackendMenu->addAction(showMPlayerStatusAct);
	playerBackendMenu->addAction(showMediaInfoStatusAct);
	playerBackendMenu->addAction(showFFprobeStatusAct);
	playerBackendAct = playerMenu->addMenu(playerBackendMenu);

	menuBar()->addSeparator();
		
	advancedMenu = menuBar()->addMenu(tr("Options"));
	advancedMenu->addAction(toggleConsoleButtonAct);
	advancedMenu->addAction(toggleAdvancedMonitorAct);
	advancedMenu->addAction(resetAdvancedMonitorAct);
	advancedMenu->addAction(clearAdvancedMonitorAct);
	advancedMenu->addSeparator();
	verbosityMenu = new QMenu(tr("Verbosity..."), this);
	verbosityMenu->addAction(verbosityQuietAct);
	verbosityMenu->addAction(verbosityNormalAct);
	verbosityMenu->addAction(verbosityVerboseAct);
	verbosityMenuAct = advancedMenu->addMenu(verbosityMenu);
	advancedMenu->addAction(showPreferencesAct);
	advancedMenu->addAction(clearAllSettingsAct);

	menuBar()->addSeparator();
		
	helpMenu = menuBar()->addMenu(tr("Help"));
	helpMenu->addAction(onlineFaqAct);
	helpMenu->addAction(onlineHelpAct);
	helpMenu->addAction(checkUpdateAct);
	helpMenu->addAction(creditsAct);
	helpMenu->addSeparator();
	helpMenu->addAction(aboutAct);
}

void MainWindow::createStatusBar()
{
	statusLabel = new QLabel(tr("Ready"));
	statusLabel->setMinimumSize(statusLabel->sizeHint());

	statusBar()->addWidget(statusLabel);
}

void MainWindow::createControls()
{
	// A common monitor
	advancedMonitor = new AdvancedMonitorWidget(this, 0);

	connect(advancedMonitor, SIGNAL(hidden()), 
		this, SLOT(updateAdvancedMonitorMenu()));
	connect(advancedMonitor, SIGNAL(visible()), 
		this, SLOT(updateAdvancedMonitorMenu()));

	// In/Out/Err/Log channels monitoring
	logChannel = new QTextEdit;
	logChannel->setReadOnly(true);
	inputChannel = new QTextEdit;
	inputChannel->setReadOnly(true);
	outputChannel = new QTextEdit;
	outputChannel->setReadOnly(true);
	errorChannel = new QTextEdit;
	errorChannel->setReadOnly(true);

	advancedMonitor->addLog( tr("Log:"), logChannel );
	advancedMonitor->addLog( tr("Process Input Channel:"), inputChannel );
	advancedMonitor->addLog( tr("Process Output Channel:"), outputChannel );
	advancedMonitor->addLog( tr("Process Error Channel:"), errorChannel );
	advancedMonitor->setLineWrapMode( logChannel, QTextEdit::NoWrap );
	advancedMonitor->setLineWrapMode( outputChannel, QTextEdit::NoWrap );
	advancedMonitor->setLineWrapMode( errorChannel, QTextEdit::NoWrap );

	// The job manager
	jobManager = new JobManager(inputChannel, outputChannel, errorChannel, logChannel, verbosity,
				preferences.encoderPreferences.latency_start, 
				preferences.encoderPreferences.latency_finished, 
				preferences.encoderPreferences.latency_kill);
	
	connect(jobManager, SIGNAL(processStarted(Task *)), 
		this, SLOT(startProcessConsole(Task *)));

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

	// The job manager GUI
	jobManagerDialog = new JobManagerDialog(tr("%1 - Job manager").arg(APPTITLE), this);
	jobManagerDialog->setVerbosity(verbosity);
	jobManagerWidget = jobManagerDialog->getManagerWidget();
	jobManagerWidget->setJobManager(jobManager);
	jobManagerWidget->setNoConsoleButton(!showConsoleButton);

	connect(jobManagerDialog, SIGNAL(hidden()), this, SLOT(updateJobManagerMenu()));
	connect(jobManagerWidget, SIGNAL(guiSizeAdjustRequest()), jobManagerDialog, SLOT(adjustGuiSize()));

	// The player widget
	playerWidget = new PlayerWidget(this, mplayerBinPath, mediainfoBinPath, ffprobeBinPath,
		inputChannel, outputChannel, errorChannel, logChannel, verbosity, 
		&preferences.playerPreferences, this);
#ifdef Q_OS_MAC
	playerWidget->resize(320, 50);
#else
	playerWidget->resize(320, 240);
#endif

	// The encoder widget created after PlayerWidget to send it messages via WidgetBridge
	encoderWidget = new EncoderWidget(this, jobManager,
		ffmpegBinPath, mencoderBinPath, inputChannel, outputChannel, 
		errorChannel, logChannel, verbosity, &preferences.encoderPreferences, 0);
	
	encoderWidget->setPlayerPreferences(this, mplayerBinPath, mediainfoBinPath, ffprobeBinPath,
		inputChannel, outputChannel, errorChannel, logChannel, verbosity, 
		&preferences.playerPreferences);

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

	// The main widget
	mainWidget = new MainWidget(playerWidget, encoderWidget);

	connect(playerWidget, SIGNAL(guiSizeAdjustRequest()), mainWidget, SLOT(adjustGuiSize()));
	connect(mainWidget, SIGNAL(guiSizeAdjustRequest()), this, SLOT(adjustGuiSize()));

	setCentralWidget(mainWidget);
}

void MainWindow::adjustGuiSize()
{
	adjustSize();
}

void MainWindow::clearLog()
{
	if ( logChannel != 0)
		logChannel->clear();
}

void MainWindow::clearIn()
{
	if ( inputChannel != 0)
		inputChannel->clear();
}

void MainWindow::clearOut()
{
	if ( outputChannel != 0)
		outputChannel->clear();
}

void MainWindow::clearErr()
{
	if ( errorChannel != 0)
		errorChannel->clear();
}

void MainWindow::readJobProcessStdOut(Process *process)
{
	//qDebug() << "MainWindow::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();

	// 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
		if ( !strOutput.isEmpty() ) 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() << "MainWindow::readJobProcessStdOut encoder parse output failed";
		}
	}
}

void MainWindow::readJobProcessStdErr(Process *process)
{
	//qDebug() << "MainWindow::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();

	// 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 if ( !strError.isEmpty() ) 
		{
			qDebug() << "MainWindow::readJobProcessStdOut encoder parse error failed";
		}
	}
	else
	{
		if ( !strError.isEmpty() ) console->displayError( &strError );
	}
}

void MainWindow::finishJobProcess(Process *process, int exitCode, QProcess::ExitStatus exitStatus)
{
	//qDebug() << "MainWindow::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
		if ( !strOutput.isEmpty() ) 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() << "MainWindow::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 )
	{
		if ( !strError.isEmpty() ) 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() << "MainWindow::finishJobProcess encoder parse error failed";
		}
	}

	jobManager->setProcessDone( process );

	emit encodingComplete(process);
}

void MainWindow::startProcessConsole(Task *task)
{
	jobManagerWidget->addTaskConsole(task);

	jobManagerDialog->show();
	updateJobManagerMenu();
}

void MainWindow::onMovieLoaded(int media)
{
	if ( media == Video::Media::DVD )
	{
		encoderWidget->setInput( currentDvd,  Video::Media::DVD );
		encoderWidget->enableControls( true );
		updateEncoderInput();

		updateJobMenu();

		setWindowTitle(currentDvd.isEmpty()? appName : tr("%1 - %2").arg(appName).arg(currentDvd));
		showMediaInfoAct->setEnabled(true);
		showFFprobeAct->setEnabled(true);
	}
	else if ( media == Video::Media::File )
	{
		encoderWidget->setInput( currentFile,  Video::Media::File );
		encoderWidget->enableControls( true );
		updateEncoderInput();

		updateJobMenu();

		QFileInfo fi ( currentFile ); 
		QString str = fi.fileName();

		setWindowTitle(currentFile.isEmpty()? appName : tr("%1 - %2").arg(appName).arg(str));
		showMediaInfoAct->setEnabled(true);
		showFFprobeAct->setEnabled(true);
	}
	else
	{
		encoderWidget->enableControls( false );

		updateJobMenu();

		setWindowTitle(appName);
		showMediaInfoAct->setEnabled(false);
		showFFprobeAct->setEnabled(false);
	}
}

void MainWindow::updateJobMenu()
{
	bool ready = encoderWidget->isReady() && encoderWidget->audioAndVideoSelected();

	encodeVcdAct->setEnabled( ready );
	encodeSvcdAct->setEnabled( ready );
	encodeXvidAct->setEnabled( ready );

	int Nvideo, Naudio, Nsubtitle;
	playerWidget->streamCount(&Nvideo, &Naudio, &Nsubtitle);

	splitMovieAct->setEnabled( ready 
		&& playerWidget->currentMedia() == Video::Media::File
		&& Nvideo < 2 && Naudio < 2 && Nsubtitle < 2);
}

void MainWindow::about()
{
#ifdef _DEBUG
#define BUILD_STR "debug-build"
#else
#define BUILD_STR "build"
#endif

	InfoBoxDialog::about(this, tr("About %1").arg(APPTITLE),
			tr(	"<br><b>About DVDx 4.0 Open Edition</b><br><br>"
				"Version %2 %3 %4 (%5)<br>" 
				"Running with Qt %6<br>" 
				"Author: <a href='mailto:starbuck@labdv.com?subject=%1'> "
				"Starbuck&lt;starbuck@labdv.com&gt;</a><br><br>"
				"<b>%1</b> is an audio/video encoder as well as a powerful DVD copier. "
				"You can load DVDs and quiet any audio or video files and encode to most "
				"popular video formats: VCD/CVD/SVCD/DVD, Xvid/Divx, MPEG-4, H.264, "
				"AVC, Flash Video, iPod, Sony PSP, QuickTime, Ogg, WebM and audio fortmats: "
				"AC.3, MP3, MP4/AAC, Lossless Audio, PCM, Vorbis etc.<br><br>"
   				"<a href='http://www.labdv.com/dvdx'>Visit DVDx 4 web site.</a>")
			.arg(APPTITLE).arg( APPVERSION_STR ).arg( BUILD_STR ).arg( BUILDNUMBER_STR )
			.arg( PLATFORM_STR ).arg( qVersion() ));
}

void MainWindow::credits()
{
	InfoBoxDialog::about(this, tr("Credits"),
			tr(	"<br>"
				"<b>DVDx</b><br>\n"
				"<a href='http://www.labdv.com/dvdx/'>%1 Smart Edition</a>: Jim<br>"
				"<a href='http://www.labdv.com/dvdx/community'>%1 Community Edition</a>: Starbuck<br>"
				"<a href='http://www.labdv.com/dvdx2/'>DVDx 2</a>: Jean-Luc Pons<br>"
				"<br>"
				"<b>Encoding engines</b><br>\n"
				"<a href='http://www.mplayerhq.hu/'>MEncoder/MPlayer</a>: The MPlayer Team<br>"
				"<a href='http://ffmpeg.org/'>FFmpeg</a>: Baptiste Coudurier, Diego Biurrun, "
				"Jason Garrett-Glaser, Luca Barbato, Michael Niedermayer, Stefano Sabatini<br>"
				"<br>"
#ifdef WIN32
				"<b>Encoding binaries</b><br>\n"
				"<a href='http://oss.netfarm.it/mplayer-win32.php'>Gianluigi Tiesi</a>: MEncoder/MPlayer builds<br>"
				//"<a href='http://x264.nl/'>Anonymous</a>: x264 builds<br>"
				"<a href='http://ffmpeg.zeranoe.com/'>Zeranoe</a>: FFmpeg builds<br>"
				"<br>"
#endif
				"<b>Video utilities</b><br>\n"
				"Zenitram: <a href='http://mediainfo.sourceforge.net/fr'>MediaInfo</a><br>"
				).arg(APPTITLE) );
}

void MainWindow::onlineHelp()
{
	QDesktopServices::openUrl( QUrl("http://www.labdv.com/dvdx/help") );
}

void MainWindow::onlineFaq()
{
	QDesktopServices::openUrl( QUrl("http://www.labdv.com/dvdx/faq.php") );
}

void MainWindow::checkUpdate()
{
	// Open dialog to check for updates from DVDx website
	SoftwareUpdateDialog dlg(this);
	dlg.checkUpdate();
	dlg.exec();
}

void MainWindow::enableVideoOpenActions(bool isPlaying)
{
	// We received player isPlaying state
	openDvdAct->setDisabled( isPlaying );
	openFileAct->setDisabled( isPlaying );
	
	zoom21Act->setDisabled( isPlaying );
	zoom11Act->setDisabled( isPlaying );
	zoom34Act->setDisabled( isPlaying );
	zoom12Act->setDisabled( isPlaying );
	zoom14Act->setDisabled( isPlaying );

	encoderWidget->enableControls( !isPlaying );

#ifdef Q_OS_MAC
	if (isPlaying)
	{
		//setWindowFlags(Qt::Drawer);
	}
	else
	{
		//setWindowFlags(Qt::Window);
	}
#endif
}

void MainWindow::openDvd()
{
	if (!isApplicationReady())
	{
		checkApplicationReady();  // to display alert
		return;
	}

	encoderWidget->enableControls( false );
	QCoreApplication::processEvents();

	clearIn();
	clearOut();
	clearErr();
	clearLog();	

	QString startDir = (currentDvd.isEmpty()) ? QDir::rootPath() : currentDvd;
	
	QString dvd = QFileDialog::getExistingDirectory(this,
			tr("Open DVD VIDEO_TS Folder"),
			startDir,
			QFileDialog::ShowDirsOnly);

	if (!dvd.isEmpty())
	{
		statusBar()->showMessage(tr("Loading DVD from %1").arg(dvd));

		if (playerWidget->setInput( dvd, Video::Media::DVD ))
		{
			setCurrentDvdPath(dvd);
			updateTargetName(Video::Media::DVD); 

 			emit movieLoaded( Video::Media::DVD );
	
			statusBar()->showMessage(tr("DVD loaded [%1]").arg(dvd), 5000);
			return;
		} 
		else
		{
			emit movieLoaded( -1 );

			statusBar()->showMessage(tr("Failed to set player input [%1]")
				.arg(dvd), 5000);
			return;
		}
	}

	statusBar()->showMessage(tr("DVD not loaded"), 5000);
}

void MainWindow::openFile()
{
	if (!isApplicationReady())
	{
		checkApplicationReady();  // to display alert
		return;
	}

	encoderWidget->enableControls( false );
	QCoreApplication::processEvents();

	clearIn();
	clearOut();
	clearErr();
	clearLog();	

	QString startDir = QFileInfo(currentFile).canonicalPath();

	if (startDir.isEmpty() || !QDir(startDir).exists())
	{
		QString MoviesLocation = QDesktopServices::storageLocation(QDesktopServices::MoviesLocation);
		QString DocumentsLocation = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation);
	
		startDir = MoviesLocation;
		if (startDir.isEmpty() || !QDir(startDir).exists()) startDir = DocumentsLocation;
		if (startDir.isEmpty() || !QDir(startDir).exists()) startDir = QDir::homePath();
	}
	
	QString file = QFileDialog::getOpenFileName(this,
			tr("Open media file"),
			startDir);
	
	if (!file.isEmpty())
	{
		statusBar()->showMessage(tr("Loading movie from %1").arg(file));

		if (playerWidget->setInput( file, Video::Media::File ))	
		{
			setCurrentFilePath(file);
			updateTargetName(Video::Media::File); 

			emit movieLoaded( Video::Media::File );

			statusBar()->showMessage(tr("Movie file loaded [%1]")
				.arg(file), 5000);
			return;
		} 
		else
		{
			emit movieLoaded( -1 );

			statusBar()->showMessage(tr("Failed to set player input [%1]")
				.arg(file), 5000);
			return;
		}
	}

	statusBar()->showMessage(tr("Movie file not loaded"), 5000);
}

void MainWindow::clearRecent()
{
	recentFiles.clear();

	currentDvd.clear();
	currentFile.clear();

	updateRecentPathActions();
}

void MainWindow::openRecentMovie()
{
	if (!isApplicationReady())
	{
		checkApplicationReady();  // to display alert
		return;
	}

	QString recentMovie;

	QAction *action = qobject_cast<QAction *>(sender());
	if (action)
		recentMovie = action->data().toString();
	else
		return;

	encoderWidget->enableControls( false );
	QCoreApplication::processEvents();

	clearIn();
	clearOut();
	clearErr();
	clearLog();	

	QString path = recentMovie.section(";", -2, -2);
	int media = recentMovie.section(";", -1, -1).toInt();

	if ( media == Video::Media::DVD )
	{
		statusBar()->showMessage(tr("Loading DVD from %1").arg(path));

		if (playerWidget->setInput( path, Video::Media::DVD ))
		{
			setCurrentDvdPath(path);
			updateTargetName(Video::Media::DVD); 

			emit movieLoaded( Video::Media::DVD );

			statusBar()->showMessage(tr("DVD loaded [%1]").arg(path), 5000);
			return;
		} 
		else
		{
			deleteFromRecentMovie(recentMovie);

			emit movieLoaded( -1 );

			statusBar()->showMessage(tr("Failed to set player input [%1]")
				.arg(path), 5000);
			return;
		}
	}
	else
	{
		statusBar()->showMessage(tr("Loading movie from %1").arg(path));

		if (playerWidget->setInput( path, Video::Media::File ))	
		{
			setCurrentFilePath(path);
			updateTargetName(Video::Media::File); 

			emit movieLoaded( Video::Media::File );

			statusBar()->showMessage(tr("Movie file loaded [%1]")
				.arg(path), 5000);
			return;
		} 
		else
		{
			deleteFromRecentMovie(recentMovie);

			emit movieLoaded( -1 );

			statusBar()->showMessage(tr("Failed to set player input [%1]")
				.arg(path), 5000);
			return;
		}
	}
}

void MainWindow::deleteFromRecentMovie(const QString &movie)
{
	for (int i = 0; i < recentFiles.size(); ++i)
	{
		if (recentFiles[i] == movie)
		{
			recentFiles.removeAt(i);
			break;
		}
	}

	updateRecentPathActions();
}

void MainWindow::updateTargetName(int media)
{
	QString name(playerWidget->currentName());

	if (!preferences.encoderPreferences.force_output_name 
		&& !name.isEmpty())
	{
		// Name can be a path (and currentTargetName is a filename)
		if ( name.contains(QRegExp("[\\\\/:]")) )
		{
			// Most DVD rippers create folder like .../DVD_IDENTIFIER/VIDEO_TS
			QRegExp rx("(\\w{2,}).VIDEO_TS$");
			if (rx.indexIn(name) > -1)
			{
				// We bet DVD identifier is > 2chars, and >2chars is OK for a file name
				currentTargetName = rx.cap(1);
			}
			else
			{
				if (media == Video::Media::DVD)
				{
					currentTargetName = DEFAULT_DVD_OUTPUT;
				}
				else
				{
					currentTargetName = DEFAULT_FILE_OUTPUT;
				}
			}
		}
		else
		{
			currentTargetName = name;
		}
	}
	else if (preferences.encoderPreferences.force_output_name 
		&& !preferences.encoderPreferences.output_name.isEmpty())
	{
		currentTargetName = preferences.encoderPreferences.output_name;
	}
	else
	{
		if (media == Video::Media::DVD)
		{
			currentTargetName = DEFAULT_DVD_OUTPUT;
		}
		else
		{
			currentTargetName = DEFAULT_FILE_OUTPUT;
		}
	}
	
	encoderWidget->setOutputFile( currentTargetDir, currentTargetName );
}

void MainWindow::setTarget()
{
	statusBar()->showMessage(tr("Select output target directory..."));

	// First select an existing directory
	QString startDir = currentTargetDir;

	if (startDir.isEmpty() || !QDir(startDir).exists())
	{
		QString MoviesLocation = QDesktopServices::storageLocation(QDesktopServices::MoviesLocation);
		QString DocumentsLocation = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation);
	
		startDir = MoviesLocation;
		if (startDir.isEmpty() || !QDir(startDir).exists()) startDir = DocumentsLocation;
		if (startDir.isEmpty() || !QDir(startDir).exists()) startDir = QDir::homePath();
	}
	
	QString dir = QFileDialog::getExistingDirectory(this,
			tr("Select the output target directory"),
			startDir);

	if (!dir.isEmpty())
	{
		currentTargetDir = dir;
		encoderWidget->setOutputFile( currentTargetDir, currentTargetName );

		statusBar()->showMessage(tr("Output target folder is set [%1]").arg(dir));
		return;
	}
	else if (!currentTargetDir.isEmpty())
	{
		statusBar()->showMessage(tr("Output target folder unchanged [%1]")
			.arg(currentTargetDir), 5000);
		return;
	}

	statusBar()->showMessage(tr("Output target folder is not defined"), 5000);
}

void MainWindow::openTargetOnDesktop()
{
	statusBar()->showMessage(tr("Open target folder on desktop..."));

	QDesktopServices::openUrl(QUrl( QString("file:///%1").arg(currentTargetDir) ));

	statusBar()->showMessage(tr("Done"), 3000);
}

void MainWindow::readSettingsForWindow()
{
	QSettings settings;

	settings.beginGroup("Main Window");

	if (settings.contains("position"))
	{
	    QPoint position = settings.value("position").toPoint();
		move(position);
	}

	if (settings.contains("size"))
	{
		QSize size = settings.value("size").toSize();
		resize(size);
	}

	settings.endGroup();  // Main Window
}

void MainWindow::setPathsForExternals()
{
#ifdef WIN32
		ffmpegBinPath = QCoreApplication::applicationDirPath() + "/" + DEFAULTFFMPEGPATH;
#else
		ffmpegBinPath = DEFAULTFFMPEGPATH;
#endif

#ifdef WIN32
		mencoderBinPath = QCoreApplication::applicationDirPath() + "/" + DEFAULTMENCODERPATH;
#else
		mencoderBinPath = DEFAULTMENCODERPATH;
#endif

#ifdef WIN32
		mplayerBinPath = QCoreApplication::applicationDirPath() + "/" + DEFAULTMPLAYERPATH;
#else
		mplayerBinPath = DEFAULTMPLAYERPATH;
#endif

#ifdef WIN32
		mediainfoBinPath = QCoreApplication::applicationDirPath() + "/" + DEFAULTMEDIAINFOPATH;
#else
		mediainfoBinPath = DEFAULTMEDIAINFOPATH;
#endif

#ifdef WIN32
		ffprobeBinPath = QCoreApplication::applicationDirPath() + "/" + DEFAULTFFPROBEBINPATH;
#else
		ffprobeBinPath = DEFAULTFFPROBEBINPATH;
#endif
}

void MainWindow::readSettingsForFiles()
{
	QSettings settings;

	currentDvd = settings.value("Input/DVD", "").toString();
	currentFile = settings.value("Input/file", "").toString();
	recentFiles = settings.value("Input/recent", QStringList()).toStringList();

	QString MoviesLocation = QDesktopServices::storageLocation(QDesktopServices::MoviesLocation);
	QString DocumentsLocation = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation);
	
	QString defaultTargetDir = MoviesLocation;
	if (defaultTargetDir.isEmpty() || !QDir(defaultTargetDir).exists()) defaultTargetDir = DocumentsLocation;
	if (defaultTargetDir.isEmpty() || !QDir(defaultTargetDir).exists()) defaultTargetDir = QDir::homePath();
	
	currentTargetDir = settings.value("Output/target folder", defaultTargetDir).toString();
	currentTargetName = settings.value("Output/target name", "output").toString();	
}

void MainWindow::readSettingsForMonitor()
{
	QSettings settings;

	settings.beginGroup("Advanced Monitor");

	if (settings.contains("position"))
	{
		QPoint position = settings.value("position").toPoint();
		advancedMonitor->move(position);
		qDebug() << "Advanced Monitor: position =" << advancedMonitor->pos();
	}    
	
	if (settings.contains("size"))
	{
		QSize size = settings.value("size").toSize();
	    advancedMonitor->resize(size);
		qDebug() << "Advanced Monitor: size =" << advancedMonitor->size();
	}

	if (settings.contains("hidden"))
	{
		if (settings.value("hidden").toBool())
			advancedMonitor->hide();
		else
			advancedMonitor->show();
	}

	settings.endGroup();  // Advanced Monitor
}

void MainWindow::readSettingsForOptions()
{
	QSettings settings;

	settings.beginGroup("Options");

	if (settings.contains("console button"))
	{
		showConsoleButton = settings.value("console button").toBool();
	}

	if (settings.contains("verbosity"))
	{
		verbosity = settings.value("verbosity").toInt();
	}

	settings.endGroup();  // Options
}

void MainWindow::readSettingsForPreferences()
{
	QSettings settings;

	settings.beginGroup("Preferences");

	if (settings.contains("Encoder/test frames"))
	{
		preferences.encoderPreferences.test_frames = settings.value("Encoder/test frames").toInt();
	}

	if (settings.contains("Encoder/test chapter"))
	{
		preferences.encoderPreferences.test_chapter = settings.value("Encoder/test chapter").toInt();
	}

	if (settings.contains("Encoder/test position"))
	{
		preferences.encoderPreferences.test_position = settings.value("Encoder/test position").toInt();
	}

	if (settings.contains("Encoder/volume correction"))
	{
		preferences.encoderPreferences.volume_correction = settings.value("Encoder/volume correction").toInt();
	}

	if (settings.contains("Encoder/validation size"))
	{
		preferences.encoderPreferences.validation_size = settings.value("Encoder/validation size").toInt();
	}

	if (settings.contains("Encoder/output name"))
	{
		preferences.encoderPreferences.output_name = settings.value("Encoder/output name").toString();
	}

	if (settings.contains("Encoder/force output name"))
	{
		preferences.encoderPreferences.force_output_name = settings.value("Encoder/force output name").toBool();
	}

	if (settings.contains("Encoder/latency start"))
	{
		preferences.encoderPreferences.latency_start = settings.value("Encoder/latency start").toInt();
	}

	if (settings.contains("Encoder/latency finished"))
	{
		preferences.encoderPreferences.latency_finished = settings.value("Encoder/latency finished").toInt();
	}

		if (settings.contains("Encoder/latency kill"))
	{
		preferences.encoderPreferences.latency_kill = settings.value("Encoder/latency kill").toInt();
	}

	if (settings.contains("Player/DVD/ignore short titles"))
	{
		preferences.playerPreferences.dvd_ignore_short_titles = settings.value("Player/DVD/ignore short titles").toBool();
	}

	if (settings.contains("Player/DVD/title minimum length"))
	{
		preferences.playerPreferences.dvd_title_min_length = settings.value("Player/DVD/title minimum length").toDouble();
	}

	if (settings.contains("Player/latency start"))
	{
		preferences.playerPreferences.latency_start = settings.value("Player/latency start").toInt();
	}

	if (settings.contains("Player/latency finished"))
	{
		preferences.playerPreferences.latency_finished = settings.value("Player/latency finished").toInt();
	}

	if (settings.contains("Player/latency kill"))
	{
		preferences.playerPreferences.latency_kill = settings.value("Player/latency kill").toInt();
	}

	if (settings.contains("Player/poll ticks"))
	{
		preferences.playerPreferences.poll_ticks = settings.value("Player/poll ticks").toInt();
	}

	settings.endGroup();  // Preferences
}

void MainWindow::writeSettings()
{
	QSettings settings;

	// Window stuff
	settings.beginGroup("Main Window");
    settings.setValue("position", pos());
    settings.setValue("size", size()); 
	settings.endGroup();

	// Files
	settings.setValue("Input/DVD", currentDvd);
    settings.setValue("Input/file", currentFile);
	settings.setValue("Input/recent", recentFiles);
	settings.setValue("Output/target folder", currentTargetDir);
	settings.setValue("Output/target name", currentTargetName);

	// Advanced monitor
	settings.beginGroup("Advanced Monitor");
    settings.setValue("position", advancedMonitor->pos());
    settings.setValue("size", advancedMonitor->size());
    settings.setValue("hidden", advancedMonitor->isHidden());
	settings.endGroup();

	// Options
	settings.beginGroup("Options");
    settings.setValue("console button", showConsoleButton);
    settings.setValue("verbosity", verbosity);
	settings.endGroup();

	// Preferences
	settings.beginGroup("Preferences");
	settings.setValue("Encoder/test frames", preferences.encoderPreferences.test_frames);
	settings.setValue("Encoder/test chapter", preferences.encoderPreferences.test_chapter);
	settings.setValue("Encoder/test position", preferences.encoderPreferences.test_position);
	settings.setValue("Encoder/volume correction", preferences.encoderPreferences.volume_correction);
	settings.setValue("Encoder/validation frames", preferences.encoderPreferences.validation_frames);
	settings.setValue("Encoder/validation size", preferences.encoderPreferences.validation_size);
	settings.setValue("Encoder/output name", preferences.encoderPreferences.output_name);
	settings.setValue("Encoder/force output name", preferences.encoderPreferences.force_output_name);
	settings.setValue("Encoder/latency start", preferences.encoderPreferences.latency_start);
	settings.setValue("Encoder/latency finished", preferences.encoderPreferences.latency_finished);
	settings.setValue("Encoder/latency kill", preferences.encoderPreferences.latency_kill);
	settings.setValue("Player/DVD/ignore short titles", preferences.playerPreferences.dvd_ignore_short_titles);
	settings.setValue("Player/DVD/title minimum length", preferences.playerPreferences.dvd_title_min_length);
	settings.setValue("Player/latency start", preferences.playerPreferences.latency_start);
	settings.setValue("Player/latency finished", preferences.playerPreferences.latency_finished);
	settings.setValue("Player/latency kill", preferences.playerPreferences.latency_kill);
	settings.setValue("Player/poll ticks", preferences.playerPreferences.poll_ticks);
	settings.endGroup();

	settings.sync();
}
void MainWindow::updateRecentPathActions()
{
	int numRecentFiles = qMin(recentFiles.size(), (int)MaxRecentMovies);

	for (int i = 0; i < numRecentFiles; ++i) 
	{
		QString str;
		QString path = recentFiles[i].section(";", -2, -2);
		int media = recentFiles[i].section(";", -1, -1).toInt();

		if ( media == Video::Media::File )
		{
			QFileInfo fi ( path ); 
			str = fi.fileName();
		}
		else
		{
			str = path;
		}

		recentMoviesActs[i]->setText(str);
		recentMoviesActs[i]->setData(recentFiles[i]);
		recentMoviesActs[i]->setStatusTip(path);
		recentMoviesActs[i]->setVisible(true);
	}

	for (int j = numRecentFiles; j < MaxRecentMovies; ++j)
		recentMoviesActs[j]->setVisible(false);

	clearRecentAct->setEnabled(numRecentFiles > 0);
}

void MainWindow::saveJob()
{
	if (currentDvd.isEmpty())
		saveJobAs();
//	else
//		saveDvdInfo(curFile);
}

void MainWindow::saveJobAs()
{
	QString fileName = QFileDialog::getSaveFileName(this);
	if (fileName.isEmpty())
		return;

//	saveDvdInfo(fileName);
}

void MainWindow::toggleJobManager()
{
	if (jobManagerDialog->isHidden())
	{
		jobManagerDialog->show();
		updateJobManagerMenu();
	}
	else
	{
		jobManagerDialog->hide();
	}
}

void MainWindow::updateJobManagerMenu()
{
	if (jobManagerDialog->isHidden())
	{
		toggleJobManagerAct->setChecked(false);
	}
	else
	{
		toggleJobManagerAct->setChecked(true);
	}
}

void MainWindow::showFFmpegCapability()
{
	encoder_engine_capability_t cap = encoderWidget->readFFmpegCapability();
	EncoderCapabilityDialog *dlg = new EncoderCapabilityDialog("FFmpeg", &cap, this);
	dlg->exec();		
	Q_CHECK_PTR(dlg);
	delete(dlg);
}

void MainWindow::showFFmpegStatus()
{
	encoder_engine_status_t sta = encoderWidget->readFFmpegStatus();
	EncoderStatusDialog *dlg = new EncoderStatusDialog("FFmpeg", &sta, this);
	dlg->exec();		
	Q_CHECK_PTR(dlg);
	delete(dlg);
}

void MainWindow::showMEncoderCapability()
{
	encoder_engine_capability_t cap = encoderWidget->readMEncoderCapability();
	EncoderCapabilityDialog *dlg = new EncoderCapabilityDialog("MEncoder", &cap, this);
	dlg->exec();		
	Q_CHECK_PTR(dlg);
	delete(dlg);
}

void MainWindow::showMEncoderStatus()
{
	encoder_engine_status_t sta = encoderWidget->readMEncoderStatus();
	EncoderStatusDialog *dlg = new EncoderStatusDialog("MEncoder", &sta, this);
	dlg->exec();		
	Q_CHECK_PTR(dlg);
	delete(dlg);
}

void MainWindow::showPreferences()
{
	PreferencesDialog *dlg = new PreferencesDialog(&preferences, this);
	dlg->exec();		
	Q_CHECK_PTR(dlg);
	delete(dlg);

	encoderWidget->setPreferences(&preferences.encoderPreferences);
	playerWidget->setPreferences(&preferences.playerPreferences);

	jobManager->setLatency(	preferences.encoderPreferences.latency_start, 
							preferences.encoderPreferences.latency_finished, 
							preferences.encoderPreferences.latency_kill );

	if (!preferences.encoderPreferences.output_name.isEmpty()
		&& preferences.encoderPreferences.force_output_name)
	{
		currentTargetName = preferences.encoderPreferences.output_name;
		encoderWidget->setOutputFile( currentTargetDir, currentTargetName );
	}

}

void MainWindow::showPlayerCapability()
{
	player_engine_capability_t cap = playerWidget->readCapability();
	PlayerCapabilityDialog *dlg = new PlayerCapabilityDialog(&cap, this);
	dlg->exec();		
	Q_CHECK_PTR(dlg);
	delete(dlg);
}

void MainWindow::showPlayerStatus()
{
	player_engine_status_t sta = playerWidget->readStatus();
	PlayerStatusDialog *dlg = new PlayerStatusDialog(&sta, this);
	dlg->exec();		
	Q_CHECK_PTR(dlg);
	delete(dlg);
}

void MainWindow::showMediaInfoStatus()
{
	utility_status_t sta = playerWidget->readMediaInfoStatus();
	UtilityStatusDialog *dlg = new UtilityStatusDialog(tr("MediaInfo status"), &sta, this);
	dlg->exec();		
	Q_CHECK_PTR(dlg);
	delete(dlg);
}

void MainWindow::showMediaInfo()
{
	interfacemediainfo_data_t data = playerWidget->readMediaInfoData();
	MediaInfoDataDialog *dlg = new MediaInfoDataDialog(&data, this);
	dlg->exec();		
	Q_CHECK_PTR(dlg);
	delete(dlg);
}

void MainWindow::showFFprobeStatus()
{
	utility_status_t sta = playerWidget->readFFprobeStatus();
	UtilityStatusDialog *dlg = new UtilityStatusDialog(tr("FFprobe status"), &sta, this);
	dlg->exec();		
	Q_CHECK_PTR(dlg);
	delete(dlg);
}

void MainWindow::showFFprobe()
{
	interfaceffprobe_t data = playerWidget->readFFprobeData();
/*	FFprobeDataDialog *dlg = new FFprobeDataDialog(&data, this);
	dlg->exec();		
	Q_CHECK_PTR(dlg);
	delete(dlg);
*/
}

void MainWindow::printLog(const QString &message, const int verbosityLevel)
{
	if ( logChannel == 0 )
		qDebug() << "MainWindow Log:" << message;
	else if ( verbosityLevel <= verbosity )
		logChannel->append( message );
}

void MainWindow::toggleConsoleButton()
{
	showConsoleButton = toggleConsoleButtonAct->isChecked();
	
	encoderWidget->setNoConsoleButton(!showConsoleButton);
	jobManagerWidget->setNoConsoleButton(!showConsoleButton);
}

void MainWindow::toggleAdvancedMonitor()
{
	if (advancedMonitor->isHidden())
	{
		advancedMonitor->show();
	}
	else
	{
		advancedMonitor->hide();
	}

	activateWindow();  // keep focus
}

void MainWindow::updateAdvancedMonitorMenu()
{
	if (advancedMonitor->isHidden())
	{
		toggleAdvancedMonitorAct->setChecked(false);
		resetAdvancedMonitorAct->setEnabled(false);
		clearAdvancedMonitorAct->setEnabled(false);
	}
	else
	{
		toggleAdvancedMonitorAct->setChecked(true);
		resetAdvancedMonitorAct->setEnabled(true);
		clearAdvancedMonitorAct->setEnabled(true);
	}

	activateWindow();  // get focus
}

void MainWindow::resetAdvancedMonitor()
{
	advancedMonitor->resetAppearance(this);
}

void MainWindow::clearAdvancedMonitor()
{
	advancedMonitor->clearChannels();
}

void MainWindow::changeVerbosity()
{
	if (verbosityQuietAct->isChecked())
	{
		verbosity = VERBOSITY_QUIET;
	}
	else if (verbosityNormalAct->isChecked())
	{
		verbosity = VERBOSITY_NORMAL;
	}
	else if (verbosityVerboseAct->isChecked())
	{
		verbosity = VERBOSITY_VERBOSE;
	}
	else
	{
		verbosity = VERBOSITY_NORMAL;
	}

	applyVerbosity();
	m_console->setVerbose(verbosity);
}

void MainWindow::applyVerbosity()
{
	if ( verbosity == VERBOSITY_QUIET )
	{
		advancedMonitor->hideLog( inputChannel );
		advancedMonitor->hideLog( outputChannel );
		advancedMonitor->hideLog( errorChannel );
	}
	else
	{
		advancedMonitor->showLog( inputChannel );
		advancedMonitor->showLog( outputChannel );
		advancedMonitor->showLog( errorChannel );
	}

	encoderWidget->setVerbosity(verbosity);
	playerWidget->setVerbosity(verbosity);

	jobManagerDialog->setVerbosity(verbosity);
}

void MainWindow::updateVerbosity()
{
	verbosityQuietAct->setChecked( verbosity == VERBOSITY_QUIET );
	verbosityNormalAct->setChecked( verbosity == VERBOSITY_NORMAL );
	verbosityVerboseAct->setChecked( verbosity == VERBOSITY_VERBOSE );
}

void MainWindow::setCurrentDvdPath(const QString &dvdPath)
{
	currentDvd = dvdPath;

	QString fileValue(dvdPath);
	
	// Append media type
	fileValue.append(";").append( QString::number( Video::Media::DVD ));

	recentFiles.removeAll(fileValue);
	recentFiles.prepend(fileValue);
	while (recentFiles.size() > MaxRecentMovies)
		recentFiles.removeLast();

	updateRecentPathActions();
	
	qDebug() << "Set current DVD path:" << currentDvd;
	qDebug() << "Recent movies in File menu:" << recentFiles;
}

void MainWindow::setCurrentFilePath(const QString &filePath)
{
	currentFile = filePath;
	QString fileValue(filePath);
	
	// Append media type
	fileValue.append(";").append( QString::number( Video::Media::File ));

	recentFiles.removeAll(fileValue);
	recentFiles.prepend(fileValue);
	while (recentFiles.size() > MaxRecentMovies)
		recentFiles.removeLast();

	updateRecentPathActions();

	qDebug() << "Set current File path:" << currentFile;
	qDebug() << "Recent movies in File menu:" << recentFiles;
}

QString MainWindow::strippedName(const QString &fullFileName)
{
	return QFileInfo(fullFileName).fileName();
}

void MainWindow::updateEncoderInput()
{
	qDebug() << "MainWindow::updateEncoderInput() copy player input data into encoder";
	
	if (playerWidget->isLoaded())
	{
		// Copy player input data into encoder
		encoderWidget->updateInputTracks( playerWidget->currentVideo(), playerWidget->noVideoSelected(),
										playerWidget->currentAudio(), playerWidget->noAudioSelected(),
										playerWidget->currentSubtitle(), playerWidget->noSubtitleSelected(),
										playerWidget->isLoaded() && !playerWidget->isPlaying());
	}
}

void MainWindow::showEvent(QShowEvent *)
{
	if (toggleAdvancedMonitorAct->isChecked())
	{
		advancedMonitor->show();
	}
}

void MainWindow::hideEvent(QHideEvent *)
{
	if (toggleAdvancedMonitorAct->isChecked())
	{
		advancedMonitor->hide();
	}
}

QString MainWindow::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;
}

QString MainWindow::newJobId()
{
	QDateTime stamp( QDateTime::currentDateTime() );
	
	return stamp.toString("yyyyMMdd-hhmmss-zzz");
}

void MainWindow::startJob(QString *cmd, QStringList *args, 
	const QString &id, const QString &name, const QString &title,
	const QString &input, Encoder *encoder,  const int engine)
{
	encoder_settings_t::input_t *inputSettings = encoder->getInputSettings();
	encoder_target_t outputFile = encoder->getOutputFile();

	// Create output folder if necessary
	QDir dir(outputFile.dir);

	if ( !dir.exists() && !dir.mkpath(outputFile.dir) )
	{
		statusBar()->showMessage(tr("Failed to start encoding job..."));

		printLog( tr("Failed to start job %1.").arg(name), VERBOSITY_QUIET );

		InfoBoxDialog errorBox(this);
		errorBox.setTextFormat(Qt::RichText);
		errorBox.setText(tr("<u>Job %1</u>").arg(id));
		errorBox.setInformativeText(tr(	"Job <b><i>%1</i></b> as failed to start.").arg(name));
		errorBox.setDetailedText(tr("Target output directory can't be created."));
		errorBox.exec();
		
		statusBar()->showMessage(tr("Encoding job has been cancelled (output directory not valid)"), 3000);
		return;
	}

	QString output = encoder->getOutputFilePath();
	
	// Test encoder command
	QString testStdIn, testStdOut, testStdErr;
	
	inputChannel->clear();
	outputChannel->clear();
	errorChannel->clear();

	int limit(0);

	if ( engine == Video::Encoder::FFmpeg )
	{
		limit = preferences.encoderPreferences.validation_size;
	}
	else if ( engine == Video::Encoder::MEncoder )
	{
		limit = preferences.encoderPreferences.validation_frames;
	}

	if ( !encoder->isCmdEncodeValid( engine, cmd, args, &output,
		&testStdIn, &testStdOut, &testStdErr, limit ) )
	{
		statusBar()->showMessage(tr("Failed to start encoding job..."));

		printLog( tr("Failed to start job %1.").arg(name), 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("Job %1").arg(id));
		errorBox.setInformativeText(tr(	"<b>%2</b> as failed to start.").arg(name));
		errorBox.setDetailedText(description);
		errorBox.exec();
		
		statusBar()->showMessage(tr("Encoding job has been cancelled (command not valid)"), 3000);
		return;
	}

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

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

	QList<QVariant> *data = new QList<QVariant>;
	*data << QVariant(engine);
	*data << QVariant(inputSettings->video.length);
	*data << QVariant(0);  // frames to encode
	*data << QVariant(aspect_ratio);
	*data << QVariant(0);  // run_count
	*data << QVariant(output);
	*data << QVariant(encoder->getOutputMuxer());
	*data << QVariant(false);  // never test mode here
	*data << QVariant();  // slot for layout

	Task *task = new Task;
	task->setCommandLine( data, *cmd, *args, outputFile.dir, name, title); 

	// Now we can safely create and process a new job
	Job *job = new Job;
	job->setTask( task, id, name, outputFile.fileName(), 
		inputChannel, outputChannel, errorChannel, logChannel, verbosity);

	jobManager->addJob( job );

	statusBar()->showMessage(tr("Encoding job has been queued in the job manager"), 5000);
}

void MainWindow::jobEncodeVcd()
{
	statusBar()->showMessage(tr("Preparing a new encoding job..."));

	QString cmd;
	QStringList args;
	int engine;
	QString input, inputBaseName;
	QString output, outputFileName;
	encoder_settings_t::input_t *encoderInputSettings;

	// Prepare encoding job with a local encoder
	Encoder localEncoder(inputChannel, outputChannel, errorChannel, logChannel, verbosity); 
	Encoder *encoder = &localEncoder;

	encoder->initialize();

	encoder->setOutputMuxer( "mpg" ); // MPEG muxer
	encoder_settings_t::input_t inputSettings = encoderWidget->readInputSettings();

	if ( playerWidget->currentMedia() == Video::Media::DVD )
	{
		// Do job with MEncoder
		engine = Video::Encoder::MEncoder;
		encoder->activateInterface(engine, mencoderBinPath);
		encoderInputSettings = encoder->getInputSettings();
		*encoderInputSettings = inputSettings;

		inputBaseName = QString("%1 Title_%2").arg(currentTargetName)
			.arg(QString::number(encoderInputSettings->video.title));
		input = currentDvd;
	}
	else
	{
		// Do job with FFmpeg
		engine = Video::Encoder::FFmpeg;
		encoder->activateInterface(engine, ffmpegBinPath);
		encoderInputSettings = encoder->getInputSettings();
		*encoderInputSettings = inputSettings;
	
		inputBaseName = currentTargetName;
		input = currentFile;
	}
	
	outputFileName = newFileNameInFolder( currentTargetDir, inputBaseName );
	encoder->setOutputFile(currentTargetDir, outputFileName);
	output = encoder->getOutputFilePath();

	encoder->cliCmdTargetVcd(engine, &cmd, &args, input, output, encoderInputSettings->video.standard);

	// Job and task name
	QString name = tr("Encode %1 to VideoCD (VCD/MPEG-1)").arg(inputBaseName);
	if ( !encoderInputSettings->video.standard.isEmpty() ) 
	{
		name.append( " (" + encoderInputSettings->video.standard + ")" );
	}

	QString title = tr("Encoding");
	QString id = newJobId();

	startJob( &cmd, &args, id, name, title, inputBaseName, encoder, engine);
}

void MainWindow::jobEncodeSvcd()
{
	statusBar()->showMessage(tr("Preparing a new encoding job..."));

	QString cmd;
	QStringList args;
	int engine;
	QString input, inputBaseName;
	QString output, outputFileName;
	encoder_settings_t::input_t *encoderInputSettings;

	// Prepare encoding job with a local encoder
	Encoder localEncoder(inputChannel, outputChannel, errorChannel, logChannel, verbosity); 
	Encoder *encoder = &localEncoder;

	encoder->initialize();

	encoder->setOutputMuxer( "mpg" ); // MPEG muxer
	encoder_settings_t::input_t inputSettings = encoderWidget->readInputSettings();

	if ( playerWidget->currentMedia() == Video::Media::DVD )
	{
		// Do job with MEncoder
		engine = Video::Encoder::MEncoder;
		encoder->activateInterface(engine, mencoderBinPath);
		encoderInputSettings = encoder->getInputSettings();
		*encoderInputSettings = inputSettings;

		inputBaseName = QString("%1 Title_%2").arg(currentTargetName)
			.arg(QString::number(encoderInputSettings->video.title));
		input = currentDvd;
	}
	else
	{
		// Do job with FFmpeg
		engine = Video::Encoder::FFmpeg;
		encoder->activateInterface(engine, ffmpegBinPath);
		encoderInputSettings = encoder->getInputSettings();
		*encoderInputSettings = inputSettings;
	
		inputBaseName = currentTargetName;
		input = currentFile;
	}

	outputFileName = newFileNameInFolder( currentTargetDir, inputBaseName );
	encoder->setOutputFile(currentTargetDir, outputFileName);
	output = encoder->getOutputFilePath();

	encoder->cliCmdTargetSvcd(engine, &cmd, &args, input, output, encoderInputSettings->video.standard);

	// Job and task name
	QString name = tr("Encode %1 to Super-VideoCD (SVCD/MPEG-2)").arg(inputBaseName);
	if ( !encoderInputSettings->video.standard.isEmpty() ) 
	{
		name.append( " (" + encoderInputSettings->video.standard + ")" );
	}

	QString title = tr("Encoding");
	QString id = newJobId();

	startJob( &cmd, &args, id, name, title, inputBaseName, encoder, engine);
}

void MainWindow::jobEncodeXvid()
{
	statusBar()->showMessage(tr("Preparing a new encoding job..."));

	QString cmd;
	QStringList args;
	int engine;
	QString input, inputBaseName;
	QString output, outputFileName;
	encoder_settings_t::input_t *encoderInputSettings;

	// Prepare encoding job with a local encoder
	Encoder localEncoder(inputChannel, outputChannel, errorChannel, logChannel, verbosity); 
	Encoder *encoder = &localEncoder;

	encoder->initialize();

	encoder->setOutputMuxer( "avi" ); // AVI muxer
	encoder_settings_t::input_t inputSettings = encoderWidget->readInputSettings();

	// Do job with MEncoder
	engine = Video::Encoder::MEncoder;
	encoder->activateInterface(engine, mencoderBinPath);
	encoderInputSettings = encoder->getInputSettings();
	*encoderInputSettings = inputSettings;
	
	if ( playerWidget->currentMedia() == Video::Media::DVD )
	{
		inputBaseName = QString("%1 Title_%2").arg(currentTargetName)
			.arg(QString::number(encoderInputSettings->video.title));
		input = currentDvd;
	}
	else
	{
		inputBaseName = currentTargetName;
		input = currentFile;
	}

	outputFileName = newFileNameInFolder( currentTargetDir, inputBaseName );
	encoder->setOutputFile(currentTargetDir, outputFileName);
	output = encoder->getOutputFilePath();

	encoder->cliCmdTargetXvid(engine, &cmd, &args, encoderInputSettings);

	// Job and task name
	QString name = tr("Encode %1 to Xvid/DivX (MPEG-4 Part 2)").arg(inputBaseName);
	if ( !encoderInputSettings->video.standard.isEmpty() ) 
	{
		name.append( " (" + encoderInputSettings->video.standard + ")" );
	}

	QString title = tr("Encoding");
	QString id = newJobId();

	startJob( &cmd, &args, id, name, title, inputBaseName, encoder, engine);
}

void MainWindow::jobSplitMovie()
{
	statusBar()->showMessage(tr("Preparing a new split job..."));

	// Open dialog (ensure volumes > input size) to select split options:
	// - divide by 2+
	// - split in 2+ volumes of m Mbytes
	qint64 currentFileSize = QFileInfo(currentFile).size();
	QString inputFolder(QFileInfo(currentFile).canonicalPath());
	QString outputFolder(currentTargetDir);
	int volumeNumber(0);
	qint64 volumeSize(0);

	if (playerWidget->currentMedia() != Video::Media::File) 
	{
		statusBar()->showMessage(tr("Split only possible for file input"), 3000);
		return;
	}

	SplitMovieDialog dlg(currentFileSize, inputFolder, &outputFolder, &volumeNumber, &volumeSize, this);
	
	dlg.exec();
	if (dlg.result() != QDialog::Accepted) return;
		
	// Create the split jobs and start them
	MediaSplitter *splitter = new MediaSplitter(jobManager);
	splitter->setDeleteOnFinish(true);
	splitter->setOutputFolder(outputFolder);
	splitter->start(currentFile, volumeNumber, volumeSize);
}

void MainWindow::clearAllSettings()
{
	InfoBoxDialog infoBox(this);
	infoBox.setSmall();
	infoBox.setIcon(QMessageBox::Question);
	infoBox.setText(tr(	"Clear all settings and restart the application, please confirm ?"));
	infoBox.setInformativeText(tr("After restarting the application will be like after a fresh install."));

	infoBox.addButton(tr("Clear settings and restart"), QMessageBox::AcceptRole);
	infoBox.addButton(tr("Cancel"), QMessageBox::RejectRole);

	if ( infoBox.exec() != QMessageBox::AcceptRole )
	{
		return;
	}
	
	QSettings settings;
	settings.clear();
	settings.sync();

	// Clear obsolete settings
	QSettings oldSettings("labDV", "DVDx 4.0 Open Edition");
	oldSettings.clear();
	oldSettings.sync();
	
	saveSettingsOnClose = false;
	
	// Close application and restart
	qApp->exit(RESTART_CODE);
}

void MainWindow::changeZoom()
{
	double zoom(1.0); // default 1:1

	if (zoomAutoAct->isChecked())
	{
		zoom = 0;  // so PlayerWidget must calculateAutoZoom() to resize
	}
	else if (zoomMaxAct->isChecked())
	{
		zoom = 10.0; // have to be >2.0
	}
	else if (zoom21Act->isChecked())
	{
		zoom = 2.0;
	}
	else if (zoom34Act->isChecked())
	{
		zoom = 0.75;
	}
	else if (zoom12Act->isChecked())
	{
		zoom = 0.5;
	}
	else if (zoom14Act->isChecked())
	{
		zoom = 0.25;
	}

	playerWidget->disableAutoZoom();
	playerWidget->setZoom( zoom );  // set again autoZoom if 0
}
