/***************************************************************************
 *   Copyright (C) 2004 by David Sansome                                   *
 *   me@davidsansome.com                                                   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "wizard.h"

#include <qfiledialog.h>
#include <qwidgetstack.h>
#include <qpushbutton.h>
#include <qpainter.h>
#include <qheader.h>
#include <qapplication.h>
#include <qmessagebox.h>
#include <qprogressbar.h>
#include <qradiobutton.h>
#include <qtextedit.h>
#include <qlineedit.h>
#include <qpopupmenu.h>
#include <qhbox.h>
#include <qfileinfo.h>
#include <qlabel.h>
#include <qfontmetrics.h>
#include <qcheckbox.h>

#include "data.h"

#include "rcparser.h"
#include "headerlistitem.h"

ComponentListItem::ComponentListItem(struct Component c, QListView* parent)
 : QCheckListItem(parent, "", QCheckListItem::CheckBox)
{
	component = c;
	setText(0, c.niceName);
	setOn(true);
	
	if (c.gnomeOnly)
		section=2;
	else if (c.kdeOnly)
		section=3;
	else if (c.optional)
		section=1;
	else
		section=0;
}

int ComponentListItem::compare(QListViewItem* i, int col, bool ascending) const
{
	switch (i->rtti())
	{
		case 1001: // Component
		{
			ComponentListItem* item = (ComponentListItem*) i;
			if (section < item->section)
				return -1;
			if (section > item->section)
				return 1;
			return QListViewItem::compare(i, col, ascending);
		}
		break;
		
		case 1002: // Header
		{
			HeaderListItem* item = (HeaderListItem*) i;
			if (section < item->section)
				return -1;
			return 1;
		}
		break;
	}
	return 0;
}



Wizard::Wizard(QWidget *parent, const char *name)
 : WizardBase(parent, name, false, WDestructiveClose)
{
	componentInfo->setMaximumSize(32767,70);
	componentList->header()->hide();
	//progressLabel2->setMaximumSize(32767, progressLabel2->fontMetrics().height()*2);
	externalProcess = new QProcess(this);
	connect(externalProcess, SIGNAL(processExited()), SLOT(processExited()));
	connect(externalProcess, SIGNAL(readyReadStdout()), SLOT(readyReadStdout()));
	connect(externalProcess, SIGNAL(readyReadStderr()), SLOT(readyReadStderr()));
	
	logDialog = new LogDialog(this);
	logDialog->hide();
	previousButton->hide();
	
	createActionFormats();
	
	kdeDirProcess = new QProcess(this);
	connect(kdeDirProcess, SIGNAL(readyReadStdout()), SLOT(kdeDirReady()));
	connect(kdeDirProcess, SIGNAL(processExited()), SLOT(getInstalledComponents()));
	kdeDirProcess->addArgument("kde-config");
	kdeDirProcess->addArgument("--prefix");
	if (!kdeDirProcess->start())
		getInstalledComponents();
}

void Wizard::createActionFormats()
{
	// Taken from KDevelop
	actionFormats.clear();
	actionFormats.append(ActionFormat( tr("compiling"), "g++", "g\\+\\+\\S* (?:\\S* )*-c (?:\\S* )*`[^`]*`(?:[^/\\s;]*/)*([^/\\s;]+)", 1 ));
	actionFormats.append(ActionFormat( tr("compiling"), "g++", "g\\+\\+\\S* (?:\\S* )*-c (?:\\S* )*(?:[^/]*/)*([^/\\s;]*)", 1 ));
	actionFormats.append(ActionFormat( tr("compiling"), "gcc", "gcc\\S* (?:\\S* )*-c (?:\\S* )*`[^`]*`(?:[^/\\s;]*/)*([^/\\s;]+)", 1 ));
	actionFormats.append(ActionFormat( tr("compiling"), "gcc", "gcc\\S* (?:\\S* )*-c (?:\\S* )*(?:[^/]*/)*([^/\\s;]*)", 1 ));
	actionFormats.append(ActionFormat( tr("compiling"), "distcc", "distcc (?:\\S* )*-c (?:\\S* )*`[^`]*`(?:[^/\\s;]*/)*([^/\\s;]+)", 1 ));
	actionFormats.append(ActionFormat( tr("compiling"), "distcc", "distcc (?:\\S* )*-c (?:\\S* )*(?:[^/]*/)*([^/\\s;]*)", 1 ));
	actionFormats.append(ActionFormat( tr("compiling"), "unknown", "^compiling (.*)", 1 ));
	actionFormats.append(ActionFormat( tr("generating"), "moc", "/moc\\b.*\\s-o\\s([^\\s;]+)", 1 ));
	actionFormats.append(ActionFormat( tr("generating"), "uic", "/uic\\b.*\\s-o\\s([^\\s;]+)", 1 ));
	actionFormats.append(ActionFormat( tr("linking"), "libtool", "/+bin/+sh\\s.*libtool.*--mode=link\\s.*\\s-o\\s([^\\s;]+)", 1 ));
	actionFormats.append(ActionFormat( tr("linking"), "g++", "g\\+\\+\\S* (?:\\S* )*-o ([^\\s;]+)", 1 ));
	actionFormats.append(ActionFormat( tr("linking"), "gcc", "gcc\\S* (?:\\S* )*-o ([^\\s;]+)", 1 ));
	actionFormats.append(ActionFormat( tr("creating"), "", "/(?:bin/+sh\\s.*mkinstalldirs).*\\s([^\\s;]+)", 1 ));
	actionFormats.append(ActionFormat( tr("installing"), "", "/(?:usr/+bin/+install|bin/+sh\\s.*mkinstalldirs|bin/+sh\\s.*libtool.*--mode=install).*\\s\\'?([^\\s;\\']+)\\'?", 1 ));
	actionFormats.append(ActionFormat( tr("generating"), "dcopidl", "dcopidl .* > ([^\\s;]+)", 1 ));
	actionFormats.append(ActionFormat( tr("compiling"), "dcopidl2cpp", "dcopidl2cpp (?:\\S* )*([^\\s;]+)", 1 ));
}

