/*
 * Sentinella
 * http://sentinella.sourceforge.net/
 * Copyright (c) 2009-2013 Carlos Olmedo Escobar <carlos.olmedo.e@gmail.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
 * Foun dation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include "MainWindow.h"
#include <errno.h>
#include <QFile>
#include <QDBusInterface>
#include <QDBusReply>
#include <QNetworkInterface>
#include <QSetIterator>
#include <QTreeView>
#include <kmenu.h>
#include <kaction.h>
#include <KUrl>
#include <kfiledialog.h>
#include <kstandardaction.h>
#include <kactioncollection.h>
#include <KConfig>
#include <KSharedConfig>
#include <kpassivepopup.h>
#include <kdialog.h>
#include <kaboutapplicationdialog.h>
#include <kmessagebox.h>
#include <kstandarddirs.h>
#include <ksysguard/ksysguardprocesslist.h>
#include <kiconloader.h>
#include "Conditions/CPU.h"
#include "Conditions/DateTime.h"
#include "Conditions/Memory.h"
#include "Conditions/Network.h"
#include "Conditions/ProcessDies.h"
#include "Actions/ExecuteCommand.h"
#include "Actions/KillProcess.h"
#include "Actions/PlayAlarm.h"
#include "Actions/Shutdown.h"
#include "Actions/Sleep.h"

MainWindow::MainWindow(KCmdLineArgs* cmdLineArgs, const ConditionId cmdChosenCondition, const ActionId cmdChosenAction) :
		KMainWindow(NULL, Qt::WindowContextHelpButtonHint | Qt::WindowCloseButtonHint | Qt::WindowMinimizeButtonHint), cmdLineArgs(
				cmdLineArgs) {
	condition = NULL;
	action = NULL;
	supportedShutdown = false;
	pidProcessDies = pidKillProcess = 0;
	availableSounds = NULL;
	processAPI = NULL;
	networkInterfaceAPI = NULL;

	iconLoader = KIconLoader::global();
	config = KGlobal::config();
	checkShutdownSleepStates();

	gui.setupUi(this);
	gui.pushButtonStartStop->setIcon(QIcon(iconLoader->loadIcon("arrow-right", KIconLoader::Small)));

	trayIcon.setAssociatedWidget(this);
	trayIcon.setCategory(KStatusNotifierItem::ApplicationStatus);
	trayIcon.setStatus(KStatusNotifierItem::Passive);
	trayIcon.contextMenu()->addAction(KStandardAction::aboutApp(this, SLOT(showAboutApp()), trayIcon.contextMenu()));
	trayIcon.setIconByName("sentinella");

	conditionsGroup.addButton(gui.radioButtonCPU, CPU_ID);
	conditionsGroup.addButton(gui.radioButtonMemory, MEMORY);
	conditionsGroup.addButton(gui.radioButtonNetwork, NETWORK);
	conditionsGroup.addButton(gui.radioButtonDateTime, DATETIME);
	conditionsGroup.addButton(gui.radioButtonProcessDies, PROCESSDIES);
	connect(gui.radioButtonNetwork, SIGNAL(toggled(bool)), this, SLOT(radioButtonNetworkToggled(bool)));
	connect(&conditionsGroup, SIGNAL(buttonClicked(int)), gui.stackedWidgetConditions, SLOT(setCurrentIndex(int)));
	connect(gui.pushButtonProcessDies, SIGNAL(clicked(bool)), this, SLOT(programDiesClicked()));

	if (supportedShutdown) {
		actionsGroup.addButton(gui.radioButtonShutdown, SHUTDOWN);
		actionsGroup.addButton(gui.radioButtonReboot, REBOOT);
	} else {
		gui.radioButtonShutdown->setEnabled(false);
		gui.radioButtonReboot->setEnabled(false);
	}
	if (supportedSleepStates.empty())
		gui.radioButtonSleep->setEnabled(false);
	else
		actionsGroup.addButton(gui.radioButtonSleep, SLEEP);
	actionsGroup.addButton(gui.radioButtonExecuteCommand, EXECUTECOMMAND);
	actionsGroup.addButton(gui.radioButtonPlayAlarm, PLAYALARM);
	actionsGroup.addButton(gui.radioButtonKillProcess, KILLPROCESS);
	connect(gui.radioButtonPlayAlarm, SIGNAL(toggled(bool)), this, SLOT(radioButtonPlayAlarmToggled(bool)));
	connect(&actionsGroup, SIGNAL(buttonClicked(int)), this, SLOT(actionButtonClicked(int)));
	connect(gui.pushButtonKillProcess, SIGNAL(clicked(bool)), this, SLOT(killProcessClicked()));

	connect(gui.pushButtonStartStop, SIGNAL(clicked(bool)), this, SLOT(startStopClicked()));

	initConditionWidgets(cmdChosenCondition);
	initActionWidgets(cmdChosenAction);
	if (cmdLineArgs != NULL) {
		if (cmdLineArgs->isSet("start"))
			startCondition();

		cmdLineArgs->clear(); // the given cmd args are not needed anymore
	}
}

MainWindow::~MainWindow() {
	delete availableSounds;
	delete processAPI;
	delete networkInterfaceAPI;
	delete condition;
}

void MainWindow::checkShutdownSleepStates() {
	//see http://majewsky.wordpress.com/2009/07/11/shutdown-your-machine-automatically-or-from-remote/
	QDBusInterface* interface = new QDBusInterface("org.kde.ksmserver", "/KSMServer", "org.kde.KSMServerInterface");
	QDBusReply<bool> boolReply = interface->call("canShutdown");
	if (boolReply.isValid()) {
		supportedShutdown = boolReply;
		isKDE = true;
		delete interface;
		interface = new QDBusInterface("org.kde.Solid.PowerManagement", "/org/freedesktop/PowerManagement",
				"org.freedesktop.PowerManagement");
		boolReply = interface->call("CanSuspend");
		if (boolReply.isValid() && boolReply)
			supportedSleepStates.insert(Solid::PowerManagement::SuspendState);
		boolReply = interface->call("CanHibernate");
		if (boolReply.isValid() && boolReply)
			supportedSleepStates.insert(Solid::PowerManagement::HibernateState);
	} else {
		delete interface;
		interface = new QDBusInterface("org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager",
				QDBusConnection::systemBus());
		if (interface->isValid()) {
			QDBusReply<QString> stringReply = interface->call("CanPowerOff");
			if (stringReply.isValid()) {
				supportedShutdown = stringReply.value() == "yes";
				isKDE = false;
			}
			stringReply = interface->call("CanSuspend");
			if (stringReply.isValid() && stringReply.value() == "yes")
				supportedSleepStates.insert(Solid::PowerManagement::SuspendState);

			stringReply = interface->call("CanHibernate");
			if (stringReply.isValid() && stringReply.value() == "yes")
				supportedSleepStates.insert(Solid::PowerManagement::HibernateState);
		} else {
			interface = new QDBusInterface("org.freedesktop.ConsoleKit", "/org/freedesktop/ConsoleKit/Manager",
					"org.freedesktop.ConsoleKit.Manager", QDBusConnection::systemBus());
			boolReply = interface->call("CanStop");
			if (boolReply.isValid()) {
				supportedShutdown = boolReply;
				isKDE = false;
				delete interface;

				interface = new QDBusInterface("org.freedesktop.UPower", "/org/freedesktop/UPower", "org.freedesktop.UPower",
						QDBusConnection::systemBus());
				boolReply = interface->call("SuspendAllowed");
				if (boolReply.isValid() && boolReply)
					supportedSleepStates.insert(Solid::PowerManagement::SuspendState);
				boolReply = interface->call("HibernateAllowed");
				if (boolReply.isValid() && boolReply)
					supportedSleepStates.insert(Solid::PowerManagement::HibernateState);
			}
		}
	}

	delete interface;
}

void MainWindow::initConditionWidgets(const ConditionId cmdChosenCondition) {
	ConditionId chosen;
	if (cmdChosenCondition != NO_CONDITION)
		chosen = cmdChosenCondition;
	else {
		KConfigGroup group = config->group("Conditions");
		chosen = (ConditionId) group.readEntry("LastExecuted", 0);
		if (chosen < CPU_ID || chosen > PROCESSDIES)
			chosen = CPU_ID;
	}

	conditionsGroup.button(chosen)->setChecked(true);
	gui.stackedWidgetConditions->setCurrentIndex(chosen);

	int aux;
	if (cmdLineArgs != NULL && cmdLineArgs->isSet("higher"))
		aux = 0;
	else if (cmdLineArgs != NULL && cmdLineArgs->isSet("lower"))
		aux = 1;
	else
		aux = -1;

	KConfigGroup groupCPU = config->group("ConditionCPU");
	gui.comboBoxCPU->setCurrentIndex(aux != -1 ? aux : groupCPU.readEntry("Higher", 1));
	gui.spinBoxCPU->setValue(cmdChosenCondition == CPU_ID ? cmdLineArgs->getOption("cpu").toInt() : groupCPU.readEntry("Percentage", 10));
	gui.timeEditCPU->setTime(
			QTime::fromString(
					cmdChosenCondition == CPU_ID && cmdLineArgs->isSet("during") ?
							cmdLineArgs->getOption("during") : groupCPU.readEntry("Time", "01:00"), "mm:ss"));

	KConfigGroup groupMemory = config->group("ConditionMemory");
	gui.comboBoxMemory->setCurrentIndex(aux != -1 ? aux : groupMemory.readEntry("Higher", 1));
	gui.spinBoxMemory->setValue(
			cmdChosenCondition == MEMORY ? cmdLineArgs->getOption("memory").toInt() : groupMemory.readEntry("Amount", 512));
	gui.timeEditMemory->setTime(
			QTime::fromString(
					cmdChosenCondition == MEMORY && cmdLineArgs->isSet("during") ?
							cmdLineArgs->getOption("during") : groupMemory.readEntry("Time", "01:00"), "mm:ss"));

	KConfigGroup groupNetwork = config->group("ConditionNetwork");
	gui.comboBoxNetworkDirection->setCurrentIndex(groupNetwork.readEntry("Direction", 0));
	gui.comboBoxNetworkHigher->setCurrentIndex(aux != -1 ? aux : groupNetwork.readEntry("Higher", 0));
	gui.spinBoxNetwork->setValue(
			cmdChosenCondition == NETWORK ? cmdLineArgs->getOption("network").toInt() : groupNetwork.readEntry("Amount", 10));
	gui.timeEditNetwork->setTime(
			QTime::fromString(
					cmdChosenCondition == NETWORK && cmdLineArgs->isSet("during") ?
							cmdLineArgs->getOption("during") : groupNetwork.readEntry("Time", "01:00"), "mm:ss"));

	QDateTime dateTime;
	if (cmdChosenCondition == DATETIME)
		dateTime = QDateTime::fromString(cmdLineArgs->getOption("date-time"), Qt::ISODate);
	if (cmdChosenCondition != DATETIME || !dateTime.isValid()) {
		KConfigGroup groupDateTime = config->group("ConditionDateTime");
		dateTime = QDateTime::currentDateTime().addSecs(groupDateTime.readEntry("Interval", "1800").toInt());
	}
	QTime readTime(dateTime.time().hour(), dateTime.time().minute(), 0); // Setting 0 seconds
	dateTime.setTime(readTime);
	gui.kdatetimewidgetDateTime->setDateTime(dateTime);

	if (cmdChosenCondition == PROCESSDIES) {
		struct sa_process procInfo;
		if ((procInfo.pid = cmdLineArgs->getOption("program-dies").toInt()) == 0)
			return;
		if (getProcess(&procInfo)) {
			pidProcessDies = procInfo.pid;
			gui.labelProcessDies->setText(QString::fromAscii(procInfo.filename));
		}
	}
}

void MainWindow::initActionWidgets(const ActionId cmdChosenAction) {
	ActionId chosen;
	if (cmdChosenAction != NO_ACTION)
		chosen = cmdChosenAction;
	else {
		KConfigGroup group = config->group("Actions");
		chosen = (ActionId) group.readEntry("LastExecuted", 0);
		if (chosen < SHUTDOWN || chosen > KILLPROCESS)
			chosen = SHUTDOWN;
	}
	if (!supportedShutdown && (chosen == SHUTDOWN || chosen == REBOOT))
		chosen = PLAYALARM;

	actionsGroup.button(chosen)->click();

	if (!isKDE) {
		gui.checkBoxShutdownForce->setChecked(false);
		gui.checkBoxShutdownForce->setEnabled(false);
	} else if (cmdChosenAction == SHUTDOWN || cmdChosenAction == REBOOT) {
		gui.checkBoxShutdownForce->setChecked(cmdLineArgs->getOption(cmdChosenAction == SHUTDOWN ? "shutdown" : "reboot") == "yes");
	} else {
		KConfigGroup groupShutdown = config->group("ActionShutdown");
		gui.checkBoxShutdownForce->setChecked(groupShutdown.readEntry("Force", true));
	}

	if (!supportedSleepStates.empty()) {
		gui.radioButtonSleepStandBy->setEnabled(supportedSleepStates.contains(Solid::PowerManagement::StandbyState));
		gui.radioButtonSleepSuspend->setEnabled(supportedSleepStates.contains(Solid::PowerManagement::SuspendState));
		gui.radioButtonSleepHibernate->setEnabled(supportedSleepStates.contains(Solid::PowerManagement::HibernateState));

		Solid::PowerManagement::SleepState state;
		if (cmdChosenAction == SLEEP) { // cmd arg
			QString cmdSleepArg = cmdLineArgs->getOption("sleep");
			if (cmdSleepArg == "Standby")
				state = Solid::PowerManagement::StandbyState;
			else if (cmdSleepArg == "Suspend")
				state = Solid::PowerManagement::SuspendState;
			else
				state = Solid::PowerManagement::HibernateState;
		} else { // config
			KConfigGroup groupSleep = config->group("ActionSleep");
			state = (Solid::PowerManagement::SleepState) groupSleep.readEntry("Mode", (int) Solid::PowerManagement::HibernateState);
		}

		if (supportedSleepStates.contains(state)) { // If the sleep state is supported
			switch (state) {
			case Solid::PowerManagement::StandbyState:
				gui.radioButtonSleepStandBy->setChecked(true);
				break;
			case Solid::PowerManagement::SuspendState:
				gui.radioButtonSleepSuspend->setChecked(true);
				break;
			default:
				gui.radioButtonSleepHibernate->setChecked(true);
			}
		}
	}

	if (cmdChosenAction == EXECUTECOMMAND) {
		QTextStream textStream(stdin);
		gui.texteditExecuteCommand->setPlainText(textStream.readLine());
	} else if (chosen == EXECUTECOMMAND) {
		KConfigGroup groupExecuteCommand = config->group("ActionExecuteCommand");
		gui.texteditExecuteCommand->setPlainText(groupExecuteCommand.readEntry("Command"));
	} else if (cmdChosenAction == KILLPROCESS) {
		struct sa_process procInfo;
		if ((procInfo.pid = cmdLineArgs->getOption("terminate-program").toInt()) == 0)
			return;
		if (getProcess(&procInfo)) {
			pidKillProcess = procInfo.pid;
			gui.labelKillProcess->setText(QString::fromAscii(procInfo.filename));
		}
	}
}

void MainWindow::radioButtonNetworkToggled(bool checked) {
	if (!checked)
		return;

	disconnect(gui.radioButtonNetwork, SIGNAL(toggled(bool)), this, SLOT(radioButtonNetworkToggled(bool)));

	// Populating the combobox
	QList < QNetworkInterface > interfaces = QNetworkInterface::allInterfaces();
	QList<QNetworkInterface>::ConstIterator end = interfaces.constEnd();
	for (QList<QNetworkInterface>::ConstIterator itr = interfaces.constBegin(); itr != end; ++itr) {
		if (itr->flags() & QNetworkInterface::IsUp)
			gui.comboBoxNetworkInterface->addItem(itr->name());
	}

	// Trying to find out which network interface i should choose by default
	QString interfaceName;
	KConfigGroup groupNetwork = config->group("ConditionNetwork");
	if (cmdLineArgs != NULL && cmdLineArgs->isSet("interface")) // First the cmd arguments
		interfaceName = cmdLineArgs->getOption("interface");
	else if (groupNetwork.hasKey("Interface")) // Trying with the config
		interfaceName = groupNetwork.readEntry("Interface");
	else { // Choosing the interface with more traffic
		networkInterfaceAPI = new NetworkInterface();
		struct sa_net_interface* interfaces;
		uint16_t written;
		interfaces = networkInterfaceAPI->getNetworkInterfaces(&written);
		if (networkInterfaceAPI->error) {
			showError(networkInterfaceAPI->error);
		} else {
			uint64_t traffic = 0;
			for (int i = 0; i < written; ++i) {
				if (interfaces[i].received_bytes + interfaces[i].sent_bytes > traffic) {
					interfaceName = interfaces[i].name;
					traffic = interfaces[i].received_bytes + interfaces[i].sent_bytes;
				}
			}
		}
	}
	int i = gui.comboBoxNetworkInterface->findText(interfaceName);
	gui.comboBoxNetworkInterface->setCurrentIndex(i == -1 ? 1 : i);
}

void MainWindow::radioButtonPlayAlarmToggled(bool checked) {
	if (!checked)
		return;

	disconnect(gui.radioButtonPlayAlarm, SIGNAL(toggled(bool)), this, SLOT(radioButtonPlayAlarmToggled(bool)));
	availableSounds = new QStringList(KGlobal::dirs()->findAllResources("sound", "Sentinella/*", KStandardDirs::NoDuplicates));
	availableSounds->sort();
	// Loading config for the combobox
	KConfigGroup groupPlayAlarm = config->group("ActionPlayAlarm");
	QString savedFileName = groupPlayAlarm.readEntry("File");
	int savedIndex = -1;
	// Populating the combobox
	for (int i = 0; i < availableSounds->size(); ++i) {
		if (savedIndex == -1 && savedFileName.compare(availableSounds->at(i)) == 0)
			savedIndex = i;
		QFileInfo fileInfo = QFileInfo(availableSounds->at(i));
		gui.comboBoxPlayAlarm->addItem(fileInfo.baseName());
	}

	if (savedIndex != -1)
		gui.comboBoxPlayAlarm->setCurrentIndex(savedIndex);
}

void MainWindow::chooseProcess(bool isAction) {
	KDialog* procsDialog = new KDialog(this);
	procsDialog->setCaption(i18nc("@title:window", "Choose a program"));
	procsDialog->setButtons(KDialog::Ok | KDialog::Cancel);

	KSysGuardProcessList* processList = new KSysGuardProcessList(procsDialog);
	processList->setUpdateIntervalMSecs(1500);
	processList->setKillButtonVisible(false);
	if (isAction)
		processList->setState(ProcessFilter::OwnProcesses);
	processList->treeView()->setSelectionMode(QAbstractItemView::SingleSelection);
	procsDialog->setMainWidget(processList);

	if (procsDialog->exec() == QDialog::Accepted) {
		QList<KSysGuard::Process*> selectedList = processList->selectedProcesses();
		if (!selectedList.isEmpty()) {
			KSysGuard::Process* selected = selectedList.at(0);
			int index = selected->name.indexOf(' ');
			if (isAction) {
				pidKillProcess = selected->pid;
				gui.labelKillProcess->setText(index == -1 ? selected->name : selected->name.left(index));
			} else {
				pidProcessDies = selected->pid;
				gui.labelProcessDies->setText(index == -1 ? selected->name : selected->name.left(index));
			}
		}
	}

	delete processList;
	delete procsDialog;
}

void MainWindow::programDiesClicked() {
	chooseProcess(false);
}

void MainWindow::killProcessClicked() {
	chooseProcess(true);
}

void MainWindow::startStopClicked() {
	if (condition == NULL)
		startCondition();
	else
		stopCondition();
}

void MainWindow::startCondition() {
	// validating actions
	ActionId actionCheckedId = (ActionId) actionsGroup.checkedId();
	switch (actionCheckedId) {
	case SLEEP:
		if (!gui.radioButtonSleepStandBy->isChecked() && !gui.radioButtonSleepSuspend->isChecked()
				&& !gui.radioButtonSleepHibernate->isChecked()) {
			KMessageBox::sorry(this,
					i18nc("@info The user has to choose between hibernation, standby and Suspend modes", "You must select sleep mode."));
			return;
		}
		break;
	case EXECUTECOMMAND:
		if (gui.texteditExecuteCommand->toPlainText().isEmpty()) {
			KMessageBox::sorry(this, i18n("A command must be specified."));
			return;
		}
		break;
	case KILLPROCESS:
		if (pidKillProcess == 0) {
			KMessageBox::sorry(this, i18n("You must select a program to kill."));
			return;
		}
		if (!processExists(pidKillProcess)) {
			pidKillProcess = 0;
			gui.labelKillProcess->clear();
			return;
		}
		break;
	default:
		break;
	}

	// validating conditions
	qint64 msecs = 0;
	ConditionId conditionCheckedId = (ConditionId) conditionsGroup.checkedId();
	if (conditionCheckedId == CPU_ID || conditionCheckedId == MEMORY || conditionCheckedId == NETWORK) {
		QTime time;
		switch (conditionCheckedId) {
		case CPU_ID:
			time = gui.timeEditCPU->time();
			break;
		case MEMORY:
			time = gui.timeEditMemory->time();
			break;
		default:
			time = gui.timeEditNetwork->time();
		}

		msecs = (time.minute() * 60 + time.second()) * 1000;
		if (msecs == 0) {
			KMessageBox::sorry(this,
					i18nc("@info The user has chosen a wrong time: 00:00", "The specified time must be greater than zero."));
			return;
		}
	}

	switch (conditionCheckedId) {
	case CPU_ID:
		condition = new CPU(msecs, 2000, gui.spinBoxCPU->value(), gui.comboBoxCPU->currentIndex() == 0);
		break;
	case MEMORY:
		condition = new Memory(msecs, 4000, gui.spinBoxMemory->value() * 1048576, gui.comboBoxMemory->currentIndex() == 0);
		break;
	case NETWORK:
		if (networkInterfaceAPI == NULL)
			networkInterfaceAPI = new NetworkInterface();
		condition = new Network(networkInterfaceAPI, msecs, 2000, gui.comboBoxNetworkInterface->currentText(),
				gui.spinBoxNetwork->value() * 1024, gui.comboBoxNetworkHigher->currentIndex() == 0,
				gui.comboBoxNetworkDirection->currentIndex() == 0 ? DOWNLOAD : UPLOAD);
		break;
	case DATETIME:
		msecs = QDateTime::currentDateTime().msecsTo(gui.kdatetimewidgetDateTime->dateTime());
		if (msecs <= 0) {
			KMessageBox::sorry(this, i18nc("@info", "You have selected a date in the past. Please input a future date."));
			return;
		}
		condition = new DateTime(msecs);
		break;
	default:
		if (pidProcessDies == 0) {
			KMessageBox::sorry(this, i18nc("@info", "You must select a program to monitor."));
			return;
		}
		if (!processExists(pidProcessDies)) {
			pidProcessDies = 0;
			gui.labelProcessDies->clear();
			return;
		}
		condition = new ProcessDies(processAPI, 2000, 2000, pidProcessDies);
	}

	connect(condition, SIGNAL(conditionAccomplished()), this, SLOT(conditionAccomplished()));
	connect(condition, SIGNAL(conditionFailed(int)), this, SLOT(conditionFailed(int)));
	gui.groupBoxConditions->setEnabled(false);
	gui.groupBoxActions->setEnabled(false);
	gui.pushButtonStartStop->setText(i18nc("@action:button Stop the running task", "Stop"));
	gui.pushButtonStartStop->setIcon(QIcon(iconLoader->loadIcon("process-stop", KIconLoader::Small)));
	trayIcon.setStatus(KStatusNotifierItem::Active);
	saveConfig(conditionCheckedId, actionCheckedId, msecs);
}

inline void MainWindow::saveConfig(ConditionId conditionCheckedId, ActionId actionCheckedId, qint64 msecs) {
	KConfigGroup group = config->group("Conditions");
	group.writeEntry("LastExecuted", (int) conditionCheckedId);
	KConfigGroup configGroup;
	switch (conditionCheckedId) {
	case CPU_ID:
		configGroup = config->group("ConditionCPU");
		configGroup.writeEntry("Higher", gui.comboBoxCPU->currentIndex());
		configGroup.writeEntry("Percentage", gui.spinBoxCPU->value());
		configGroup.writeEntry("Time", gui.timeEditCPU->time().toString("mm:ss"));
		break;
	case MEMORY:
		configGroup = config->group("ConditionMemory");
		configGroup.writeEntry("Higher", gui.comboBoxMemory->currentIndex());
		configGroup.writeEntry("Amount", gui.spinBoxMemory->value());
		configGroup.writeEntry("Time", gui.timeEditMemory->time().toString("mm:ss"));
		break;
	case NETWORK:
		configGroup = config->group("ConditionNetwork");
		configGroup.writeEntry("Direction", gui.comboBoxNetworkDirection->currentIndex());
		configGroup.writeEntry("Higher", gui.comboBoxNetworkHigher->currentIndex());
		configGroup.writeEntry("Amount", gui.spinBoxNetwork->value());
		configGroup.writeEntry("Interface", gui.comboBoxNetworkInterface->currentText());
		configGroup.writeEntry("Time", gui.timeEditNetwork->time().toString("mm:ss"));
		break;
	case DATETIME:
		configGroup = config->group("ConditionDateTime");
		configGroup.writeEntry("Interval", msecs / 1000);
		break;
	default:
		break;
	}

	group = config->group("Actions");
	group.writeEntry("LastExecuted", (int) actionCheckedId);
	switch (actionCheckedId) {
	case SHUTDOWN:
	case REBOOT:
		configGroup = config->group("ActionShutdown");
		configGroup.writeEntry("Force", gui.checkBoxShutdownForce->isChecked());
		break;
	case SLEEP:
		Solid::PowerManagement::SleepState state;
		configGroup = config->group("ActionSleep");
		if (gui.radioButtonSleepStandBy->isChecked())
			state = Solid::PowerManagement::StandbyState;
		else if (gui.radioButtonSleepSuspend->isChecked())
			state = Solid::PowerManagement::SuspendState;
		else
			state = Solid::PowerManagement::HibernateState;
		configGroup.writeEntry("Mode", (int) state);
		break;
	case EXECUTECOMMAND:
		configGroup = config->group("ActionExecuteCommand");
		configGroup.writeEntry("Command", gui.texteditExecuteCommand->toPlainText());
		break;
	case PLAYALARM:
		configGroup = config->group("ActionPlayAlarm");
		configGroup.writeEntry("File", availableSounds->at(gui.comboBoxPlayAlarm->currentIndex()));
		break;
	default:
		break;
	}
	config->sync();
}

void MainWindow::conditionAccomplished() {
#define PASSIVE_POPUP_TITLE i18nc("@info The name of the app in the title of a passive popup", "Sentinella")
	stopCondition();

	ActionId actionCheckedId = (ActionId) actionsGroup.checkedId();
	switch (actionCheckedId) {
	case SHUTDOWN:
	case REBOOT:
		action = new Shutdown(isKDE, gui.radioButtonShutdown->isChecked(), gui.checkBoxShutdownForce->isChecked());
		if (gui.radioButtonShutdown->isChecked())
			KPassivePopup::message(PASSIVE_POPUP_TITLE, i18nc("@info", "Condition met. Halting the system."), this);
		else
			KPassivePopup::message(PASSIVE_POPUP_TITLE, i18nc("@info", "Condition met. Rebooting the system."), this);
		break;
	case SLEEP:
		Solid::PowerManagement::SleepState state;
		if (gui.radioButtonSleepStandBy->isChecked()) {
			state = Solid::PowerManagement::StandbyState;
			KPassivePopup::message(PASSIVE_POPUP_TITLE, i18nc("@info", "Condition met. Setting the system's state to standby."), this);
		} else if (gui.radioButtonSleepSuspend->isChecked()) {
			state = Solid::PowerManagement::SuspendState;
			KPassivePopup::message(PASSIVE_POPUP_TITLE, i18nc("@info", "Condition met. Suspending the system."), this);
		} else {
			state = Solid::PowerManagement::HibernateState;
			KPassivePopup::message(PASSIVE_POPUP_TITLE, i18nc("@info", "Condition met. Hibernating the system."), this);
		}
		action = new Sleep(isKDE, state);
		break;
	case EXECUTECOMMAND:
		action = new ExecuteCommand(gui.texteditExecuteCommand->toPlainText());
		KPassivePopup::message(PASSIVE_POPUP_TITLE, i18nc("@info", "Condition met. Executing command."), this);
		break;
	case PLAYALARM:
		action = new PlayAlarm(availableSounds->at(gui.comboBoxPlayAlarm->currentIndex()));
		KPassivePopup::message(PASSIVE_POPUP_TITLE, i18nc("@info", "Condition met. Throwing alarm."), this);
		break;
	default:
		action = new KillProcess(pidKillProcess);
		KPassivePopup::message(PASSIVE_POPUP_TITLE, i18nc("@info", "Condition met. Killing the selected program."), this);
		pidKillProcess = 0;
		gui.labelKillProcess->clear();
	}
	action->execute();

	if (actionCheckedId == PLAYALARM) {
		trayIcon.setStatus(KStatusNotifierItem::NeedsAttention);
		KMessageBox::information(this, i18n("Close this message to turn off the alarm."));
		((PlayAlarm*) action)->stop();
		trayIcon.setStatus(KStatusNotifierItem::Passive);
		delete action;
		action = NULL;
	}

	ConditionId conditionCheckedId = (ConditionId) conditionsGroup.checkedId();
	switch (conditionCheckedId) {
	case PROCESSDIES:
		pidProcessDies = 0;
		gui.labelProcessDies->clear();
		break;
	default:
		break;
	}
}

void MainWindow::conditionFailed(int error) {
	showError(error);
	stopCondition();
}

void MainWindow::stopCondition() {
	condition->stop();
	condition->deleteLater();
	disconnect(condition, SIGNAL(conditionAccomplished()), this, SLOT(conditionAccomplished()));
	disconnect(condition, SIGNAL(conditionFailed(int)), this, SLOT(conditionFailed(int)));
	condition = NULL;

	gui.groupBoxConditions->setEnabled(true);
	gui.groupBoxActions->setEnabled(true);
	gui.pushButtonStartStop->setText(i18nc("@action:button Verb in imperative form", "Start"));
	gui.pushButtonStartStop->setIcon(QIcon(iconLoader->loadIcon("arrow-right", KIconLoader::Small)));
	trayIcon.setStatus(KStatusNotifierItem::Passive);
}

void MainWindow::showError(const int error) {
	switch (error) {
	case ENOSYS:
		KMessageBox::error(this, i18n("Feature not available."));
		break;
	case ENODEV:
		KMessageBox::error(this, i18n("No such device."));
		break;
	case ENOMEM:
		KMessageBox::error(this, i18n("Not enough memory."));
		break;
	case ESRCH:
		KMessageBox::error(this, i18n("No such process."));
		break;
	case EIO:
		KMessageBox::error(this, i18n("I/O error."));
		break;
	default:
		KMessageBox::error(this, i18n("An internal error occurred."));
	}
}

void MainWindow::showAboutApp() {
	KAboutApplicationDialog* aboutAppDialog = new KAboutApplicationDialog(KCmdLineArgs::aboutData(), this);
	aboutAppDialog->exec();
	delete aboutAppDialog;
}

void MainWindow::closeEvent(QCloseEvent *event) {
	if (!event->spontaneous())
		return;
	setVisible(false);
	event->ignore();
}

void MainWindow::saveProperties(KConfigGroup& config) {
	config.writeEntry("Visible", isVisible());
}

void MainWindow::readProperties(const KConfigGroup& config) {
	if (config.readEntry("Visible", true))
		show();
}

bool MainWindow::createProcess() {
	processAPI = new Process();
	if (processAPI->error != 0) {
		showError(processAPI->error);
		delete processAPI;
		processAPI = NULL;
		return false;
	}
	return true;
}

bool MainWindow::processExists(pid_t pid) {
	if (processAPI == NULL && !createProcess())
		return false;
	if (processAPI->processExists(pid))
		return true;
	showError(processAPI->error);
	return false;
}

bool MainWindow::getProcess(struct sa_process* procInfo) {
	if (processAPI == NULL && !createProcess())
		return false;
	processAPI->getProcess(procInfo);
	if (processAPI->error) {
		showError(processAPI->error);
		return false;
	}
	return true;
}

void MainWindow::actionButtonClicked(int id) {
	gui.stackedWidgetActions->setCurrentIndex(id == 0 ? 0 : id - 1);
}

#include "MainWindow.moc"
