////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2024 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
//    you must not claim that you wrote the original software.
//    If you use this software in a product, an acknowledgment
//    in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
//    and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////

#pragma once

////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Window/Joystick.hpp>
#include <SFML/Window/Keyboard.hpp>
#include <SFML/Window/Mouse.hpp>
#include <SFML/Window/Sensor.hpp>

#include <SFML/System/Vector2.hpp>

#include <variant>


namespace sf
{
////////////////////////////////////////////////////////////
/// \brief Defines a system event and its parameters
///
////////////////////////////////////////////////////////////
class Event
{
public:
    ////////////////////////////////////////////////////////////
    /// \brief Closed event subtype
    ///
    ////////////////////////////////////////////////////////////
    struct Closed
    {
    };

    ////////////////////////////////////////////////////////////
    /// \brief Resized event subtype
    ///
    ////////////////////////////////////////////////////////////
    struct Resized
    {
        Vector2u size; //!< New size, in pixels
    };

    ////////////////////////////////////////////////////////////
    /// \brief Lost focus event subtype
    ///
    ////////////////////////////////////////////////////////////
    struct FocusLost
    {
    };

    ////////////////////////////////////////////////////////////
    /// \brief Gained focus event subtype
    ///
    ////////////////////////////////////////////////////////////
    struct FocusGained
    {
    };

    ////////////////////////////////////////////////////////////
    /// \brief Text event subtype
    ///
    ////////////////////////////////////////////////////////////
    struct TextEntered
    {
        char32_t unicode{}; //!< UTF-32 Unicode value of the character
    };

    ////////////////////////////////////////////////////////////
    /// \brief Key pressed event subtype
    ///
    ////////////////////////////////////////////////////////////
    struct KeyPressed
    {
        Keyboard::Key      code{};     //!< Code of the key that has been pressed
        Keyboard::Scancode scancode{}; //!< Physical code of the key that has been pressed
        bool               alt{};      //!< Is the Alt key pressed?
        bool               control{};  //!< Is the Control key pressed?
        bool               shift{};    //!< Is the Shift key pressed?
        bool               system{};   //!< Is the System key pressed?
    };

    ////////////////////////////////////////////////////////////
    /// \brief Key released event subtype
    ///
    ////////////////////////////////////////////////////////////
    struct KeyReleased
    {
        Keyboard::Key      code{};     //!< Code of the key that has been released
        Keyboard::Scancode scancode{}; //!< Physical code of the key that has been released
        bool               alt{};      //!< Is the Alt key pressed?
        bool               control{};  //!< Is the Control key pressed?
        bool               shift{};    //!< Is the Shift key pressed?
        bool               system{};   //!< Is the System key pressed?
    };

    ////////////////////////////////////////////////////////////
    /// \brief Mouse wheel scrolled event subtype
    ///
    ////////////////////////////////////////////////////////////
    struct MouseWheelScrolled
    {
        Mouse::Wheel wheel{}; //!< Which wheel (for mice with multiple ones)
        float delta{}; //!< Wheel offset (positive is up/left, negative is down/right). High-precision mice may use non-integral offsets.
        Vector2i position; //!< Position of the mouse pointer, relative to the top left of the owner window
    };

    ////////////////////////////////////////////////////////////
    /// \brief Mouse button pressed event subtype
    ///
    ////////////////////////////////////////////////////////////
    struct MouseButtonPressed
    {
        Mouse::Button button{}; //!< Code of the button that has been pressed
        Vector2i      position; //!< Position of the mouse pointer, relative to the top left of the owner window
    };

    ////////////////////////////////////////////////////////////
    /// \brief Mouse button released event subtype
    ///
    ////////////////////////////////////////////////////////////
    struct MouseButtonReleased
    {
        Mouse::Button button{}; //!< Code of the button that has been released
        Vector2i      position; //!< Position of the mouse pointer, relative to the top left of the owner window
    };

    ////////////////////////////////////////////////////////////
    /// \brief Mouse move event subtype
    ///
    ////////////////////////////////////////////////////////////
    struct MouseMoved
    {
        Vector2i position; //!< Position of the mouse pointer, relative to the top left of the owner window
    };

