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

#pragma once

#include "infowidget.h"
#include "utils.h"

#include <QAction>
#include <QCommandLineOption>
#include <QFormLayout>
#include <QLoggingCategory>

#include <chrono>
#include <optional>

#ifdef QT_DBUS_LIB
	#include <QDBusInterface>
	#include <QDBusReply>
#endif // QT_DBUS_LIB

class MainWindow;

class Strategy final {
	friend class StrategyManager;
public:
	explicit Strategy(const Cond &cond) :
		m_cond(cond) { }
	virtual ~Strategy();
	bool notAvailable(const QString &method);
	QString toString() const;
private:
	Cond m_cond;
	#ifdef QT_DBUS_LIB
	QDBusInterface *m_dbus = nullptr;
	QString m_dbusMethod = QString();
	#endif // QT_DBUS_LIB
	qint64 m_pid = 0;
	QString m_dummy = QString();
	QString m_program = QString();
	QStringList m_args = QStringList();
	Q_DISABLE_COPY(Strategy)
};

class StrategyManager final {
	friend class PluginManager;
public:
	explicit StrategyManager() { }
	virtual ~StrategyManager() { }
	void dummy(const Cond &cond, const QString &text);
	QList<std::shared_ptr<Strategy>> list() const { return m_list; }
	void program(const Cond &cond, const QString &program, const QStringList &args = QStringList());
	bool run();
	#ifdef QT_DBUS_LIB
	std::shared_ptr<Strategy> sessionBus(const Cond &cond, QDBusInterface *dbus, const QString &method);
	std::shared_ptr<Strategy> sessionBus(const Cond &cond, const QString &service, const QString &path, const QString &interface, const QString &method);
	#endif // QT_DBUS_LIB
	void terminate(const Cond &cond, const qint64 pid);
private:
	QList<std::shared_ptr<Strategy>> m_list;
	inline static const QLoggingCategory log = QLoggingCategory("strategy");
	Q_DISABLE_COPY(StrategyManager)
	void printInfo(const QString &pluginID);
};

class Base: public QObject {
public:
	inline static const QString TIME_DISPLAY_FORMAT      = "hh'h ':' 'mm'm'";
	inline static const QString LONG_TIME_DISPLAY_FORMAT = "hh'h ':' 'mm'm ':' 'ss's'";
	inline static const QString TIME_PARSE_FORMAT        = "h:mm";

	enum class State { Start, Stop };
	explicit Base(const QString &id);
	virtual ~Base() = default;

	bool canBookmark() const { return m_canBookmark; }

	void setCanBookmark(const bool value) { m_canBookmark = value; }

	QString error() const { return m_error; }
	virtual QString configGroup() const = 0;

	QFormLayout *getContainerForm() { return dynamic_cast<QFormLayout *>(getContainerWidget()->layout()); }
	QHBoxLayout *getContainerHBox() { return dynamic_cast<QHBoxLayout *>(getContainerWidget()->layout()); }
	QWidget *getContainerWidget();
	virtual void initContainerWidget() = 0;
	QFormLayout *makeFormLayout();
	QHBoxLayout *makeHBoxLayout();

	virtual QString getStringOption() { return QString(); }
	virtual void setStringOption([[maybe_unused]] const QString &option) { }
	QString id() const { return m_id; }
	virtual bool isEnabled() const { return true; }
	QString originalText() const { return m_originalText; }
	virtual void readConfig() { }
	virtual void setState(const State state);
	QString status() const { return m_status; }
	InfoWidget::Type statusType() const { return m_statusType; }
	void setErrorStatus(const QString &status) { setStatus(status, InfoWidget::Type::Error); }
	void setInfoStatus(const QString &status) { setStatus(status, InfoWidget::Type::Info); }
	void setWarningStatus(const QString &status) { setStatus(status, InfoWidget::Type::Warning); }
	std::shared_ptr<StrategyManager> strategyManager() { return m_strategyManager; }
	virtual void updateStatusWidget(MainWindow *mainWindow) = 0;
	virtual void writeConfig() { };
protected:
	QString m_error = QString();
	QString m_id;

	/**
	 * A text without "&" shortcut.
	 */
	QString m_originalText = QString();
	virtual void setEnabled(const bool enabled, const QString &status, const InfoWidget::Type statusType = InfoWidget::Type::Error);
#ifdef Q_OS_WIN32
	void setLastError();
#endif // Q_OS_WIN32
private:
	Q_DISABLE_COPY(Base)
	bool m_canBookmark;
	InfoWidget::Type m_statusType = InfoWidget::Type::Info;
	std::shared_ptr<StrategyManager> m_strategyManager = std::make_shared<StrategyManager>();
	QString m_status = QString();
	QWidget *m_containerWidget = nullptr;
	void setStatus(const QString &status, const InfoWidget::Type statusType);
};

