/* 
 * This file is part of DesktopBSD, see the README file.
 *
 * Author: Peter Hofer <ph@desktopbsd.net>
 *         (C) 2006
 *
 * Copyright: BSD, see the COPYING file included in this distribution
 *
 */


#include "frminstallationwizard.h"

#include <moddisksetup.h>
#include <moddisksel.h>
#include <modpartsel.h>
#include <moddevinfo.h>
#include <variables.h>
#include <translation.h>
#include <dialogs.h>
#include <dbsdglobal.h>
#include <dbsdiconloader.h>
#include <grubconfiguration.h>

#include <qlabel.h>
#include <qpushbutton.h>
#include <qprocess.h>
#include <qlistbox.h>
#include <qwidgetstack.h>
#include <qlayout.h>
#include <qbuttongroup.h>
#include <qprogressbar.h>
#include <qradiobutton.h>

#include <sys/param.h>
#include <sys/mount.h>
#include <stdlib.h>

FrmInstallationWizard::FrmInstallationWizard(QWidget* parent, const char* name, bool modal, WFlags fl)
	:  frmInstallationWizard(parent, name, modal, fl),
		cproc(NULL),
		currentPage(0),
		grubAvailable(GrubConfiguration::isGrubAvailable())
{
	DBSDIconLoader *il = DBSDGlobal::getIconLoader();
	setPaletteBackgroundPixmap(il->loadIcon("background"));
	pixProgressIndicator->setPixmap(il->loadIcon("progressarrow"));
	pixDesktopBSDLogo->setPixmap(il->loadIcon("logo"));
	pixMain->setPixmap(il->loadIcon("desktopbsd-bubble"));
	pixLangSelect->setPixmap(il->loadIcon("locale"));
	pixDiskSel->setPixmap(il->loadIcon("bootsector"));
	pixBootSector->setPixmap(il->loadIcon("bootsector"));
	pixCopy->setPixmap(il->loadIcon("copy"));
	pixSuccess->setPixmap(il->loadIcon("success"));
	pixFailure->setPixmap(il->loadIcon("error"));
	btnReboot->setIconSet(il->loadIcon("reload"));
	btnReboot2->setIconSet(il->loadIcon("reload"));

	setLiveSystem(false);
	
	QFile f("/usr/local/share/desktopbsd/install/languages");
	if(f.open(IO_ReadOnly)) {
		QTextStream s(&f);
		s.setEncoding(QTextStream::UnicodeUTF8);
		
		while(!s.atEnd()) {
			QStringList lang = QStringList::split(':', s.readLine());
			
			langCodes.append(lang[0]); /* ISO 639-1 code, e.g. "de" */
			lstLanguage->insertItem(lang[1]); /* Native name, e.g. "Deutsch" */
		}
		
		f.close();
	}
	
	/* No page to go back to */
	btnBack->setEnabled(false);
	
	connect(btnBack, SIGNAL(clicked()), this, SLOT(pageBack()));
	connect(btnNext, SIGNAL(clicked()), this, SLOT(pageNext()));
	connect(btnReboot, SIGNAL(clicked()), this, SLOT(reboot()));
	connect(btnReboot2, SIGNAL(clicked()), this, SLOT(reboot()));
}


FrmInstallationWizard::~FrmInstallationWizard()
{
}


void FrmInstallationWizard::setProgressIndicatorY(const QWidget *w)
{
	if(w != NULL)
	{
		QPoint pos = pixProgressIndicator->pos();
		pos.setY(w->pos().y());
		pixProgressIndicator->move(pos);
	}
}


void FrmInstallationWizard::pageMove(int mv)
{
	const QWidget *progressLabels[] = {
		lblProgressPreparationLanguage,
		lblProgressPreparationWelcome,
		lblProgressPreparationHardware,
		lblProgressPreparationInstallUpgrade,
		lblProgressPreparationInstallDestination,
		lblProgressPreparationBootloader,
		lblProgressPreparationDiskPartitioning,
		
		lblProgressInstallationCopyFiles,
		lblProgressInstallationReboot,
		
		NULL,
		NULL
	};
	
	wsPages->raiseWidget(currentPage += mv);
	setProgressIndicatorY(progressLabels[currentPage]);
	pageChanged(wsPages->visibleWidget());
}


