/*
* Copyright 2016 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/

#ifndef CommandSet_DEFINED
#define CommandSet_DEFINED

#include "include/core/SkString.h"
#include "include/core/SkTypes.h"
#include "include/private/base/SkTArray.h"
#include "tools/skui/InputState.h"
#include "tools/skui/Key.h"
#include "tools/skui/ModifierKey.h"

#include <functional>
#include <vector>

class SkCanvas;

namespace sk_app {
class Window;

/**
 * Helper class used by applications that want to hook keypresses to trigger events.
 *
 * An app can simply store an instance of CommandSet and then use it as follows:
 * 1) Attach to the Window at initialization time.
 * 2) Register commands to be executed for characters or keys. Each command needs a Group and a
 *    description (both just strings). Commands attached to Keys (rather than characters) also need
 *    a displayable name for the Key. Finally, a function to execute when the key or character is
 *    pressed must be supplied. The easiest option to is pass in a lambda that captures [this]
 *    (your application object), and performs whatever action is desired.
 * 3) Register key and char handlers with the Window, and - depending on your state - forward those
 *    events to the CommandSet's onKey, onChar, and onSoftKey.
 * 4) At the end of your onPaint, call drawHelp, and pass in the application's canvas.

 * The CommandSet always binds 'h' to cycle through two different help screens. The first shows
 * all commands, organized by Group (with headings for each Group). The second shows all commands
 * alphabetically by key/character.
 */
class CommandSet {
public:
    CommandSet();

    void attach(Window* window);
    bool onKey(skui::Key key, skui::InputState state, skui::ModifierKey modifiers);
    bool onChar(SkUnichar, skui::ModifierKey modifiers);
    bool onSoftkey(const SkString& softkey);

    void addCommand(SkUnichar c, const char* group, const char* description,
                    std::function<void(void)> function);
    void addCommand(skui::Key k, const char* keyName, const char* group, const char* description,
                    std::function<void(void)> function);

    void drawHelp(SkCanvas* canvas);

    std::vector<SkString> getCommandsAsSoftkeys() const;

private:
    struct Command {
        enum CommandType {
            kChar_CommandType,
            kKey_CommandType,
        };

        Command(SkUnichar c, const char* group, const char* description,
                std::function<void(void)> function)
            : fType(kChar_CommandType)
            , fChar(c)
            , fKeyName(' ' == c ? SkString("Space") : SkStringPrintf("%c", c))
            , fGroup(group)
            , fDescription(description)
            , fFunction(function) {}

        Command(skui::Key k, const char* keyName, const char* group, const char* description,
                std::function<void(void)> function)
            : fType(kKey_CommandType)
            , fKey(k)
            , fKeyName(keyName)
            , fGroup(group)
            , fDescription(description)
            , fFunction(function) {}

        CommandType fType;

        // For kChar_CommandType
        SkUnichar fChar;

        // For kKey_CommandType
        skui::Key fKey;

        // Common to all command types
        SkString fKeyName;
        SkString fGroup;
        SkString fDescription;
        std::function<void(void)> fFunction;

        SkString getSoftkeyString() const {
            return SkStringPrintf("%s (%s)", fKeyName.c_str(), fDescription.c_str());
        }
    };

    static bool compareCommandKey(const Command& first, const Command& second);
    static bool compareCommandGroup(const Command& first, const Command& second);

    enum HelpMode {
        kNone_HelpMode,
        kGrouped_HelpMode,
        kAlphabetical_HelpMode,
    };

    Window*                       fWindow;
    skia_private::TArray<Command> fCommands;
    HelpMode                      fHelpMode;
};

}   // namespace sk_app

#endif
