// Aseprite
// Copyright (C) 2020-2025  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"

#include <optional>

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(WheelAction action) const;
  KeyPtr dragAction(WheelAction action) const;

  void disableShortcut(const ui::Shortcut& shortcut,
                       KeySource source,
                       KeyContext keyContext,
                       const Key* newKey);

  static KeyContext getCurrentKeyContext();

  KeyPtr findBestKeyFromMessage(
    const ui::Message* msg,
    KeyContext currentKeyContext = KeyboardShortcuts::getCurrentKeyContext(),
    std::optional<KeyType> filterByType = std::nullopt) const;

  bool getCommandFromKeyMessage(
    const ui::Message* msg,
    Command** command,
    Params* params,
    KeyContext currentKeyContext = KeyboardShortcuts::getCurrentKeyContext());

  tools::Tool* getCurrentQuicktool(const ui::Message* msg,
                                   const tools::Tool* currentTool,
                                   ui::Shortcut& pressedShortcut) const;
  KeyAction getCurrentActionModifiers(KeyContext context);
  WheelAction getWheelActionFromMouseMessage(KeyContext context, const ui::Message* msg);
  Keys getDragActionsFromMessage(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);
  static void exportShortcut(tinyxml2::XMLElement* parent,
                             const Key* key,
                             const ui::Shortcut& shortcut,
                             bool removed);

  mutable Keys m_keys;
};

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
