/*****************************************************************************
 *
 *  jobmanagerwidget.cpp -- Monitor running jobs in a Widget.
 * 
 *  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 "jobmanagerwidget.h"
#include "infoboxdialog.h"
#include "macros.h"
#include "../encoder/encoder.h"
#include <QtGui>
#include <QFontMetrics>

Q_DECLARE_METATYPE( QLayout * );

JobManagerWidget::JobManagerWidget(QWidget *parent) 
	: QWidget(parent), 
	idleLabel(0), jobManager(0), noMiniConsoleGroup(false), noStateDisplay(false), noConsoleButton(false)
{
	jobManagerWidgetLayout = new QVBoxLayout(this);
	jobManagerWidgetLayout->setAlignment(Qt::AlignTop);
	
	idleLabel = new QLabel ( tr("<b>No job currently running.</b>"), this );
	jobManagerWidgetLayout->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);

	jobManagerWidgetLayout->addWidget(idleLabel);

#ifndef Q_OS_MAC
	jobManagerWidgetLayout->setContentsMargins(0,0,0,0);
	jobManagerWidgetLayout->setSpacing(0);
#endif
	setContentsMargins(0,0,0,0);
}

JobManagerWidget::~JobManagerWidget()
{
	for (int i = 0; i < miniConsoles.count(); i++)
	{
		if (miniConsoles[i]) delete( miniConsoles[i] );
	}
}

void JobManagerWidget::setJobManager(JobManager* jobManager)
{
	this->jobManager = jobManager;

	synchronizeManager();
}

void JobManagerWidget::synchronizeManager()
{
	clearConsoleGarbage();

	miniConsoles.clear();

	if ( !jobManager ) return;

	QList<Job *> monitoredJobs = jobManager->getJobs();

	for (int i = 0; i < monitoredJobs.count(); i++)
	{
		addTaskConsole( monitoredJobs[i]->getTask() );
	}
}

void JobManagerWidget::addTaskConsole(Task *task)
{
	clearConsoleGarbage();
	
	for (int i = 0; i < tasks.count(); i++)
	{
		if (tasks[i] == task) return;
	}

	QList<QVariant> *data = (QList<QVariant> *) task->getData();
	int engine = data->value(0).toInt();
	QLayout *taskLayout = data->value(8).value<QLayout *>();
	bool reverseChannelLayout = (engine == Video::Encoder::FFmpeg);
	
	// Create console
	ConsoleDialog *console = new ConsoleDialog(task->getName(), task->getTitle(), task->getCommandLine(),
		Encoder::name(engine), task->getPidNumber(), reverseChannelLayout);

	if (!console) return;

	if ( engine == Video::Encoder::FFmpeg ) 
	{
		console->setTotalTime( data->value(1).toDouble() );
		console->setTotalSize( data->value(2).toInt() / 1000 );  // in kBytes
	}
	else if ( engine == Video::Encoder::MEncoder ) 
	{
		console->setTotalTime( data->value(1).toDouble() );
		console->setTotalFrame( data->value(2).toInt() );
	}

	console->attachTask( task);
	task->attachConsole( (void *)console );

	// Create mini-console group
	QGroupBox *mcGroup;
	QVBoxLayout *mcGroupLayout;

	if (noMiniConsoleGroup || taskLayout)
	{
		mcGroup = 0;		
		mcGroupLayout = 0;
	}
	else
	{
		mcGroup = new QGroupBox();		
		mcGroupLayout = new QVBoxLayout(mcGroup);
		mcGroupLayout->setSpacing(0);

		mcGroup->setTitle( task->getName() );

		Job *job = (Job *)task->getJobRef();
		mcGroup->setToolTip( tr("Output: %1").arg(job->getOutput()) );
		
		QMargins margins = mcGroupLayout->contentsMargins();
		margins.setLeft( margins.left() * 1 );  // *2 is nicer on some platforms
		margins.setTop( margins.top() );
		margins.setRight( 0 );
		margins.setBottom( 0 );
		mcGroupLayout->setContentsMargins( margins );
		
		jobManagerWidgetLayout->addSpacing(8);
		jobManagerWidgetLayout->addWidget( mcGroup );
	}

	// Create mini console
	MiniConsoleWidget *mc = new MiniConsoleWidget(console, this);

	if (!mc) return;

	mc->setNoStateDisplay( noStateDisplay );
	mc->setNoConsoleButton( noConsoleButton );

	if ( engine == Video::Encoder::FFmpeg ) 
	{
		mc->setTotalTime( data->value(1).toDouble() );
		mc->setTotalSize( data->value(2).toInt() / 1000 );  // in kBytes
	}
	else if ( engine == Video::Encoder::MEncoder ) 
	{
		mc->setTotalTime( data->value(1).toDouble() );
		mc->setTotalFrame( data->value(2).toInt() );
	}

	if (taskLayout)
	{
		taskLayout->addWidget(mc);
	}
	else
	{
		if (mcGroupLayout)
		{
			mcGroupLayout->addWidget(mc);
			data->removeLast();
			*data << qVariantFromValue((QLayout *)mcGroupLayout);
		}
		else
		{
			jobManagerWidgetLayout->addWidget(mc);
			data->removeLast();
			*data << qVariantFromValue((QLayout *)jobManagerWidgetLayout);
		}
	}

	connect( mc, SIGNAL(requestToAbort(MiniConsoleWidget *)),
		this, SLOT(abortMiniConsole(MiniConsoleWidget *)) );
	connect( mc, SIGNAL(requestToRemove(MiniConsoleWidget *)),
		this, SLOT(removeMiniConsole(MiniConsoleWidget *)) );

	miniConsoles.append( mc );
	
	idleLabel->setVisible(false);
}

void JobManagerWidget::abortMiniConsole(MiniConsoleWidget *mc)
{
	Task *task = mc->getTask();

	// Confirm to kill encoding process
	InfoBoxDialog infoBox(this);
	infoBox.setText(tr(	"Do you really want to abort encoding process:<br>"
					"<b>%1</b> ?").arg(task->getName()));

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

	if ( infoBox.exec() == QMessageBox::AcceptRole ) 
	{
		Process *process = (Process *)task->getProcess();
		process->setAbortedByUser();

		task->killParentProcess();
	}
}

void JobManagerWidget::removeMiniConsole(MiniConsoleWidget *mc, bool confirmed)
{
	ConsoleDialog *console = mc->getConsole();

	if ( !confirmed )
	{
		// Confirm to remove job from the manager and delete console
		InfoBoxDialog infoBox(this);
		infoBox.setText(tr(	"Do you really want to remove this job from the manager:<br>"
							"<b>%1</b> ?").arg(console->getTask()->getName()));
		infoBox.setInformativeText(tr("The job will be clear from the manager "
									  "window manager but the output file won't be deleted."));

		infoBox.addButton(tr("Remove job"), QMessageBox::AcceptRole);
		infoBox.addButton(tr("Keep job"), QMessageBox::RejectRole);

		if ( infoBox.exec() != QMessageBox::AcceptRole ) return;
	}

	mc->getTask()->detachConsole();

	if ( console != 0 ) console->close();  // will be deleted by mc destructor

	if ( mc != 0 )
	{
		mc->setVisible(false);
		jobManagerWidgetLayout->removeWidget(mc);
		miniConsoles.removeAll(mc);

		// MiniConsole mc can't be deleted now (still in use while running this method)
		miniConsolesToDelete.append( mc );  // deletion will be done at next addConsole()

		if (!noMiniConsoleGroup)
		{
			QWidget *mcGroup = (QWidget *)mc->parent();

			if (mcGroup)
			{
				mcGroup->setVisible(false);
				jobManagerWidgetLayout->removeWidget(mcGroup);
			}
		}
	}

	if ( miniConsoles.isEmpty() )
	{
		idleLabel->setVisible(true);
	}

	emit guiSizeAdjustRequest();
}

void JobManagerWidget::clearConsoleGarbage()
{
	int toDelete = miniConsolesToDelete.count();
	
	for (int i = 0; i < toDelete; i++)
	{
		if (miniConsolesToDelete[i]) delete(miniConsolesToDelete[i]);
	}

	miniConsolesToDelete.clear();
}

void JobManagerWidget::stopAllJobs()
{	
	int toAbort = miniConsoles.count();
	
	for (int i = 0; i < toAbort; i++)
	{
		Task *task = miniConsoles[i]->getTask();

		if( task->isProcessing() ) 
		{
			Process *process = (Process *)task->getProcess();
			process->setAbortedByUser();

			task->killParentProcess();
		}
	}

}

void JobManagerWidget::reset()
{
	clearConsoleGarbage();

	int toDelete = miniConsoles.count();
	
	for (int i = 0; i < toDelete; i++)
	{
		if (miniConsoles[i]) delete(miniConsoles[i]);
	}

	miniConsoles.clear();

	idleLabel->setVisible(true);
}

bool JobManagerWidget::isIdle()
{
	bool idle(true);

	int toCheck = miniConsoles.count();
	
	for (int i = 0; i < toCheck; i++)
	{
		Task *task = miniConsoles[i]->getTask();

		if( !task->isFinished() ) 
		{
			idle = false;
			break;
		}
	}

	return idle;
}

void JobManagerWidget::setNoConsoleButton(bool noConsole) 
{ 
	noConsoleButton = noConsole;

	for (int i = 0; i < miniConsoles.count(); i++)
	{
		miniConsoles[i]->setNoConsoleButton( noConsole );
	}
}