void FrmInstallationWizard::pageBack()
{
	pageMove(-1);
}


void FrmInstallationWizard::pageNext()
{
	pageMove(+1);
}


void FrmInstallationWizard::closeEvent(QCloseEvent *ce)
{
	if(btnCancelFinish->isVisible())
	{
		if(liveSystem)
		{
			if(Dialogs::question(tr("Are you sure you want to cancel the installation?\n"
											"\n"
											"Click 'Yes' to confirm and continue using the live\n"
											"system or 'No' to continue installing DesktopBSD.")))
			{
				/* Kill installation script if running */
				if(cproc != NULL)
					cproc->kill();

				ce->accept();
			}
			else
				ce->ignore();
		}
		else
		{
			if(Dialogs::question(tr("Are you sure you want to cancel the installation?\n"
											"\n"
											"Click 'Yes' to confirm and reboot the computer or\n"
											"'No' to continue installing DesktopBSD.")))
				/* Don't clean up, everything is lost :) */
				reboot();
			else
				ce->ignore();
		}
	}
	else
		ce->accept();
}


void FrmInstallationWizard::reboot() {
	/* Don't use reboot(2) here, because reboot(1) does
	 * some valuable preparations like syncing buffers
	 * and trying to shutdown processes tidily.
	 */
	system("/sbin/reboot");
}


void FrmInstallationWizard::pageChanged(QWidget *cpage) {
	if(cpage == pgWelcome) {
		/* Don't allow going back after the language selection */
		btnBack->setEnabled(false);
	
		if(lang.isEmpty())
			setLanguage();

		erase();
	} else if(cpage == pgDevInfo) {
		btnBack->setEnabled(true);
		
		delete modDeviceInfo;
		modDeviceInfo = new modDevInfo(pgDevInfo);
		pgDevInfoLayout->addWidget(modDeviceInfo, 1, 0);
		modDeviceInfo->initializeModule();
		modDeviceInfo->show();

	} else if(cpage == pgInstallUpgrade) {
		
		lblProgressPreparationDiskPartitioning->setEnabled(true);
		lblProgressPreparationBootloader->setEnabled(true);

	} else if(cpage == pgInstallDest) {
		delete modInstallDest;

		bool install = radInstallation->isChecked();
		lblProgressPreparationDiskPartitioning->setEnabled(install);
		lblProgressPreparationBootloader->setEnabled(install);
		
		if(install)
		{
			/* Installation, disk selection */
			modInstallDest = new modDiskSel(pgInstallDest);
			lblDestinationSel->setText(tr("Select the disk on which you want to install DesktopBSD and click \"Next\"."));
		}
		else
		{
			/* Upgrade, partition selection */
			modInstallDest = new modPartSel(pgInstallDest);
			lblDestinationSel->setText(tr("Select the partition that contains your existing DesktopBSD system and click \"Next\"."));
		}
		
		pgInstallDestLayout->addWidget(modInstallDest, 1, 0);
		modInstallDest->initializeModule();
		modInstallDest->show();
	} else if(cpage == pgBootLoader) {
	
		if(radUpgrade->isChecked())
		{
			if(!modInstallDest->exitReady())
			{
				Dialogs::warnDlg(tr("Please select an installation destination first!"));
				pageBack();
			} else
				/* Skip boot loader configuration when upgrading */
				pageNext();
		}
		else
		{
			/* When going back from the disk partitioner */
			btnNext->show();
	
			if(modInstallDest != NULL)
			{
				modInstallDest->apply();
	
				delete modInstallDest;
				modInstallDest = NULL;
			}
		}
	} else if (cpage == pgDiskPart) {
		
		if(radInstallation->isChecked())
		{
			btnNext->hide();
	
			/* Only install boot loader here, boot menu needs to be installed from the script */
			QWidget *selectedLoader = grpLoader->selected();
			if(selectedLoader == radSingleLoader)
			{
				Variables::setValue("disk.bootLoader", "1");
			}
			else if(selectedLoader == radSelectLoader && !grubAvailable)
			{
				Variables::setValue("disk.bootLoader", "2");
			}
	
			delete modPartDisk;
			modPartDisk = new modDiskSetup(pgDiskPart);
			pgDiskPartLayout->addWidget(modPartDisk);
			modPartDisk->initializeModule();
			modPartDisk->show();
			connect(modPartDisk, SIGNAL(done()), SLOT(pageNext()));
		} else
			/* Skip partitioning when upgrading an existing system */
			pageNext();
			
	} else if(cpage == pgCopy) {
      btnBack->hide();
		btnNext->hide();

		delete modPartDisk;
		modPartDisk = NULL;

		struct statfs buf;
		uint count;
		bool countOk = false;
		if(countOk = !statfs("/usr", &buf))
			/* Use file count on shared /usr and add 20% since this
			 * should match output lines, not files and we also have
			 * to extract additional files from a tar archive.
			 */
			count = (uint) ((buf.f_files - buf.f_ffree) * 1.2);
			
      prgrCopy->setTotalSteps(countOk ? count : 0);
      
		startCopy();
	} else if(cpage == pgFinish) {
		btnCancelFinish->hide();
		if(!cproc->normalExit() || cproc->exitStatus()) {
			pageNext();
			lblExactError->setText(lastLine);
			delete cproc;
		}

		/* -- Do nothing -- */
	}
}


