// Aseprite
// Copyright (C) 2020-2024  Igara Studio S.A.
// Copyright (C) 2001-2018  David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.

#ifndef APP_UI_KEYBOARD_SHORTCUTS_H_INCLUDED
#define APP_UI_KEYBOARD_SHORTCUTS_H_INCLUDED
#pragma once

#include "app/ui/key.h"
#include "obs/signal.h"

namespace tinyxml2 {
  class XMLElement;
}

namespace app {

  class KeyboardShortcuts {
  public:
    typedef Keys::iterator iterator;
    typedef Keys::const_iterator const_iterator;

    static KeyboardShortcuts* instance();
    static void destroyInstance();

    KeyboardShortcuts();
    KeyboardShortcuts(const KeyboardShortcuts&) = delete;
    KeyboardShortcuts& operator=(const KeyboardShortcuts&) = delete;
    ~KeyboardShortcuts();

    iterator begin() { return m_keys.begin(); }
    iterator end() { return m_keys.end(); }
    const_iterator begin() const { return m_keys.begin(); }
    const_iterator end() const { return m_keys.end(); }

    void setKeys(const KeyboardShortcuts& keys,
                 const bool cloneKeys);

    void clear();
    void importFile(tinyxml2::XMLElement* rootElement, KeySource source);
    void importFile(const std::string& filename, KeySource source);
    void exportFile(const std::string& filename);
    void reset();

    KeyPtr command(const char* commandName,
                   const Params& params = Params(),
                   const KeyContext keyContext = KeyContext::Any) const;
    KeyPtr tool(tools::Tool* tool) const;
    KeyPtr quicktool(tools::Tool* tool) const;
    KeyPtr action(const KeyAction action,
                  const KeyContext keyContext = KeyContext::Any) const;
    KeyPtr wheelAction(const WheelAction action) const;
    KeyPtr dragAction(const WheelAction action) const;

    void disableAccel(const ui::Accelerator& accel,
                      const KeySource source,
                      const KeyContext keyContext,
                      const Key* newKey);

    KeyContext getCurrentKeyContext() const;
    bool getCommandFromKeyMessage(const ui::Message* msg, Command** command, Params* params);
    tools::Tool* getCurrentQuicktool(tools::Tool* currentTool);
    KeyAction getCurrentActionModifiers(KeyContext context);
    WheelAction getWheelActionFromMouseMessage(const KeyContext context,
                                               const ui::Message* msg);
    Keys getDragActionsFromKeyMessage(const KeyContext context,
                                      const ui::Message* msg);
    bool hasMouseWheelCustomization() const;
    void clearMouseWheelKeys();
    void addMissingMouseWheelKeys();
    void setDefaultMouseWheelKeys(const bool zoomWithWheel);

    void addMissingKeysForCommands();

    // Generated when the keyboard shortcuts are modified by the user.
    // Useful to regenerate tooltips with shortcuts.
    obs::signal<void()> UserChange;

  private:
    void exportKeys(tinyxml2::XMLElement* parent, KeyType type);
    void exportAccel(tinyxml2::XMLElement* parent, const Key* key, const ui::Accelerator& accel, bool removed);

    mutable Keys m_keys;
  };

  std::string key_tooltip(const char* str, const Key* key);

  inline std::string key_tooltip(const char* str,
                                 const char* commandName,
                                 const Params& params = Params(),
                                 KeyContext keyContext = KeyContext::Any) {
    return key_tooltip(
      str, KeyboardShortcuts::instance()->command(
        commandName, params, keyContext).get());
  }

  inline std::string key_tooltip(const char* str,
                                 KeyAction keyAction,
                                 KeyContext keyContext = KeyContext::Any) {
    return key_tooltip(
      str, KeyboardShortcuts::instance()->action(keyAction, keyContext).get());
  }

} // namespace app

#endif
