/*
	actions.cpp - Actions
	Copyright (C) 2005  Konrad Twardowski <kdtonline@poczta.onet.pl>

	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 "actions.h"
#include "configuration.h"
#include "confirmation.h"
#include "extras.h"
#include "miscutils.h"
#include "mmainwindow.h"
#include "msystemtray.h"
#include "systemconfig.h"

#include <tqimage.h>
#include <tqtimer.h>

#include <dcopclient.h>
#include <kdebug.h>
#include <kiconloader.h>
#include <kimageeffect.h>
#include <tdelocale.h>
#include <tdemessagebox.h>

Action *Action::_instance = 0;

Action::~Action()
{
}

TQString Action::actionToConfigGroup(const Type action) const
{
	switch (action)
	{
		case ShutDown: return "Shut Down";
		case Reboot: return "Reboot";
		case LockScreen: return "Lock Screen";
		case Logout: return "Logout";
		default: return TQString::null;
	}
}

// TODO: 2.0: GDM support
// TODO: 2.0: force application exit (without asking for data save)
bool Action::endSession(const TDEApplication::ShutdownType type, const Type action)
{
	_totalExit = false;

	MiscUtils::passiveMessage(i18n("Please wait..."));

	// test mode
	if (_testMode)
	{
		MiscUtils::showTestMessage(getName(action));

		return true;
	}

	_totalExit = true;
	if (
		!kapp->requestShutDown(
			TDEApplication::ShutdownConfirmNo,
			type,
			TDEApplication::ShutdownModeForceNow
		)
	)
	{
		KMessageBox::error(
			0,
			i18n(
				"Could not logout properly.\n" \
				"The session manager cannot be contacted."
			)
		);
		_totalExit = false;

		return false; // error
	}

	return true; // ok
}

bool Action::exec(const Type action, const bool stopTimer)
{
	if (stopTimer)
		ks_main->cancel();

	_totalExit = false;

	Method method = Method_TDE;
	TQString command;

	if (!isEnabled(action))
		return false; // error

	// kdDebug() << "Action::exec: " << action << endl;

	switch (action)
	{
		// nothing
		case Nothing:
			return false; // error

		// shut down
		case ShutDown:
			_totalExit = true;
			MiscUtils::closeCDTray();
			getMethod(action, method, command);
			MiscUtils::runCommandBeforeAction("Shut Down");

			if (method == Method_TDE)
				return endSession(TDEApplication::ShutdownTypeHalt, action);

			break;

		// reboot
		case Reboot:
			_totalExit = true;
			MiscUtils::closeCDTray();
			getMethod(action, method, command);
			MiscUtils::runCommandBeforeAction("Reboot");

			if (method == Method_TDE)
				return endSession(TDEApplication::ShutdownTypeReboot, action);

			break;

		case LockScreen:
			return lockScreen();

		// logout
		case Logout:
			_totalExit = true;
			MiscUtils::closeCDTray();
			getMethod(action, method, command);
			MiscUtils::runCommandBeforeAction("Logout");

			if (method == Method_TDE)
				return endSession(TDEApplication::ShutdownTypeNone, action);

			break;

		// "extras" action
		case Extras:
			// test mode
			if (_testMode)
			{
				MiscUtils::showTestMessage(
					ks_extras->getActionDescription() + "\n" +
					ks_extras->fileToExecute()
				);

				return true; // ok
			}

			return ks_extras->execAction();
	}

	// test mode
	if (_testMode)
	{
		MiscUtils::showTestMessage(i18n("Command: %1").arg(command));

		return true; // ok
	}

	// run default or user command
// TODO: 2.0: add function to change /sbin/* permissions (e.g. /sbin/poweroff)
// TODO: 2.0: save session before /sbin/* execution
	if (MiscUtils::runCommand(command))
	{
		// hide window
		if ((action == LockScreen) && (MSystemTray::mode() == MSystemTray::Always))
			ks_main->hide();

		return true; // ok
	}

	_totalExit = false;

	return false; // error
}

bool Action::execConfirm(const Type action, const TQString &delay)
{
	if (
		(action != LockScreen) && // no confirmation for "Lock Session"
		!Confirmation::confirm(action, delay.isNull() ? i18n("No Delay") : delay)
	)
		return false;

	return exec(action, action != LockScreen);
}

bool Action::execCurrent()
{
	return exec(_current);
}

TQString Action::getCurrentName() const
{
	return getName(_current);
}

TQPixmap Action::getIcon(const Type action) const
{
	TQMapIterator<Type, TQPixmap> i = iconCache->find(action);

	return (i == iconCache->end()) ? SmallIcon("misc") : i.data();
}

TQString Action::getIconName(const Type action) const
{
	switch (action)
	{
		case ShutDown: return "system-log-out";
		case Reboot: return "reload";
		case LockScreen: return "system-lock-screen";
		case Logout: return "edit-undo";
		case Extras: return "bookmark";
		case Nothing:
		default:
			return "misc";
	}
}

TQString Action::getMethod(const Action::Type action, Method &method, TQString &command)
{
	TQString group = actionToConfigGroup(action);

	if (group.isNull())
	{
		// kdDebug() << "Action::getMethod: No group in config for action " << action << endl;

		return TQString::null;
	}

	TQString defaultCommand;
	switch (action)
	{
		case ShutDown:
			defaultCommand = DEFAULT_SHUT_DOWN_COMMAND;
			break;
		case Reboot:
			defaultCommand = DEFAULT_REBOOT_COMMAND;
			break;
		case LockScreen:
			defaultCommand = DEFAULT_LOCK_SCREEN_COMMAND;
			break;
		case Logout:
			defaultCommand = DEFAULT_LOGOUT_COMMAND;
			break;
		default:
			defaultCommand = TQString::null;
	}

	TDEConfig *conf = kshutdownrc->config();
	if (!conf->hasGroup(group))
	{
		method = Method_TDE;
		command = defaultCommand;
		// kdDebug() << "Action::getMethod: No group in config for action " << action << endl;

		return defaultCommand;
	}

	conf->setGroup(group);

	// read method
	method = (Method)conf->readNumEntry("Method", Method_TDE);
	if ((method < Method_TDE) || (method > Method_UserCommand))
		method = Method_TDE;

	switch (method)
	{
		case Method_TDE:
			command = conf->readEntry("Command", defaultCommand);
			break;
		case Method_DefaultCommand:
			command = defaultCommand;
			break;
		case Method_UserCommand:
			command = conf->readEntry("Command", defaultCommand);
			if (command.isEmpty())
				method = Method_TDE;
			break;
	}

	return defaultCommand;
}

void Action::setMethod(const TQString &group, const Method method, const TQString &command) const
{
	TDEConfig *conf = kshutdownrc->config();
	conf->setGroup(group);
	if (method == Method_UserCommand)
		conf->writeEntry("Command", command);
	conf->writeEntry("Method", method);
}

TQString Action::getName(const Type action) const
{
	switch (action)
	{
		case Nothing: return i18n("Nothing");
		case ShutDown: return i18n("Turn Off Computer");
		case Reboot: return i18n("Restart Computer");
		case LockScreen: return i18n("Lock Session");
		case Logout: return i18n("End Current Session");
		case Extras: return ks_extras->getActionDescription();
	}

	return i18n("Unknown");
}

bool Action::isEnabled(const Type action)
{
	Method m = Method_TDE;
	TQString c = TQString::null;
	switch (action)
	{
		case Nothing:
			return false;
		case ShutDown:
			getMethod(action, m, c);
			return
				!MiscUtils::isRestricted("action_shutdown") &&
				!((m == Method_TDE) && !SystemConfig::canShutDown());
		case Reboot:
			getMethod(action, m, c);
			return
				!MiscUtils::isRestricted("action_reboot") &&
				!((m == Method_TDE) && !SystemConfig::canShutDown());
		case LockScreen:
			return !MiscUtils::isRestricted("action_lockscreen");
		case Logout:
			return !MiscUtils::isRestricted("action_logout");
		case Extras:
			return !MiscUtils::isRestricted("action_extras");
	}

	return false;
}

void Action::totalExec()
{
	if (!execCurrent())
		KMessageBox::sorry(0, i18n("Action failed! (%1)").arg(_current));
}

Action::Action()
	: TQObject(ks_main),
	_active(false),
	_testMode(false),
	_totalExit(false),
	_current(Nothing)
{
	// NOTE: compatible with the standard KDE logout dialog
	// NOTE: sync. with panel applet
	iconCache = new TQMap<Type, TQPixmap>();
	#define KS_ADD_ICON(action) \
		iconCache->insert(action, SmallIcon(getIconName(action)));
	KS_ADD_ICON(Nothing);
	KS_ADD_ICON(ShutDown);
	KS_ADD_ICON(Reboot);
	KS_ADD_ICON(LockScreen);
	KS_ADD_ICON(Logout);
	KS_ADD_ICON(Extras);
}

bool Action::lockScreen() {
	Method method;
	TQString command;
	getMethod(LockScreen, method, command);
	MiscUtils::runCommandBeforeAction("Lock Screen");
	if (method == Method_TDE) {
		// test mode
		if (_testMode) {
			MiscUtils::showTestMessage(getName(LockScreen));

			return true; // ok
		}

		// this is a modified "void Lockout::lock()"
		// from the "Lock/Logout Applet" (lockout.cpp [3.1.4])
		TQCString kdesktop("kdesktop");
		int kshutdown_screen_number = tqt_xscreen();
		if (kshutdown_screen_number)
			kdesktop.sprintf("kdesktop-screen-%d", kshutdown_screen_number);

		if (!kapp->dcopClient()->send(kdesktop, "KScreensaverIface", "lock()", "")) {
			KMessageBox::error(0, i18n("kdesktop: DCOP call failed!"));

			return false; // error
		}

		// hide window
		if (MSystemTray::mode() == MSystemTray::Always)
			ks_main->hide();

		return true; // ok
	}
	else {
		if (_testMode) {
			MiscUtils::showTestMessage(i18n("Command: %1").arg(command));

			return true; // ok
		}
		else if (MiscUtils::runCommand(command)) {
			// hide window
			if (MSystemTray::mode() == MSystemTray::Always)
				ks_main->hide();

			return true; // ok
		}
	}

	return false; // error
}

// public slots

void Action::slotLockScreen()
{
	execConfirm(LockScreen);
}

void Action::slotLogout()
{
	execConfirm(Logout);
}

void Action::slotReboot()
{
	execConfirm(Reboot);
}

void Action::slotShutDown()
{
	execConfirm(ShutDown);
}
#include "actions.moc"