    ////////////////////////////////////////////////////////////
    /// \brief Mouse move raw event subtype
    ///
    /// Raw mouse input data comes unprocessed from the
    /// operating system hence "raw". While the MouseMoved
    /// position value is dependent on the screen resolution,
    /// raw data is not. If the physical mouse is moved too
    /// little to cause the screen cursor to move at least a
    /// single pixel, no MouseMoved event will be generated. In
    /// contrast, any movement information generated by the
    /// mouse independent of its sensor resolution will always
    /// generate a `MouseMovedRaw` event.
    ///
    /// In addition to screen resolution independence, raw
    /// mouse data also does not have mouse acceleration or
    /// smoothing applied to it as MouseMoved does.
    ///
    /// Raw mouse movement data is intended for controlling
    /// non-cursor movement, e.g. controlling the camera
    /// orientation in a first person view, whereas MouseMoved
    /// is intended primarily for controlling things related to
    /// the screen cursor hence the additional processing
    /// applied to it.
    ///
    /// Currently, raw mouse input events will only be generated
    /// on Windows and Linux.
    ///
    ////////////////////////////////////////////////////////////
    struct MouseMovedRaw
    {
        Vector2i delta; ///< Delta movement of the mouse since the last event
    };

    ////////////////////////////////////////////////////////////
    /// \brief Mouse entered event subtype
    ///
    ////////////////////////////////////////////////////////////
    struct MouseEntered
    {
    };

    ////////////////////////////////////////////////////////////
    /// \brief Mouse left event subtype
    ///
    ////////////////////////////////////////////////////////////
    struct MouseLeft
    {
    };

    ////////////////////////////////////////////////////////////
    /// \brief Joystick button pressed event subtype
    ///
    ////////////////////////////////////////////////////////////
    struct JoystickButtonPressed
    {
        unsigned int joystickId{}; //!< Index of the joystick (in range [0 .. Joystick::Count - 1])
        unsigned int button{}; //!< Index of the button that has been pressed (in range [0 .. Joystick::ButtonCount - 1])
    };

    ////////////////////////////////////////////////////////////
    /// \brief Joystick button released event subtype
    ///
    ////////////////////////////////////////////////////////////
    struct JoystickButtonReleased
    {
        unsigned int joystickId{}; //!< Index of the joystick (in range [0 .. Joystick::Count - 1])
        unsigned int button{}; //!< Index of the button that has been released (in range [0 .. Joystick::ButtonCount - 1])
    };

    ////////////////////////////////////////////////////////////
    /// \brief Joystick axis move event subtype
    ///
    ////////////////////////////////////////////////////////////
    struct JoystickMoved
    {
        unsigned int   joystickId{}; //!< Index of the joystick (in range [0 .. Joystick::Count - 1])
        Joystick::Axis axis{};       //!< Axis on which the joystick moved
        float          position{};   //!< New position on the axis (in range [-100 .. 100])
    };

    ////////////////////////////////////////////////////////////
    /// \brief Joystick connected event subtype
    ///
    ////////////////////////////////////////////////////////////
    struct JoystickConnected
    {
        unsigned int joystickId{}; //!< Index of the joystick (in range [0 .. Joystick::Count - 1])
    };

    ////////////////////////////////////////////////////////////
    /// \brief Joystick disconnected event subtype
    ///
    ////////////////////////////////////////////////////////////
    struct JoystickDisconnected
    {
        unsigned int joystickId{}; //!< Index of the joystick (in range [0 .. Joystick::Count - 1])
    };

    ////////////////////////////////////////////////////////////
    /// \brief Touch began event subtype
    ///
    ////////////////////////////////////////////////////////////
    struct TouchBegan
    {
        unsigned int finger{}; //!< Index of the finger in case of multi-touch events
        Vector2i     position; //!< Start position of the touch, relative to the top left of the owner window
    };

    ////////////////////////////////////////////////////////////
    /// \brief Touch moved event subtype
    ///
    ////////////////////////////////////////////////////////////
    struct TouchMoved
    {
        unsigned int finger{}; //!< Index of the finger in case of multi-touch events
        Vector2i     position; //!< Current position of the touch, relative to the top left of the owner window
    };

    ////////////////////////////////////////////////////////////
    /// \brief Touch ended event subtype
    ///
    ////////////////////////////////////////////////////////////
    struct TouchEnded
    {
        unsigned int finger{}; //!< Index of the finger in case of multi-touch events
        Vector2i     position; //!< Final position of the touch, relative to the top left of the owner window
    };

    ////////////////////////////////////////////////////////////
    /// \brief Sensor event subtype
    ///
    ////////////////////////////////////////////////////////////
    struct SensorChanged
    {
        Sensor::Type type{}; //!< Type of the sensor
        Vector3f     value;  //!< Current value of the sensor on the X, Y, and Z axes
    };

