/*
    This file is part of the KDE games library
    SPDX-FileCopyrightText: 2001 Andreas Beckermann <b_mann@gmx.de>
    SPDX-FileCopyrightText: 2007 Simon Hürlimann <simon.huerlimann@huerlisi.ch>

    SPDX-License-Identifier: LGPL-2.0-only

    This class was shamelessly stolen from kdelibs/kdeui/kstdction.[cpp|h] and
    after that just edited for our needs
*/

#ifndef KSTANDARDGAMEACTION_H
#define KSTANDARDGAMEACTION_H

// own
#include <libkdegames_export.h>
// KF
#include <kwidgetsaddons_version.h>
#include <KRecentFilesAction>
#include <KToggleAction>
#include <KSelectAction>
// Qt
#include <QAction>
#include <QLoggingCategory>
// std
#include <type_traits>

Q_DECLARE_LOGGING_CATEGORY(GAMES_UI)

/**
 * Extension for KStandardAction in KDE Games
 *
 * This class is an extension to the usual KStandardAction class which provides
 * easy access to often used KDE actions.
 *
 * Using these actions helps maintaining consistency among the games.
 *
 * Games often use different menu entries than other programs, e.g. games use
 * the menu "game" instead of "file". This class provides the entries which
 * differ from the usual KStandardAction entries.
 *
 * @see <tt>KStandardAction</tt>
 *
 * @author Andreas Beckermann <b_mann@gmx.de>
 */
namespace KStandardGameAction
{
  /**
   * The standard menubar and toolbar actions.
   **/
    enum StandardGameAction {
        // Game menu
        New=1, Load, LoadRecent, Save, SaveAs, End, Pause, Highscores, Statistics,
        Print, Quit,
        // Move menu
        Repeat, Undo, Redo, Roll, EndTurn,
        // Settings menu
        Carddecks,
        ChooseGameType,
        ConfigureHighscores,
        ClearHighscores,
        ClearStatistics,
        Restart,
        Hint,
        Demo,
        Solve,
        ActionNone
    };

    /**
     * Creates an action corresponding to the
     * KStandardAction::StandardAction enum.
     */
    KDEGAMES_EXPORT QAction* create( StandardGameAction id, const QObject *recvr, const char *slot,
                            QObject* parent );

    /**
     * @internal
     */
    KDEGAMES_EXPORT QAction *_k_createInternal(StandardGameAction id, QObject *parent);

    /**
     * This overloads create() to allow using the new connect syntax.
     *
     * @note If you use @c LoadRecent as @p id, you should manually connect to the urlSelected(const QUrl &)
     * signal of the returned KRecentFilesAction instead or use KStandardGameAction::loadRecent(Receiver *, Func, QObject*).
     * If you use @c ChooseGameType as @p id, you should manually connect to the triggered(int)
     * signal of the returned KSelectAction instead or use KStandardGameAction::chooseGameType(Receiver *, Func, QObject*).
     *
     * @see create(StandardGameAction, const QObject *, const char *, QObject *)
     * @since 7.3
     */
#ifdef K_DOXYGEN
    inline QAction *create(StandardGameAction id, const QObject *recvr, Func slot, QObject* parent)
#else
    template<class Receiver, class Func>
    inline typename std::enable_if<!std::is_convertible<Func, const char*>::value, QAction>::type *create(StandardGameAction id, const Receiver *recvr, Func slot, QObject *parent)
#endif
    {
        QAction *action = _k_createInternal(id, parent);
        QObject::connect(action, &QAction::triggered, recvr, slot);
        return action;
    }