void Wizard::kdeDirReady()
{
	while (kdeDirProcess->canReadLineStdout())
	{
		QString line = kdeDirProcess->readLineStdout();
		if (!line.isEmpty())
			kdeDir = line;
	}
}

void Wizard::getInstalledComponents()
{
	QFile uninstallScript("/tmp/arkollon-uninstall.sh");
	if (uninstallScript.exists())
		uninstallScript.remove();
	uninstallScript.open(IO_WriteOnly);
	QDataStream stream(&uninstallScript);
	stream.writeRawBytes((const char*)uninstaller_sh_data, uninstaller_sh_len);
	uninstallScript.close();
	
	installedComponentsProcess = new QProcess(this);
	connect(installedComponentsProcess, SIGNAL(readyReadStdout()), SLOT(installedComponentsReady()));
	connect(installedComponentsProcess, SIGNAL(processExited()), SLOT(setup()));
	installedComponentsProcess->addArgument("/bin/sh");
	installedComponentsProcess->addArgument("/tmp/arkollon-uninstall.sh");
	installedComponentsProcess->addArgument("--list");
	if (!installedComponentsProcess->start())
		setup();
}

void Wizard::installedComponentsReady()
{
	while (installedComponentsProcess->canReadLineStdout())
	{
		QString line = installedComponentsProcess->readLineStdout();
		if (line.isEmpty())
			continue;
			
		// See if it already exists
		if (installedComponents.find(line.lower()) != installedComponents.end())
			continue;
		
		installedComponents.append(line.lower());
		//printf("Found installed component %s\n", parser.cap(3).latin1());
	}
}

void Wizard::setup()
{
	QFile uninstallScript("/tmp/arkollon-uninstall.sh");
	if (uninstallScript.exists())
		uninstallScript.remove();
	
	if (kdeDir.isEmpty())
		kdeDir = "/usr";

	// Firstly check if there's an arkollonrc file in the current directory
	// If there is, use it.	
	dir = QDir::currentDirPath();
	if (qApp->argc() > 1)
	{
		// The directory specified on the command line overrides the current dir.
		QDir d(qApp->argv()[1]);
		dir = d.absPath();
	}
	if (QFile::exists(dir + "/arkollonrc"))
	{
		setupFromRc();
	}
	else
	{
		if (!setupFromDir())
		{
			QMessageBox::critical(NULL, "Error", "This directory does not contain any recognised buildsystem", QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton);
			reject();
			return;
		}
	}
	show();
}