    ////////////////////////////////////////////////////////////
    /// \brief Construct from a given `sf::Event` subtype
    ///
    /// \tparam `TEventSubtype` Type of event subtype used to construct the event
    ///
    /// \param eventSubtype Event subtype instance used to construct the event
    ///
    ////////////////////////////////////////////////////////////
    template <typename TEventSubtype>
    Event(const TEventSubtype& eventSubtype);

    ////////////////////////////////////////////////////////////
    /// \brief Check current event subtype
    ///
    /// \tparam `TEventSubtype` Type of the event subtype to check against
    ///
    /// \return `true` if the current event subtype matches given template parameter
    ///
    ////////////////////////////////////////////////////////////
    template <typename TEventSubtype>
    [[nodiscard]] bool is() const;

    ////////////////////////////////////////////////////////////
    /// \brief Attempt to get specified event subtype
    ///
    /// \tparam `TEventSubtype` Type of the desired event subtype
    ///
    /// \return Address of current event subtype, otherwise `nullptr`
    ///
    ////////////////////////////////////////////////////////////
    template <typename TEventSubtype>
    [[nodiscard]] const TEventSubtype* getIf() const;

    ////////////////////////////////////////////////////////////
    /// \brief Apply a visitor to the event
    ///
    /// \param visitor The visitor to apply
    ///
    /// \return The result of applying the visitor to the event
    ///
    ////////////////////////////////////////////////////////////
    template <typename T>
    decltype(auto) visit(T&& visitor) const;

private:
    ////////////////////////////////////////////////////////////
    // Member data
    ////////////////////////////////////////////////////////////
    std::variant<Closed,
                 Resized,
                 FocusLost,
                 FocusGained,
                 TextEntered,
                 KeyPressed,
                 KeyReleased,
                 MouseWheelScrolled,
                 MouseButtonPressed,
                 MouseButtonReleased,
                 MouseMoved,
                 MouseMovedRaw,
                 MouseEntered,
                 MouseLeft,
                 JoystickButtonPressed,
                 JoystickButtonReleased,
                 JoystickMoved,
                 JoystickConnected,
                 JoystickDisconnected,
                 TouchBegan,
                 TouchMoved,
                 TouchEnded,
                 SensorChanged>
        m_data; //!< Event data

    ////////////////////////////////////////////////////////////
    // Helper functions
    ////////////////////////////////////////////////////////////
    template <typename T, typename... Ts>
    [[nodiscard]] static constexpr bool isInParameterPack(const std::variant<Ts...>*)
    {
        return (std::is_same_v<T, Ts> || ...);
    }

    template <typename T>
    static constexpr bool isEventSubtype = isInParameterPack<T>(decltype (&m_data)(nullptr));
};

} // namespace sf

#include <SFML/Window/Event.inl>


////////////////////////////////////////////////////////////
/// \class sf::Event
/// \ingroup window
///
/// `sf::Event` holds all the information about a system event
/// that just happened. Events are retrieved using the
/// `sf::Window::pollEvent` and `sf::Window::waitEvent` functions.
///
/// A `sf::Event` instance contains the subtype of the event
/// (mouse moved, key pressed, window closed, ...) as well
/// as the details about this particular event. Each event
/// corresponds to a different subtype struct which contains
/// the data required to process that event.
///
/// Event subtypes are event types belonging to `sf::Event`,
/// such as `sf::Event::Closed` or `sf::Event::MouseMoved`.
///
/// The way to access the current active event subtype is via
/// `sf::Event::getIf`. This member function returns the address
/// of the event subtype struct if the event subtype matches the
/// active event, otherwise it returns `nullptr`.
///
/// `sf::Event::is` is used to check the active event subtype
/// without actually reading any of the corresponding event data.
///
/// \code
/// while (const std::optional event = window.pollEvent())
/// {
///     // Window closed or escape key pressed: exit
///     if (event->is<sf::Event::Closed>() ||
///         (event->is<sf::Event::KeyPressed>() &&
///          event->getIf<sf::Event::KeyPressed>()->code == sf::Keyboard::Key::Escape))
///         window.close();
///
///     // The window was resized
///     if (const auto* resized = event->getIf<sf::Event::Resized>())
///         doSomethingWithTheNewSize(resized->size);
///
///     // etc ...
/// }
/// \endcode
///
////////////////////////////////////////////////////////////