    /**
     * This will return the internal name of a given standard action.
     */
    KDEGAMES_EXPORT const char* name( StandardGameAction id );

// we have to disable the templated function for const char* as Func, since it is ambiguous otherwise
// TODO: KF6: unify const char* version and new style by removing std::enable_if
#ifdef K_DOXYGEN
#define KSTANDARDGAMEACTION_WITH_NEW_STYLE_CONNECT(name, enumValue) \
    inline QAction *name(const QObject *recvr, Func slot, QObject *parent);
#define KSTANDARDGAMETOGGLEACTION_WITH_NEW_STYLE_CONNECT(name, enumValue) \
    inline KToggleAction *name(const QObject *recvr, Func slot, QObject *parent);
#else
#define KSTANDARDGAMEACTION_WITH_NEW_STYLE_CONNECT(name, enumValue) \
    template<class Receiver, class Func> \
    inline typename std::enable_if<!std::is_convertible<Func, const char*>::value, QAction>::type *name(const Receiver *recvr, Func slot, QObject *parent) \
    { return create(enumValue, recvr, slot, parent); }
#define KSTANDARDGAMETOGGLEACTION_WITH_NEW_STYLE_CONNECT(name, enumValue) \
    template<class Receiver, class Func> \
    inline typename std::enable_if<!std::is_convertible<Func, const char*>::value, KToggleAction>::type *name(const Receiver *recvr, Func slot, QObject *parent) \
    { \
        QAction* ret = create(enumValue, recvr, slot, parent); \
        Q_ASSERT(qobject_cast<KToggleAction *>(ret)); \
        return static_cast<KToggleAction *>(ret); \
    }
#endif

    /**
     * Start a new game.
     **/
    KDEGAMES_EXPORT QAction *gameNew(const QObject *recvr, const char *slot,
                            QObject *parent );
    /**
     * Start a new game.
     * @since 7.3
     */
    KSTANDARDGAMEACTION_WITH_NEW_STYLE_CONNECT(gameNew, New)

    /**
     * Load a previously saved game.
     */
    KDEGAMES_EXPORT QAction *load(const QObject *recvr, const char *slot,
                         QObject *parent );

    /**
     * Load a previously saved game.
     * @since 7.3
     */
    KSTANDARDGAMEACTION_WITH_NEW_STYLE_CONNECT(load, Load)

    // FIXME why not to delete this and use just KStandardAction::openRecent???
    // loadRecent seems to mimic its behaviour
    /**
     * Load a recently loaded game.
     * The signature of slot is of the form slotURLSelected(const QUrl&)
     */
    KDEGAMES_EXPORT KRecentFilesAction *loadRecent(const QObject *recvr, const char *slot,
                                          QObject *parent );
    /**
     * Load a recently loaded game.
     * @since 7.3
     */
#ifdef K_DOXYGEN
    inline KRecentFilesAction *loadRecent(const QObject *recvr, Func slot, QObject *parent)
#else
    template<class Receiver, class Func>
    inline typename std::enable_if<!std::is_convertible<Func, const char*>::value, KRecentFilesAction>::type *loadRecent(const Receiver *recvr, Func slot, QObject *parent)
#endif
    {
        QAction* action = _k_createInternal(LoadRecent, parent);
        KRecentFilesAction* recentAction = qobject_cast<KRecentFilesAction*>(action);
        Q_ASSERT(recentAction);
        QObject::connect(recentAction, &KRecentFilesAction::urlSelected, recvr, slot);
        return recentAction;
    }

    /**
     * Save the current game.
     */
    KDEGAMES_EXPORT QAction *save(const QObject *recvr, const char *slot,
                         QObject *parent);

    /**
     * Save the current game.
     * @since 7.3
     */
    KSTANDARDGAMEACTION_WITH_NEW_STYLE_CONNECT(save, Save)

    /**
     * Save the current game under a different filename.
     */
    KDEGAMES_EXPORT QAction *saveAs(const QObject *recvr, const char *slot,
                           QObject *parent );

    /**
     * Save the current game under a different filename.
     * @since 7.3
     */
    KSTANDARDGAMEACTION_WITH_NEW_STYLE_CONNECT(saveAs, SaveAs)

    /**
     * Pause the game.
     **/
    KDEGAMES_EXPORT KToggleAction *pause(const QObject *recvr, const char *slot,
                                QObject *parent );

