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

// Misc. utilities

#pragma once

#include <optional>

#include <QApplication>
#include <QDir>
#include <QFormLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QMenu>
#include <QPlainTextEdit>
#include <QProcess>
#include <QTextEdit>

#ifdef KS_PURE_QT
	#define i18n(text) QApplication::translate(nullptr, (text))
#else
	#include <KLocalizedString> // for i18n
#endif // KS_PURE_QT

// HACK: keep it after QApplication include to fix
// fs_fwd.:39: Parse error at "std" in "./test"
// CREDITS: https://stackoverflow.com/a/73715143
#include <filesystem>

#define KS_S(string) (string).toLocal8Bit().constData()

inline int operator "" _px(unsigned long long int value) { return value; }

typedef std::filesystem::path UPath;

using namespace std::chrono;

class ToolTipFilter final: public QObject {
public:
	explicit ToolTipFilter(QObject *parent);
	virtual ~ToolTipFilter() = default;
protected:
	virtual bool eventFilter(QObject *object, QEvent *event) override;
};

class Cond final {
public:
	/* explicit */Cond()
		: m_value(false), m_name("<undefined>") { }
	explicit Cond(const QString &name, const bool value)
		: m_value(value), m_name(name) { }

	explicit operator bool() const { return value(); }

	[[nodiscard]] QString name() const { return m_name; }
	[[nodiscard]] bool value() const { return m_value; }
private:
	bool m_value;
	QString m_name;
};

class Utils final {
public:
	inline static const hours DAY_HOURS = 24h;
	inline static const QString HTML_START = "<html>";
	inline static const QString HTML_END = "</html>";
	inline static const QString TITLE_SEPARATOR = " — "; // Em Dash

	inline static Cond cinnamon;
	inline static Cond enlightenment;
	inline static Cond haiku;
	inline static Cond kde;
	inline static Cond linuxOS; // NOTE: using weird name because of "expected unqualified-id before numeric constant" error
	inline static Cond lxde;
	inline static Cond lxqt;
	inline static Cond mate;
	inline static Cond unixLike; // NOTE: using weird name because of "expected unqualified-id before numeric constant" error
	inline static Cond windows;
	inline static Cond xfce;

	static void addTitle(QMenu *menu, const QIcon &icon, const QString &text);
	static QAction *execMenu(QMenu *menu);
	[[nodiscard]] static QTime fromMinutes(const minutes &minutes);
	[[nodiscard]] static QTime fromSeconds(const seconds &seconds);
	static QString getAntiqueMessage();
	static QDir getAppDir();
	#ifdef Q_OS_LINUX
	static QFileInfo getAppImageInfo();
	#endif // Q_OS_LINUX
	static QString getApplicationVersionInfo();
	static UPath getAutostartDir(const bool mkdir);
	static QString getBuildTypeInfo();
	static QString getDesktopInfo() { return m_desktopInfo; }
	static QSize getLargeIconSize();
	static QString getMonospaceFontName();
	static QString getQtVersionInfo();
	static QSize getSmallIconSize();
	static QString getSystemInfo();
	static QString getUser();
	static void init();
	static bool isAntique() { return m_antique; }
	[[nodiscard]] static bool isDark(const QColor &color);
	[[nodiscard]] static bool isDark(const QWidget *widget);
	static bool isGNOME();
	static bool isGTKStyle();
	static bool isKDEFullSession();
	static bool isKDE();
	static bool isOpenbox();
	static bool isRestricted(const QString &action);
	static bool isTrinity();
	static bool isUnity();
	[[nodiscard]] static bool isWritable(const UPath &path);
	[[nodiscard]] static int kdeSessionVersion() { return m_kdeSessionVersion; }
	[[nodiscard]] static QString makeHTML(const QString &html);
	[[nodiscard]] static QString makeLink(QWidget *widget, const QString &href, const QString &text);
	[[nodiscard]] static QString makeTitle(const QString &s1, const QString &s2);
	static QFormLayout *newFormLayout(QWidget *parent = nullptr);
	static QHBoxLayout *newHeader(const QString &text, QWidget *buddy = nullptr, const int relativeSize = 2);

	static QHBoxLayout *newHBoxLayout(QWidget *parent, const QList<QWidget *> &widgetList, const int spacing, const int margin = 0_px);
	static QHBoxLayout *newHBoxLayout(const QList<QWidget *> &widgetList, const int spacing, const int margin = 0_px) {
		return newHBoxLayout(nullptr, widgetList, spacing, margin);
	}

	static QVBoxLayout *newVBoxLayout(QWidget *parent, const QList<QWidget *> &widgetList, const int spacing, const int margin = 0_px);
	static QVBoxLayout *newVBoxLayout(const QList<QWidget *> &widgetList, const int spacing, const int margin = 0_px) {
		return newVBoxLayout(nullptr, widgetList, spacing, margin);
	}

	static QTextEdit *newHTMLView();
	static QLabel *newLabel(const QString &text, QWidget *buddy);
	static QAction *newLinkAction(const QString &text, const QString &url);
	static QPlainTextEdit *newTextEdit(const QString &text, const QPlainTextEdit::LineWrapMode lineWrapMode = QPlainTextEdit::NoWrap);
	static bool openInExternalApp(const QUrl &url, const bool showErrorMessage = true);
	static bool openInExternalApp(const UPath &path, const bool showErrorMessage = true);
	static void println(const QString &text);
	[[nodiscard]] static QString read(QProcess &process, bool &ok);
	[[nodiscard]] static bool run(const QString &program, const QStringList &args, const QString &workingDirectory = QString(), const bool waitFor = false);
	[[nodiscard]] static bool runSplitCommand(const QString &programAndArgs, const QString &workingDirectory = QString(), const bool waitFor = false);
	static void setFont(QWidget *widget, const int relativeSize, const bool bold);
	static void setMargin(QLayout *layout, const int margin);
	static void showToolTips(QMenu *menu);
	[[nodiscard]] static QStringList splitPair(const QString &s, const char separator, const bool trim);
	[[nodiscard]] static QStringList splitPair(const QString &s, const QString &separator, const bool trim);
	[[nodiscard]] static std::optional<double> toDouble(const QString &s);
	[[nodiscard]] static std::optional<int> toInt(const QString &s);
	[[nodiscard]] static std::optional<qint64> toInt64(const QString &s);
	[[nodiscard]] static seconds toSeconds(const QTime &time);
	[[nodiscard]] static QString trim(QString &text, const int maxLength);
private:
	Q_DISABLE_COPY(Utils)
	explicit Utils() { }
	inline static bool m_antique = false;
	inline static int m_kdeSessionVersion = 0;
	inline static const QProcessEnvironment m_env = QProcessEnvironment::systemEnvironment();
	inline static QString m_desktopInfo;
	inline static QString m_desktopSession;
	inline static QString m_xdgCurrentDesktop;
	[[nodiscard]] static bool dsContainsIgnoreCase(const QString &de);
};