void Wizard::setupFromRc()
{
	RcParser parser;
	parser.addSearchDir(dir);
	if (!parser.openFile("arkollonrc"))
	{
		QMessageBox::critical(NULL, "Error", "The \"arkollonrc\" file in this directory could not be read", QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton);
		reject();
		return;
	}
	
	parser.setSection("Arkollon");
	QString appName = parser.readString("AppName");
	if (appName.isEmpty())
	{
		QMessageBox::critical(NULL, "Error", "The \"arkollonrc\" file in this directory contains no application name!", QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton);
		reject();
		return;
	}
	setAppName(appName);
	
	QString icon32Path = parser.readString("Icon32");
	QString icon16Path = parser.readString("Icon16");
	if ((!icon32Path.isEmpty()) && (QFile::exists(dir + "/" + icon32Path)))
	{
		QPixmap icon32(dir + "/" + icon32Path);
		appIcon->setPixmap(icon32);
	}
	if ((!icon16Path.isEmpty()) && (QFile::exists(dir + "/" + icon16Path)))
	{
		QPixmap icon16(dir + "/" + icon16Path);
		setIcon(icon16);
	}
	
	buildOrder = parser.readList("BuildOrder");
	QStringList compNames = parser.readList("Components");
	QStringList::Iterator it = compNames.begin();
	while( it != compNames.end() )
	{
		parser.setSection(*it);
		
		struct Component c;
		c.name = *it;
		c.niceName = parser.readString("NiceName");
		c.subDir = parser.readString("SubDir");
		c.forceDir = sub(parser.readString("ForceDir"));
		c.optional = parser.readBool("Optional", false);
		c.kdeOnly = parser.readBool("KDEOnly", false);
		c.gnomeOnly = parser.readBool("GnomeOnly", false);
		c.description = sub(parser.readString("Description"));
		c.confOptions = parser.readString("ConfigureOptions");
		c.alreadyInstalled = false;
		
		// Load the buildtimes data
		if (QFile::exists(dir + "/" + c.subDir + "/buildtimes"))
		{
			QRegExp re("([^,]*,[^,]*),(\\d*)");
			QFile file(dir + "/" + c.subDir + "/buildtimes");
			file.open(IO_ReadOnly);
			QTextStream stream(&file);
			for (;;)
			{
				QString line = stream.readLine();
				if (line.isNull())
					break;
				if (re.search(line) == -1)
					continue;
				c.buildTimes.insert(re.cap(1), re.cap(2).toInt());
			}
		}
		
		// Add the header for this component
		ComponentListItem* item = new ComponentListItem(c, componentList);
		if (!headers.contains(item->section))
		{
			headers[item->section] = new HeaderListItem(componentList);
			((HeaderListItem*)headers[item->section])->section = item->section;
			
			switch(item->section)
			{
			case 0: headers[item->section]->setText(0, "Required components"); break;
			case 1: headers[item->section]->setText(0, "Optional components"); break;
			case 2: headers[item->section]->setText(0, "Gnome specific components"); break;
			case 3: headers[item->section]->setText(0, "KDE specific components"); break;
			}
		}
		
		// Check if it's already installed
		QStringList::Iterator it2 = installedComponents.begin();
		while( it2 != installedComponents.end() )
		{
			int dashPos = c.subDir.findRev('-');
			if (dashPos < 0)
				dashPos = c.subDir.length();
			QString version = c.subDir.left(dashPos) + ":" + c.subDir.right(c.subDir.length() - dashPos - 1);
			if (*it2 == version.lower())
			{
				item->setOn(false);
				item->component.alreadyInstalled = true;
			}
			++it2;
		}
		
		++it;
	}
	parser.setSection("Arkollon");
	exec = sub(parser.readString("Exec"));
	desktop = sub(parser.readString("Desktop"));
}