    /**
     * Pause the game.
     * @since 7.3
     */
    KSTANDARDGAMETOGGLEACTION_WITH_NEW_STYLE_CONNECT(pause, Pause)

    /**
     * Show the highscores.
     */
    KDEGAMES_EXPORT QAction *highscores(const QObject *recvr, const char *slot,
                               QObject *parent );

    /**
     * Show the highscores.
     * @since 7.3
     */
    KSTANDARDGAMEACTION_WITH_NEW_STYLE_CONNECT(highscores, Highscores)

    /**
     * Clear highscores.
     */
    KDEGAMES_EXPORT QAction *clearHighscores(const QObject *recvr, const char *slot,
                               QObject *parent );

    /**
     * Clear highscores.
     * @since 7.3
     */
    KSTANDARDGAMEACTION_WITH_NEW_STYLE_CONNECT(clearHighscores, ClearHighscores)

    /**
     * Show the statistics.
     */
    KDEGAMES_EXPORT QAction *statistics(const QObject *recvr, const char *slot,
                               QObject *parent );

    /**
     * Show the statistics.
     * @since 7.3
     */
    KSTANDARDGAMEACTION_WITH_NEW_STYLE_CONNECT(statistics, Statistics)

    /**
     * Clear statistics.
     */
    KDEGAMES_EXPORT QAction *clearStatistics(const QObject *recvr, const char *slot,
                               QObject *parent );

    /**
     * Clear statistics.
     * @since 7.3
     */
    KSTANDARDGAMEACTION_WITH_NEW_STYLE_CONNECT(clearStatistics, ClearStatistics)


    /**
     * End the current game, but do not quit the program.
     * Think of a "close" entry.
     */
    KDEGAMES_EXPORT QAction *end(const QObject *recvr, const char *slot,
                        QObject *parent );

    /**
     * End the current game, but do not quit the program.
     * Think of a "close" entry.
     * @since 7.3
     */
    KSTANDARDGAMEACTION_WITH_NEW_STYLE_CONNECT(end, End)

    /**
     * Print current game.
     * Not useful in all games.
     */
    KDEGAMES_EXPORT QAction *print(const QObject *recvr, const char *slot,
                          QObject *parent );

    /**
     * Print current game.
     * Not useful in all games.
     * @since 7.3
     */
    KSTANDARDGAMEACTION_WITH_NEW_STYLE_CONNECT(print, Print)

    /**
     * Quit the game.
     */
    KDEGAMES_EXPORT QAction *quit(const QObject *recvr, const char *slot,
                         QObject *parent );

    /**
     * Quit the game.
     * @since 7.3
     */
    KSTANDARDGAMEACTION_WITH_NEW_STYLE_CONNECT(quit, Quit)

    /**
     * Repeat the last move.
     **/
    KDEGAMES_EXPORT QAction *repeat(const QObject *recvr, const char *slot,
                           QObject *parent );

    /**
     * Repeat the last move.
     * @since 7.3
     */
    KSTANDARDGAMEACTION_WITH_NEW_STYLE_CONNECT(repeat, Repeat)

    /**
     * Undo the last move.
     **/
    KDEGAMES_EXPORT QAction *undo(const QObject *recvr, const char *slot,
                         QObject *parent );

    /**
     * Undo the last move.
     * @since 7.3
     */
    KSTANDARDGAMEACTION_WITH_NEW_STYLE_CONNECT(undo, Undo)

    /**
     * Redo the last move (which has been undone).
     **/
    KDEGAMES_EXPORT QAction *redo(const QObject *recvr, const char *slot,
                         QObject *parent );

    /**
     * Redo the last move (which has been undone).
     * @since 7.3
     */
    KSTANDARDGAMEACTION_WITH_NEW_STYLE_CONNECT(redo, Redo)

    /**
     * Roll die or dice.
     **/
    KDEGAMES_EXPORT QAction *roll(const QObject *recvr, const char *slot,
                         QObject *parent );

