#include <Core/HAL/Display/DisplayDevice.h>
#include <Core/HAL/Event/SystemEvent.h>

#pragma push_macro("global")
#undef global
#include <chrono>
#pragma pop_macro("global")

#include <queue>

namespace Juliet
{
    namespace
    {
        // TODO : make my own queue / using vector
        std::queue<SystemEvent> eventQueue;

        // Update all systems event loops and gather events into the main queue
        void PumpEvents()
        {
            if (DisplayDevice* displayDevice = GetDisplayDevice())
            {
                displayDevice->PumpEvents(displayDevice);
            }
        }

        bool AddEvent_Internal(const SystemEvent& event)
        {
            auto& newEvent = eventQueue.emplace();
            newEvent       = event;

            // TODO : Logs

            return true;
        }
    } // namespace

    bool GetEvent(SystemEvent& event)
    {
        return WaitEvent(event, 0);
    }

    bool WaitEvent(SystemEvent& event, int32 timeoutInNS /* = -1 */)
    {
        using namespace std::chrono;

        // Handle the "Infinite Wait" and "Timed Wait" logic
        const bool        isInfinite = (timeoutInNS < 0);
        const nanoseconds timeout(timeoutInNS);
        const auto        startTime = steady_clock::now();

        while (true)
        {
            PumpEvents();

            if (!eventQueue.empty())
            {
                event = eventQueue.front();
                eventQueue.pop();
                return true;
            }

            // If timeout is 0, we only check once (PumpEvents already ran)
            if (timeoutInNS == 0)
            {
                break;
            }

            // Check if we have exceeded our time limit
            if (!isInfinite)
            {
                auto elapsed = steady_clock::now() - startTime;
                if (elapsed >= timeout)
                {
                    break;
                }
            }
        }

        return false;
    }

    bool AddEvent(SystemEvent& event)
    {
        if (event.Timestamp == 0)
        {
            event.Timestamp = 1; // TODO : Clock::Now();
        }

        return AddEvent_Internal(event);
    }
} // namespace Juliet