bool Wizard::setupFromDir()
{
	if ((!QFile::exists(dir + "/configure")) &&
	    (!QFile::exists(dir + "/autogen.sh")) &&
		(!QFile::exists(dir + "/auto-gen.sh")))
	{
		QDir myDir(dir + "/" + selectedComponents[currentComponent].subDir);
		if (myDir.entryList("*.pro", QDir::Files).count() <= 0)
			return false;
	}
	
	// Use the directory name as the appname
	QDir myDir2(dir);
	QString dirName = myDir2.dirName();
	setAppName(makeDirNice(dirName));
	
	buildOrder.append(dirName);
	struct Component c;
	c.name = dirName;
	c.niceName = makeDirNice(dirName);
	c.optional = false;
	c.subDir = "";
	c.gnomeOnly = false;
	c.kdeOnly = false;
	c.description = "<i>No description is associated with this component.</i>";
	c.alreadyInstalled = false;
	ComponentListItem* item = new ComponentListItem(c, componentList);
	HeaderListItem* header = new HeaderListItem(componentList);
	header->setText(0, "Required components");
	header->section = 0;
	
	QStringList::Iterator it2 = installedComponents.begin();
	while( it2 != installedComponents.end() )
	{
		int dashPos = dirName.findRev('-');
		if (dashPos < 0)
			dashPos = dirName.length();
		QString version = dirName.left(dashPos) + ":" + dirName.right(dirName.length() - dashPos - 1);
		if (*it2 == version.lower())
			item->component.alreadyInstalled = true;
		++it2;
	}
	
	return true;
}


Wizard::~Wizard()
{
}


void Wizard::componentSelected(QListViewItem* item)
{
	if (item->rtti() != 1001)
		return;
		
	ComponentListItem* i = (ComponentListItem*) item;
	QString text = "<p><b>" + item->text(0) + "</b>";
	if (i->component.alreadyInstalled)
		text += "  <i>(Already installed)</i>";
	text += "</p><p>";
	text += i->component.description;
	text += "</p>";
	
	componentInfo->setText(text);
}


QString Wizard::makeDirNice(QString name)
{
	int dashPos = name.findRev('-');
	if (dashPos < 0)
		dashPos = name.length();
		
	QString ret = name.left(dashPos);
	ret = ret.left(1).upper() + ret.right(ret.length()-1);
	
	return ret;
}


void Wizard::cancelPressed()
{
	if (externalProcess->isRunning())
		externalProcess->tryTerminate();
	
	reject();
}

void Wizard::nextPressed()
{
	int currentId = installStack->id(installStack->visibleWidget());
	if (currentId == 3)
	{
		QDir d(kdeDir);
		d.mkdir("share/apps/kdesktop/Desktop");
		if (shortcutBox->isChecked())
		{
			QFile source(dir + "/" + desktop);
			QString destDir = kdeDir + "/share/apps/kdesktop/Desktop";
			int slashPos = desktop.findRev('/');
			if (slashPos < 0)
				slashPos = 0;
			QFile dest(destDir + "/" + desktop.right(desktop.length() - slashPos));
			source.open(IO_ReadOnly);
			dest.open(IO_WriteOnly | IO_Truncate);
			QDataStream destStream(&dest);
			QByteArray data = source.readAll();
			destStream.writeRawBytes(data.data(), data.size());
			source.close();
			dest.close();
		}
		if (uninstallBox->isChecked())
		{
			QFile source(dir + "/Uninstall KlamAV.desktop");
			QFile dest(kdeDir + "/share/apps/kdesktop/Desktop/Uinstall KlamAV.desktop");
			source.open(IO_ReadOnly);
			dest.open(IO_WriteOnly | IO_Truncate);
			QDataStream destStream(&dest);
			QByteArray data = source.readAll();
			destStream.writeRawBytes(data.data(), data.size());
			source.close();
			dest.close();
		}
		accept();
		return;
	}
	if ((currentId == 2) && (exec.isEmpty()))
	{
		accept();
		return;
	}
	
	if (currentId == 1)
	{
		bool itemsSelected = false;
		
		QListViewItemIterator it( componentList );
		while ( it.current() )
		{
			if (it.current()->rtti() != 1001)
			{
				++it;
				continue;
			}
			ComponentListItem* item = (ComponentListItem*) it.current();
			if (item->isOn())
				itemsSelected = true;
			++it;
		}
		if (!itemsSelected)
		{
			QMessageBox::warning(this, "Warning", "You need to select at least one component", QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton);
			return;
		}
	}
	
	installStack->raiseWidget(++currentId);
	
	if (currentId == 2)
	{
		pleaseWaitLabel->setText("Please wait while the software is compiled and installed");
		timeRemaining->setText("Estimated time remaining: <b>Calculating...</b>");
		startProcess();
		nextButton->setEnabled(false);
		previousButton->setEnabled(false);
	}
	else if (currentId == 3)
	{
		nextButton->setText("Finish");
	}
	else
		previousButton->setEnabled(true);
}