void FrmInstallationWizard::startCopy() {
	cproc = new QProcess(QString("/usr/local/share/desktopbsd/install/install.sh"));
	
	/* [1] Install or upgrade */
	cproc->addArgument(radInstallation->isChecked() ? "install" : "upgrade");
	
	/* [2] Disk to install to (set by modDiskSel) */
	cproc->addArgument(Variables::getValue("disk.activeDisk"));

	/* [3] Disk and slice to install or upgrade (set by modDiskSetup/modPartSel) */
	cproc->addArgument(Variables::getValue("disk.activeSlice"));

	/* [4] Installation language. Creates a file for the "Initial
	 * Configuration Wizard" which can then read the installation
	 * language and doesn't have to ask the user about this.
	 */
	cproc->addArgument(lang);
	
	/* [5] Boot sector: "grub" for installing GNU GRUB or "keep" to leave it untouched */
	QString bootsector = "keep";
	if(grubAvailable && radInstallation->isChecked() && radSelectLoader->isChecked())
	{
		bootsector = "grub";
	}
	cproc->addArgument(bootsector);

   cproc->setCommunication(QProcess::Stdout | QProcess::Stderr | QProcess::DupStderr);

 	connect(cproc, SIGNAL(readyReadStdout()), SLOT(copyActivity()));
	connect(cproc, SIGNAL(processExited()), SLOT(pageNext()));
	cproc->start();
}


/* Previously used to display the script's output */
void FrmInstallationWizard::copyActivity() {
	static int progress = 0;

	QString line, previous;
	while(!(line = cproc->readLineStdout()).isNull()) {
		prgrCopy->setProgress(++progress);
		previous = line;
	}

	lastLine = previous;
}



void FrmInstallationWizard::setLanguage() {
	QListBoxItem *lbi = lstLanguage->selectedItem();

	if(lbi != NULL) {
		int index = lstLanguage->index(lbi);
		
		if(index > 0) {
			/* Not English */
			lang = langCodes[index - 1];
			Translation::installTranslator(lang);
		} else
			lang = "en";
	}
}


void FrmInstallationWizard::cancel()
{
	close();
}


void FrmInstallationWizard::setLiveSystem(bool live)
{
	liveSystem = live;
	if(live)
	{
		btnFinish->show();
		btnFinish2->show();
	}
	else
	{
		btnFinish->hide();
		btnFinish2->hide();
	}
}
