/**
 * @file EventLoop.h
 * @author apio (cloudapio.eu)
 * @brief Base class for event-driven applications.
 *
 * @copyright Copyright (c) 2023-2024, the Luna authors.
 *
 */

#pragma once
#include <luna/HashMap.h>
#include <luna/Vector.h>
#include <os/Action.h>
#include <os/Timer.h>
#include <sys/poll.h>

namespace os
{
    /**
     * @brief Event loop implementation.
     */
    class EventLoop
    {
      public:
        /**
         * @brief Construct a new event loop, which will automatically register itself as the current global event loop.
         */
        EventLoop();

        ~EventLoop();

        EventLoop(EventLoop&&) = delete;
        EventLoop(const EventLoop&) = delete;

        /**
         * @brief Fetch the current global event loop.
         *
         * @return EventLoop& The current global event loop.
         */
        static EventLoop& the();

        /**
         * @brief Register a new event listener on a file descriptor.
         *
         * @param fd The file descriptor to listen on.
         * @param listener The callback function to invoke when events occur on the file descriptor. The first parameter
         * is the file descriptor registered, and the second argument is the type of event (POLLIN or POLLHUP)
         * @return Result<void> Whether the operation succeeded.
         */
        Result<void> register_fd_listener(int fd, Function<int, int> listener);

        /**
         * @brief Register a new POSIX signal handler.
         *
         * Unlike standard POSIX signal handling, this handler will be run synchronously as part of the event loop,
         * eliminating many problems with asynchronous signal handling.
         *
         * @param sig The signal to handle.
         * @param handler The function to call when this signal is caught.
         * @return Result<void> Whether the operation succeeded.
         */
        Result<void> register_signal_handler(int sig, Function<int> handler);

        /**
         * @brief Run the event loop until it is asked to quit.
         *
         * @return int The status passed to the quit() method.
         */
        int run();

        /**
         * @brief Ask the event loop to quit.
         *
         * @param status The status value to return from run().
         */
        void quit(int status = 0);

      private:
        HashMap<int, Function<int, int>> m_fd_listeners;
        HashMap<int, Function<int>> m_signal_handlers;
        Vector<struct pollfd> m_pollfds;

        static void handle_signal(int sig);
        static void handle_quit_signal(int);
        static void handle_timer_signal(int);

        bool m_should_quit { false };
        int m_quit_status { 0 };

        int m_signal_pipe[2];

        LinkedList<Timer> m_timer_list;

        friend class Timer;
    };
}