/*
 * GDevelop C++ Platform
 * Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
 * reserved. This project is released under the MIT License.
 */
#ifndef INPUTMANAGER_H
#define INPUTMANAGER_H
#include <SFML/Window.hpp>
#include <map>
#include <set>
#include <string>
#include <vector>
#include "GDCpp/Runtime/String.h"

/**
 * \brief Manage the events and mouse, keyboard
 * and touches inputs of a sf::Window.
 *
 * In particular, each RuntimeScene owns an InputManager.
 *
 * \see RuntimeScene
 */
class GD_API InputManager {
 public:
  /**
   * @brief Default constructor.
   *
   * Call SetWindow to set the window for which events must be handled
   */
  InputManager()
      : window(nullptr),
        lastPressedKey(0),
        mouseWheelDelta(0),
        keyWasPressed(false),
        touchSimulateMouse(true),
        windowHasFocus(true),
        disableInputWhenNotFocused(true) {}

  /**
   * @brief Constructor with a window to manage.
   */
  InputManager(sf::Window* win);

  /** \name Connection with window and events management
   */
  ///@{
  /**
   * \brief Set the window managed by the input manager.
   */
  InputManager& SetWindow(sf::Window* win) {
    window = win;
    return *this;
  }

  /**
   * Set if the input must be disabled when window lose focus.
   */
  void DisableInputWhenFocusIsLost(bool disable = true) {
    disableInputWhenNotFocused = disable;
  }

  /**
   * \brief Handle a SFML event made on the window.
   */
  void HandleEvent(sf::Event& event);

  /**
   * \brief Call it when a new frame is rendered.
   */
  void NextFrame();
  ///@}

  /** \name Keyboard
   */
  ///@{
  /**
   * \brief Return the key name of the latest pressed key.
   */
  gd::String GetLastPressedKey() const;

  /**
   * \brief Return true if the specified key name is pressed.
   */
  bool IsKeyPressed(gd::String key) const;

  /**
   * \brief Return true if the specified key name was just released.
   */
  bool WasKeyReleased(gd::String key) const;

  /**
   * \brief Return true if any key was pressed since the last call
   * to NextFrame.
   */
  bool AnyKeyIsPressed() const;

  /**
   * @brief Get the unicode value of the characters entered during the last
   * frame.
   */
  std::vector<sf::Uint32> GetCharactersEntered() const {
    return charactersEntered;
  };

  static const std::map<gd::String, int>& GetKeyNameToSfKeyMap();
  static const std::map<int, gd::String>& GetSfKeyToKeyNameMap();
  ///@}

  /** \name Mouse
   */
  ///@{
  /**
   * @brief Return the position of the mouse, in window coordinates.
   */
  sf::Vector2i GetMousePosition() const;

  /**
   * @brief Return true if the specified mouse button is pressed.
   */
  bool IsMouseButtonPressed(const gd::String& button) const;

  /**
   * @brief Return true if the specified mouse button was released in this
   * frame.
   */
  bool IsMouseButtonReleased(const gd::String& button) const;

  /**
   * @brief Get the number of ticks the wheel moved during last frame.
   */
  int GetMouseWheelDelta() const;

  /**
   * @brief Return true is mouse wheel scroll up.
   */
  bool IsScrollingUp() const;

  /**
   * @brief Return true is mouse wheel scroll down.
   */
  bool IsScrollingDown() const;

  static const std::map<gd::String, int>& GetButtonNameToSfButtonMap();
  static const std::map<int, gd::String>& GetSfButtonToButtonNameMap();
  ///@}

  /** \name Touches
   */
  ///@{
  /**
   * @brief Get all touches being made on the screen, along with their
   * coordinates.
   */
  const std::map<int, sf::Vector2i>& GetAllTouches() { return touches; }
  ///@}

 private:
  sf::Window* window;

  int lastPressedKey;  ///< SFML key code of the last pressed key.
  bool keyWasPressed;  ///< True if a key was pressed during the last step.
  std::map<gd::String, bool> keysPressed;  ///< The keys pressed for this frame.
  std::map<gd::String, bool>
      oldKeysPressed;  ///< The keys pressed during the last frame.
  std::vector<sf::Uint32>
      charactersEntered;  ///< The characters entered for this frame.

  int mouseWheelDelta;
  sf::Vector2i mousePosition;  ///< The mouse position for this frame.
  std::map<gd::String, bool>
      buttonsPressed;  ///< The buttons pressed for this frame.
  std::map<gd::String, bool>
      oldButtonsPressed;  ///< The buttons pressed during the last frame.

  void SimulateMousePressed(sf::Vector2i pos);
  bool touchSimulateMouse;
  std::map<int, sf::Vector2i> touches;

  bool windowHasFocus;  ///< True if the render target has the focus.
  bool disableInputWhenNotFocused;  ///< True if input should be ignored when
                                    ///< focus is lost.
};

#endif