    /**
     * Roll die or dice.
     * @since 7.3
     */
    KSTANDARDGAMEACTION_WITH_NEW_STYLE_CONNECT(roll, Roll)

    /**
     * End the current turn (not the game).
     * Usually to let the next player start.
     **/
    KDEGAMES_EXPORT QAction *endTurn(const QObject *recvr, const char *slot,
                            QObject *parent );

    /**
     * End the current turn (not the game).
     * Usually to let the next player start.
     * @since 7.3
     */
    KSTANDARDGAMEACTION_WITH_NEW_STYLE_CONNECT(endTurn, EndTurn)

    /**
     * Display configure carddecks dialog.
     */
    KDEGAMES_EXPORT QAction *carddecks(const QObject *recvr, const char *slot,
                              QObject *parent );

    /**
     * Display configure carddecks dialog.
     * @since 7.3
     */
    KSTANDARDGAMEACTION_WITH_NEW_STYLE_CONNECT(carddecks, Carddecks)

    /**
     * Display configure highscores dialog.
     */
    KDEGAMES_EXPORT QAction *configureHighscores(const QObject *recvr, const char *slot,
                                        QObject *parent );

    /**
     * Display configure highscores dialog.
     * @since 7.3
     */
    KSTANDARDGAMEACTION_WITH_NEW_STYLE_CONNECT(configureHighscores, ConfigureHighscores)

    /**
     * Give an advice/hint.
     */
    KDEGAMES_EXPORT QAction *hint(const QObject *recvr, const char *slot,
                         QObject *parent );

    /**
     * Give an advice/hint.
     * @since 7.3
     */
    KSTANDARDGAMEACTION_WITH_NEW_STYLE_CONNECT(hint, Hint)

    /**
     * Show a demo.
     */
    KDEGAMES_EXPORT KToggleAction *demo(const QObject *recvr, const char *slot,
                               QObject *parent );

    /**
     * Show a demo.
     * @since 7.3
     */
    KSTANDARDGAMETOGGLEACTION_WITH_NEW_STYLE_CONNECT(demo, Demo)

    /**
     * Solve the game.
     */
    KDEGAMES_EXPORT QAction *solve(const QObject *recvr, const char *slot,
                          QObject *parent );

    /**
     * Solve the game.
     * @since 7.3
     */
    KSTANDARDGAMEACTION_WITH_NEW_STYLE_CONNECT(solve, Solve)

    /**
     * Choose game type.
     * The signature of slot is of the form slotGameTypeChosen(int)
     */
    KDEGAMES_EXPORT KSelectAction *chooseGameType(const QObject *recvr, const char *slot,
                                         QObject *parent );

    /**
     * Choose game type.
     * @since 7.3
     */
#ifdef K_DOXYGEN
    inline KSelectAction *chooseGameType(const QObject *recvr, Func slot, QObject *parent)
#else
    template<class Receiver, class Func>
    inline typename std::enable_if<!std::is_convertible<Func, const char*>::value, KSelectAction>::type *chooseGameType(const Receiver *recvr, Func slot, QObject *parent)
#endif
    {
        QAction* action = _k_createInternal(ChooseGameType, parent);
        KSelectAction* chooseGameTypeAction = qobject_cast<KSelectAction*>(action);
        Q_ASSERT(chooseGameTypeAction);
#if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 78, 0)
        QObject::connect(chooseGameTypeAction, &KSelectAction::indexTriggered, recvr, slot);
#else
        QObject::connect(chooseGameTypeAction, QOverload<int>::of(&KSelectAction::triggered), recvr, slot);
#endif
        return chooseGameTypeAction;
    }

    /**
     * Restart the game.
     */
    KDEGAMES_EXPORT QAction *restart(const QObject *recvr, const char *slot,
                            QObject *parent );
    /**
     * Restart the game.
     * @since 7.3
     */
    KSTANDARDGAMEACTION_WITH_NEW_STYLE_CONNECT(restart, Restart)

}

#endif
