// SPDX-License-Identifier: GPL-2.0-or-later
// SPDX-FileCopyrightText: 2009 Konrad Twardowski

#include "lock.h"

#include "../config.h"
#include "../utils.h"

#include <QDebug>

#ifdef Q_OS_WIN32
	#ifndef WIN32_LEAN_AND_MEAN
		#define WIN32_LEAN_AND_MEAN
	#endif // WIN32_LEAN_AND_MEAN
	#include <windows.h>
#else
	#include <QScreen>
#endif // Q_OS_WIN32

// LockAction

// public

bool LockAction::onAction() {
#ifdef Q_OS_WIN32
	BOOL result = ::LockWorkStation();
	if (result == 0) {
		setLastError();

		return false;
	}

	return true;
#else
// TEST: if (true) return unsupportedAction();

// TODO: support custom command in other actions
	QString customCommand = Config::readString("KShutdown Action lock", "Custom Command", "");

	customCommand = customCommand.trimmed();
	if (!customCommand.isEmpty()) {
		qDebug() << "Using custom lock command: " << customCommand;

		if (!Utils::runSplitCommand(customCommand))
			qDebug() << "Lock command failed to start";
		else
			return true;
	}

// TODO: support org.freedesktop.DisplayManager Lock

	// HACK: This is a workaround for slow initial KDE Screen Locker start and repaint.
	// Now the screen content is hidden immediately.
	//#define KS_DEBUG_LOCK_WINDOW
	#ifdef KS_DEBUG_LOCK_WINDOW
	QLabel *blackScreen = nullptr;
	#else
	QWidget *blackScreen = nullptr;
	#endif // KS_DEBUG_LOCK_WINDOW
	if (Utils::isKDE()) {
		#ifdef KS_DEBUG_LOCK_WINDOW
		blackScreen = new QLabel(
			i18n("Locking..."),
		#else
		blackScreen = new QWidget(
		#endif // KS_DEBUG_LOCK_WINDOW
			nullptr,
			Qt::FramelessWindowHint |
			Qt::NoDropShadowWindowHint |
			Qt::WindowDoesNotAcceptFocus |
			Qt::WindowStaysOnTopHint |
			Qt::X11BypassWindowManagerHint |
			Qt::Tool
		);
		#ifdef KS_DEBUG_LOCK_WINDOW
		blackScreen->setAlignment(Qt::AlignLeft | Qt::AlignTop);
		blackScreen->setMargin(20_px);
		#endif // KS_DEBUG_LOCK_WINDOW

		// set black background color
		blackScreen->setAutoFillBackground(true);
		QPalette p;
		p.setColor(QPalette::Window, Qt::black);
		p.setColor(QPalette::WindowText, Qt::darkGray);
		blackScreen->setPalette(p);

		// set full screen size
		blackScreen->resize(QApplication::primaryScreen()->size());

		// show and force repaint
		blackScreen->show();
		QApplication::processEvents();
	}

// TODO: systemd/logind

	#ifdef QT_DBUS_LIB
	// try DBus
	QDBusInterface *dbus = getQDBusInterface();
	dbus->call("Lock");
	QDBusError error = dbus->lastError();
	#endif // QT_DBUS_LIB

	if (blackScreen != nullptr) {
		//QThread::sleep(2);
		blackScreen->deleteLater();
	}

	#ifdef QT_DBUS_LIB
	if (error.type() == QDBusError::NoError)
		return true;
	#endif // QT_DBUS_LIB

	QStringList args;

	// Unity, GNOME Shell
	
	if (Utils::isGNOME() || Utils::isUnity()) {
		args.clear();
		args << "--activate";
		if (Utils::run("gnome-screensaver-command", args))
			return true;
	}

	// try "gnome-screensaver-command" command

	if (Utils::isGNOME()) {
		args.clear();
		args << "--lock";
		if (Utils::run("gnome-screensaver-command", args))
			return true;
	}

	if (strategyManager()->run())
		return true;

	// Trinity

	if (Utils::isTrinity()) {
		args.clear();
		args << "kdesktop";
		args << "KScreensaverIface";
		args << "lock";
		if (Utils::run("dcop", args))
			return true;
	}

	// do not set "m_error" because it may block auto shutdown
	qCritical() << "Could not lock the screen";
	
	return false;
#endif // Q_OS_WIN32
}

#ifdef QT_DBUS_LIB
QDBusInterface *LockAction::getQDBusInterface() {
	if (m_qdbusInterface == nullptr) {
		m_qdbusInterface = new QDBusInterface(
			"org.freedesktop.ScreenSaver",
			"/ScreenSaver",
			"org.freedesktop.ScreenSaver"
		);
		if (!m_qdbusInterface->isValid() && Utils::isKDE()) {
			delete m_qdbusInterface;
			
			qDebug() << "LockAction::getQDBusInterface(): using org.kde.krunner";
			m_qdbusInterface = new QDBusInterface(
				"org.kde.krunner",
				"/ScreenSaver",
				"org.freedesktop.ScreenSaver"
			);
		}
		else {
			qDebug() << "LockAction::getQDBusInterface(): using org.freedesktop.ScreenSaver";
		}
	}
	
	return m_qdbusInterface;
}
#endif // QT_DBUS_LIB

// private

LockAction::LockAction() :
	Action(
		i18n("Lock"),
		"system-lock-screen", "lock"
) {
	QString longDisplayName = i18n("Lock Screen");

	setCanBookmark(true);
	setCommandLineOption({ "k", "lock" }, longDisplayName);
	setShouldStopTimer(false);

	uiAction()->setToolTip(longDisplayName);

	auto sm = strategyManager();

	// by desktop environment

	sm->program(Utils::cinnamon, "cinnamon-screensaver-command", { "--lock" });
// TODO: use D-Bus
	sm->program(Utils::enlightenment, "enlightenment_remote", { "-desktop-lock" });
	sm->program(Utils::haiku, "screen_blanker");
	sm->program(Utils::lxde, "lxlock");
	sm->program(Utils::lxqt, "lxqt-leave", { "--lockscreen" });
	sm->program(Utils::mate, "mate-screensaver-command", { "--lock" });
	sm->program(Utils::xfce, "xflock4");

	// generic fallback

	sm->program(Utils::unixLike, "xdg-screensaver", { "lock" });
	sm->program(Utils::unixLike, "xscreensaver-command", { "-lock" });

	// info

	sm->dummy(Utils::windows, "WinAPI ::LockWorkStation");
}
