#pragma once

#include <Core/HAL/Display/Display.h>
#include <Core/HAL/Keyboard/Keyboard.h>
#include <Core/HAL/Keyboard/KeyCode.h>
#include <Core/HAL/Mouse/Mouse.h>
#include <Juliet.h>

// Handles all events from systems handling the Hardware
// Very inspired by SDL3

namespace Juliet
{
    enum class EventType : uint32
    {
        None  = 0,
        First = None,

        // Application Events
        // User querying an exit
        Application_Exit = 100,
        // OS terminating the application
        Application_OS_Terminate,
        Application_Begin = Application_Exit,
        Application_End   = Application_OS_Terminate,

        // Window Events
        Window_Close_Request = 200,
        Window_Begin         = Window_Close_Request,
        Window_End           = Window_Close_Request,

        // Keyboard Event
        Key_Down = 300,
        Key_Up,
        Keyboard_Begin = Key_Down,
        Keyboard_End   = Key_Up,

        // Mouse Event
        Mouse_Move = 400,
        Mouse_ButtonPressed,
        Mouse_ButtonReleased,

        Mouse_Begin = Mouse_Move,
        Mouse_End   = Mouse_ButtonReleased,

        Last // Get value from the previous one
    };

    struct WindowEvent
    {
        WindowID AssociatedWindowID;
        uint32   DataPadding[2]; // TODO : define how much data param we need
    };

    struct KeyboardEvent
    {
        KeyboardID AssociatedKeyboardID;
        WindowID   WindowID;
        Key        Key;
        KeyState   KeyState;
        KeyMod     KeyModeState;
    };

    // =====================================================
    // Mouse Events
    // =====================================================
    struct MouseMovementEvent
    {
        MouseID     AssociatedMouseID;
        WindowID    WindowID;
        float       X;
        float       Y;
        float       X_Displacement;
        float       Y_Displacement;
        MouseButton ButtonState;
    };

    struct MouseButtonEvent
    {
        MouseID     AssociatedMouseID;
        WindowID    WindowID;
        float       X;
        float       Y;
        MouseButton ButtonState;
        bool        IsPressed : 1;
    };

    // Tagged union representing ALL possible system events + a bit of data for custom event if needed
    union AllSystemEventUnion
    {
        WindowEvent        Window;
        KeyboardEvent      Keyboard;
        MouseMovementEvent MouseMovement;
        MouseButtonEvent   MouseButton;
        uint8              Padding[128]; // Make sure that the union is fixed in size and big enough on all platforms.
    };
    // Make sure we do not bust the union size
    static_assert(sizeof(AllSystemEventUnion) == sizeof(((AllSystemEventUnion*)nullptr)->Padding));

    struct SystemEvent
    {
        EventType           Type;
        uint64              Timestamp;
        AllSystemEventUnion Data;
    };

    // Poll for any event, return false if no event is available.
    // Equivalent to WaitEvent(event, 0);
    // Will not block
    extern JULIET_API bool GetEvent(SystemEvent& event);

    // TODO : use chrono to tag the timeout correctly with nanosec
    // timeout == -1 means wait for any event before pursuing
    // timeout == 0 means checking once for the frame and getting out
    // timeout > 0 means wait until time is out
    extern JULIET_API bool WaitEvent(SystemEvent& event, int32 timeoutInNS = -1);

    // Add an event onto the event queue.
    // TODO : support array of events
    extern JULIET_API bool AddEvent(SystemEvent& event);
} // namespace Juliet