class Action: public Base {
	friend class PluginManager;
public:
	explicit Action(const QString &text, const QString &iconName, const QString &id);
	void activate();
	bool authorize(QWidget *parent);
	std::optional<QCommandLineOption> commandLineOption() { return m_commandLineOption; }
	virtual QString configGroup() const final override { return "KShutdown Action " + m_id; }
	QAction *createConfirmAction(const bool alwaysShowConfirmationMessage);
	int getUIGroup() const;
	QIcon icon() const { return m_uiAction->icon(); }
	bool isCommandLineOptionSet() const;
	virtual bool isEnabled() const final override { return m_uiAction->isEnabled(); }
	virtual bool onAction() = 0;
	virtual bool onCommandLineOption() { return true; }
	bool shouldStopTimer() const { return m_shouldStopTimer; }
	void setShouldStopTimer(const bool value) { m_shouldStopTimer = value; }
	bool showConfirmationMessage();
	static bool totalExit() { return m_totalExit; }
	QAction *uiAction() const { return m_uiAction; }
	virtual void updateStatusWidget(MainWindow *mainWindow) final override;

	bool visibleInMainMenu() const { return m_visibleInMainMenu; }
	void setVisibleInMainMenu(const bool value) { m_visibleInMainMenu = value; }

	bool visibleInSystemTrayMenu() const { return m_visibleInSystemTrayMenu; }
	void setVisibleInSystemTrayMenu(const bool value) { m_visibleInSystemTrayMenu = value; }

	bool visibleInWindow() const { return m_visibleInWindow; }
	void setVisibleInWindow(const bool value) { m_visibleInWindow = value; }
protected:
	inline static bool m_totalExit = false;
	#ifdef QT_DBUS_LIB
	static QDBusInterface *getLoginInterface();
	#endif // QT_DBUS_LIB
	void setCommandLineOption(const QStringList &names, const QString &description = "");
	void setCommandLineOptionValue(const QCommandLineOption &option);
	virtual void setEnabled(const bool enabled, const QString &status, const InfoWidget::Type statusType = InfoWidget::Type::Error) override;
	bool unsupportedAction();
private:
	Q_DISABLE_COPY(Action)
	#ifdef QT_DBUS_LIB
	inline static QDBusInterface *m_loginInterface = nullptr;
	#endif // QT_DBUS_LIB
	bool m_shouldStopTimer;
	bool m_visibleInMainMenu = true;
	bool m_visibleInSystemTrayMenu = true;
	bool m_visibleInWindow = true;
	QAction *m_uiAction = nullptr;
	std::optional<QCommandLineOption> m_commandLineOption;

	void readProperties();

	// event handlers:
	void onFire();
};

class Trigger: public Base {
public:
	enum/* non-class */DisplayStatus {
		DISPLAY_STATUS_HTML = 1 << 0,
		DISPLAY_STATUS_HTML_NO_ACTION = 1 << 1,
		DISPLAY_STATUS_APP_NAME = 1 << 2
	};

	explicit Trigger(const QString &text, const QString &iconName, const QString &id);
	QIcon icon() const { return m_icon; }
	virtual bool canActivateAction() = 0;
	virtual QString configGroup() const final override { return "KShutdown Trigger " + m_id; }

	milliseconds checkInterval() const { return m_checkInterval; }
	QString createDisplayStatus(Action *action, const unsigned int options);
	void setCheckInterval(const milliseconds &value) { m_checkInterval = value; }

	QString text() const { return m_text; }
	QString toolTip() const { return m_toolTip; }
	void setToolTip(const QString &value) { m_toolTip = value; }

	virtual void updateStatusWidget(MainWindow *mainWindow) final override;
private:
	Q_DISABLE_COPY(Trigger)
	milliseconds m_checkInterval = 500ms;
	QIcon m_icon;
	QString m_text;
	QString m_toolTip = "";
};

class PluginManager final {
public:
	static Action *action(const QString &id) { return m_actionMap[id]; }
	static Action *actionAlias(const QString &id);
	static QList<Action*> actionList() { return m_actionList; }
	static QHash<QString, Action*> actionMap() { return m_actionMap; }
	static void add(Action *action);
	static void add(Trigger *trigger);
	static void initActionsAndTriggers();
	static void readConfig();
	static void shutDown();
	static Trigger *trigger(const QString &id) { return m_triggerMap[id]; }
	static QList<Trigger*> triggerList() { return m_triggerList; }
	static QHash<QString, Trigger*> triggerMap() { return m_triggerMap; }
	static void writeConfig();
private:
	Q_DISABLE_COPY(PluginManager)
	explicit PluginManager() { }
	inline static QHash<QString, Action*> m_actionMap = QHash<QString, Action*>();
	inline static QHash<QString, Trigger*> m_triggerMap = QHash<QString, Trigger*>();
	inline static QList<Action*> m_actionList = QList<Action*>();
	inline static QList<Trigger*> m_triggerList = QList<Trigger*>();
};