void Wizard::previousPressed()
{
	int currentId = installStack->id(installStack->visibleWidget());
	if (currentId == 0)
		return;
		
	installStack->raiseWidget(--currentId);
	
	if (currentId == 0)
		previousButton->setEnabled(false);
	nextButton->setEnabled(true);
}


void Wizard::startProcess()
{
	selectedComponents.clear();
	
	totalBTime = 0;
	elapsedTime = 0;
	elapsedBTime = 0;
	for ( QStringList::Iterator it = buildOrder.begin(); it != buildOrder.end(); ++it )
	{
		QListViewItemIterator it2( componentList );
		while ( it2.current() )
		{
			if (it2.current()->rtti() != 1001)
			{
				++it2;
				continue;
			}
			ComponentListItem* item = (ComponentListItem*) it2.current();
			if (item->component.name == *it)
			{
				if (item->isOn())
				{
					selectedComponents.append(item->component);
					QMap<QString, uint>::iterator it3;
					for ( it3 = item->component.buildTimes.begin(); it3 != item->component.buildTimes.end(); ++it3 )
						totalBTime += it3.data();
				}
			}
			++it2;
		}
	}
	
	progressBar->setProgress(0);
	progressBar->setTotalSteps(totalBTime);
	
	currentComponent = 0;
	currentStage = None;
	currentIsQMake = false;
	
	nextStep();
}

void Wizard::nextStep()
{
	externalProcess->clearArguments();
	
	switch (currentStage)
	{
	case None:
	{
		logLine(selectedComponents[currentComponent].niceName);
		progressLabel1->setText("<b>Running autogen for " + selectedComponents[currentComponent].niceName + "</b>");
		setProgress2Text("");
		//progressLabel2->setCursorPosition(0);
		currentStage = Autogen;
		QDir myDir(dir + "/" + selectedComponents[currentComponent].subDir);
		if (myDir.entryList("*.pro", QDir::Files).count() > 0)
		{
			currentIsQMake = true;
			nextStep();
			return;
		}
		
		if (QFile::exists(dir + "/" + selectedComponents[currentComponent].subDir + "/configure"))
		{
			nextStep();
			return;
		}
		
		QString autogenName;
		if (QFile::exists(dir + "/" + selectedComponents[currentComponent].subDir + "/autogen.sh"))
			autogenName = "autogen.sh";
		else if (QFile::exists(dir + "/" + selectedComponents[currentComponent].subDir + "/auto-gen.sh"))
			autogenName = "auto-gen.sh";
		else
		{
			logLine("No configure, autogen, or qmake scripts found");
			errorOccured();
			return;
		}
		
		externalProcess->addArgument(dir + "/" + selectedComponents[currentComponent].subDir + autogenName);
		externalProcess->setWorkingDirectory(dir + "/" + selectedComponents[currentComponent].subDir);
		logLine("Running autogen...");
		externalProcess->start();
		break;
	}	
	case Autogen:
	{
		//progressBar->setProgress(progressBar->progress() + 1);
		currentStage = Configure;
		if (currentIsQMake)
		{
			progressLabel1->setText("<b>Running qmake for " + selectedComponents[currentComponent].niceName + "</b>");
			setProgress2Text("");
			//progressLabel2->setCursorPosition(0);
			externalProcess->addArgument("qmake");
			externalProcess->setWorkingDirectory(dir + "/" + selectedComponents[currentComponent].subDir);
			if (!externalProcess->start())
			{
				logLine("Error: qmake was not found.  Try installing the Qt-devel libraries.");
				errorOccured();
				return;
			}
			logLine("Running qmake...");
			break;
		}
		
		if (!selectedComponents[currentComponent].forceDir.isEmpty())
			prefix = selectedComponents[currentComponent].forceDir;
		else
			prefix = "/usr/local";
		
		externalProcess->addArgument("./configure");
		//externalProcess->addArgument("--prefix=" + prefix);
		if (!selectedComponents[currentComponent].confOptions.isEmpty())
		{
			QStringList extraArgs = QStringList::split(" ", sub(selectedComponents[currentComponent].confOptions));
			for ( QStringList::Iterator it = extraArgs.begin(); it != extraArgs.end(); ++it )
				externalProcess->addArgument(*it);
		}
		externalProcess->setWorkingDirectory(dir + "/" + selectedComponents[currentComponent].subDir);
		logLine("Running configure (" + externalProcess->arguments().join(" ") + ")...");
		progressLabel1->setText("<b>Configuring " + selectedComponents[currentComponent].niceName + "</b>");
		setProgress2Text("");
		timer.start();
		externalProcess->start();
		break;
	}
	case Configure:
	{
		updateTime("configure,");
		
		currentStage = Compile;
		
		externalProcess->addArgument("make");
		externalProcess->setWorkingDirectory(dir + "/" + selectedComponents[currentComponent].subDir);
		logLine("Running make...");
		progressLabel1->setText("<b>Compiling " + selectedComponents[currentComponent].niceName + "</b>");
		setProgress2Text("");
		timer.setHMS(0, 0, 0);
		externalProcess->start();
		break;
	}
	case Compile:
	{
		currentStage = Install;
		
		logLine("Installing...");
		progressLabel1->setText("<b>Installing " + selectedComponents[currentComponent].niceName + "</b>");
		setProgress2Text("");
		
		installedFiles.clear();
		
		externalProcess->addArgument("make");
		externalProcess->addArgument("install");
		externalProcess->setWorkingDirectory(dir + "/" + selectedComponents[currentComponent].subDir);
		externalProcess->start();
		
		break;
	}
	case Install:
	{
		currentStage = WriteUninstallInfo;
		logLine("Writing uninstall information...");
		progressLabel1->setText("<b>Writing uninstall information for " + selectedComponents[currentComponent].niceName + "</b>");
		setProgress2Text("");
		
		QFile postInstallScript("/tmp/arkollon-postinstall.sh");
		if (postInstallScript.exists())
			postInstallScript.remove();
		postInstallScript.open(IO_WriteOnly);
		QDataStream stream(&postInstallScript);
		stream.writeRawBytes((const char*)postinstall_sh_data, postinstall_sh_len);
		postInstallScript.close();
		
		QFile fileList("/tmp/arkollon-filelist");
		if (fileList.exists())
			fileList.remove();
		fileList.open(IO_WriteOnly);
		QTextStream fileStream(&fileList);
		QStringList doneFiles;
		for ( QStringList::Iterator it = installedFiles.begin(); it != installedFiles.end(); ++it )
		{
			if (doneFiles.find(*it) != doneFiles.end())
				continue;
			QFileInfo fileInfo(*it);
			if (fileInfo.isDir())
				continue;
			fileStream << (*it) << '\n';
			doneFiles.append(*it);
		}
		fileList.close();
		
		struct Component c = selectedComponents[currentComponent];
		int dashPos = c.subDir.findRev('-');
		if (dashPos < 0)
			dashPos = c.subDir.length();
		QString appname = c.subDir.left(dashPos);
		QString version = c.subDir.right(c.subDir.length() - dashPos - 1);
		
		externalProcess->addArgument("/bin/sh");
		externalProcess->addArgument("/tmp/arkollon-postinstall.sh");
		externalProcess->addArgument("--appname");
		externalProcess->addArgument(appname);
		externalProcess->addArgument("--version");
		externalProcess->addArgument(version);
		externalProcess->addArgument("--filelist");
		externalProcess->addArgument("/tmp/arkollon-filelist");
		externalProcess->start();
		break;
	}
	case WriteUninstallInfo:
	{
		currentStage = None;
		currentComponent++;
		currentIsQMake = false;
		if (currentComponent >= selectedComponents.count())
		{
			progressLabel1->setText("<b>Installation completed!</b>");
			pleaseWaitLabel->setText("Installation complete");
			timeRemaining->setText("");
			progressBar->setProgress(totalBTime);
			nextButton->setEnabled(true);
			if (exec.isEmpty())
			{
				nextButton->setText("Finish");
				setProgress2Text("");
			}
			else
			{
				setProgress2Text("Click \"next\" to continue");
			}

			return;
		}
		nextStep();
		break;
	}
	default:
		break;
	}
}

void Wizard::processExited()
{
	if (currentStage == WriteUninstallInfo)
	{
		// Remove temp files from the last stage
		QFile postInstallScript("/tmp/arkollon-postinstall.sh");
		if (postInstallScript.exists())
			postInstallScript.remove();
		
		QFile fileList("/tmp/arkollon-filelist");
		if (fileList.exists())
			fileList.remove();
	}
	if (!externalProcess->normalExit())
	{
		logLine("Process was killed");
		errorOccured();
		return;
	}
	if (externalProcess->exitStatus() != 0)
	{
		logLine("Return value " + QString::number(externalProcess->exitStatus()));
		errorOccured();
		return;
	}
	if (currentStage == Compile)
		updateTime(lastTimeLine);
	nextStep();
}

void Wizard::readyReadStderr()
{
	while (externalProcess->canReadLineStderr())
	{
		QString line = externalProcess->readLineStderr().latin1();
		logDialog->logBox->append(" * "+line);
	}
}

void Wizard::readyReadStdout()
{
	while (externalProcess->canReadLineStdout())
	{
		QString line = externalProcess->readLineStdout().latin1();
		logDialog->logBox->append(line);
		
		if (currentStage == Configure)
		{
			setProgress2Text(line);
			continue;
		}
		commandLine += line;
		if (line.right(1) == "\\")
			continue;
		commandLine = commandLine.left(commandLine.find(';'));
		for ( QValueList<ActionFormat>::Iterator it = actionFormats.begin(); it != actionFormats.end(); ++it )
		{
			if ((*it).regExp.search(commandLine) == -1)
				continue;
			setProgress2Text((*it).action + " <b>" + (*it).regExp.cap(1) + "</b> (" + (*it).tool + ")");
			if ((currentStage == Install) && ((*it).action == "installing"))
				installedFiles.append((*it).regExp.cap(1));
			else
				updateTime(lastTimeLine);
			lastTimeLine = (*it).tool + "," + (*it).regExp.cap(1);
		}
		commandLine = "";
	}
}

void Wizard::updateTime(QString key)
{
	if (!timer.isNull())
	{
		elapsedBTime += selectedComponents[currentComponent].buildTimes[key];
		elapsedTime += timer.elapsed();
		float ratio = (float)elapsedTime / (float)elapsedBTime;
		int remainingTime = (int)((float)(totalBTime - elapsedBTime) * ratio) / 60000;
		if (remainingTime < 0)
			remainingTime = 0;
		QString text = QString::number(remainingTime + 1) + " minutes";
		if (remainingTime == 0)
			text = "1 minute";
		timeRemaining->setText("Estimated time remaining: <b>" + text + "</b>");
		progressBar->setProgress(elapsedBTime);
	}
	timer.start();
}

void Wizard::logLine(QString line)
{
	QString tmp = line;
	QStringList lines = QStringList::split("\n", tmp);
	for ( QStringList::Iterator it = lines.begin(); it != lines.end(); ++it )
	{
		if ((*it).isEmpty())
			continue;
		logDialog->logBox->append("***** "+*it);
	}
}

void Wizard::setProgress2Text(QString text)
{
	QString croppedText = text;
	int i = croppedText.length();
	
	QFont boldFont = progressLabel2->font();
	boldFont.setBold(true);
	QFontMetrics boldFontMetrics(boldFont);
	while (boldFontMetrics.width(croppedText) > progressLabel2->width())
	{
		croppedText = croppedText.left(--i);
	}
	
	progressLabel2->setText(croppedText);
}

void Wizard::errorOccured()
{
	//logFrame->show();
	pleaseWaitLabel->setText("An error occured");
	progressLabel1->setText("<b>An error occured</b>");
	setProgress2Text("See the log file for more information");
	previousButton->setEnabled(true);
}

QString Wizard::sub(QString s)
{
	QString tmp = s;
	tmp.replace(QRegExp("\\$KDEDIR"), kdeDir);
	tmp.replace(QRegExp("\\$HOMEDIR"), QDir::homeDirPath());
	tmp.replace(QRegExp("~"), QDir::homeDirPath());
	tmp.replace(QRegExp("\\$PREFIX"), "/usr/local");
	
	return tmp;
}

void Wizard::runPressed()
{
	QProcess* proc = new QProcess(this);
	proc->addArgument(exec);
	if (!proc->start())
	{
		QMessageBox::warning(this, "Warning", "The application could not be started<br>"+exec, QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton);
	}
}

void Wizard::logPressed()
{
	logDialog->show();
}

#include "wizard.moc"

