#include "Mouse.h"
#include "Screen.h"
#include "Window.h"
#include <errno.h>
#include <moon/Keyboard.h>
#include <os/ArgumentParser.h>
#include <os/File.h>
#include <os/LocalServer.h>
#include <pwd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <time.h>
#include <unistd.h>

Result<int> luna_main(int argc, char** argv)
{
    srand((unsigned)time(NULL));

    StringView socket_path = "/tmp/wind.sock";
    StringView user;

    os::ArgumentParser parser;
    parser.add_description("The display server for Luna's graphical user interface."_sv);
    parser.add_system_program_info("wind"_sv);
    parser.add_value_argument(socket_path, 's', "socket"_sv, "the path for the local IPC socket"_sv);
    parser.add_value_argument(user, 'u', "user"_sv, "the user to run as"_sv);
    parser.parse(argc, argv);

    if (geteuid() != 0)
    {
        os::eprintln("error: wind must be run as root to initialize resources, run with --user=<USERNAME> to drop "
                     "privileges afterwards");
        return 1;
    }

    auto mouse = TRY(os::File::open("/dev/mouse", os::File::ReadOnly));
    mouse->set_buffer(os::File::NotBuffered);
    mouse->set_close_on_exec();

    auto keyboard = TRY(os::File::open("/dev/kbd", os::File::ReadOnly));
    keyboard->set_buffer(os::File::NotBuffered);
    keyboard->set_close_on_exec();

    auto screen = TRY(Screen::open());

    auto server = TRY(os::LocalServer::create(socket_path, false));
    TRY(server->listen(20));

    Mouse mouse_pointer { screen.canvas() };

    ioctl(STDIN_FILENO, TTYSETGFX, 1);

    setpgid(0, 0);

    int fd = open("/dev/null", O_RDONLY);
    if (fd >= 0)
    {
        dup2(fd, STDIN_FILENO);
        close(fd);
    }

    clearenv();

    if (!user.is_empty())
    {
        auto* pwd = getpwnam(user.chars());
        if (pwd)
        {
            setgid(pwd->pw_gid);
            setuid(pwd->pw_uid);
        }
    }

    ui::Color background = ui::BLACK;

    TRY(make<Window>(ui::Rect { 200, 200, 600, 400 }, ui::GREEN, "Calculator"_sv));
    TRY(make<Window>(ui::Rect { 100, 100, 300, 200 }, ui::RED, "Settings"_sv));
    TRY(make<Window>(ui::Rect { 600, 130, 350, 250 }, ui::CYAN, "File Manager"_sv));

    Vector<SharedPtr<os::LocalServer::Client>> clients;

    while (1)
    {
        screen.canvas().fill(background);
        for (auto* window : g_windows) window->draw(screen.canvas());
        mouse_pointer.draw(screen.canvas());
        screen.sync();

        struct pollfd fds[] = {
            { .fd = mouse->fd(), .events = POLLIN, .revents = 0 },
            { .fd = keyboard->fd(), .events = POLLIN, .revents = 0 },
            { .fd = server->fd(), .events = POLLIN, .revents = 0 },
        };

        int rc = poll(fds, 3, 1000);
        if (!rc) continue;
        if (rc < 0) { os::println("poll: error: %s", strerror(errno)); }

        if (fds[0].revents & POLLIN)
        {
            moon::MousePacket packet;
            TRY(mouse->read_typed(packet));
            mouse_pointer.update(packet);
        }
        if (fds[1].revents & POLLIN)
        {
            moon::KeyboardPacket packet;
            TRY(keyboard->read_typed(packet));
            if (!packet.released)
            {
                TRY(make<Window>(ui::Rect { rand() % screen.canvas().width, rand() % screen.canvas().height,
                                            rand() % screen.canvas().width, rand() % screen.canvas().height },
                                 ui::Color::from_rgb(static_cast<u8>(rand() % 256), static_cast<u8>(rand() % 256),
                                                     static_cast<u8>(rand() % 256)),
                                 strerror(packet.key)));
            }
        }
        if (fds[2].revents & POLLIN)
        {
            auto client = TRY(server->accept());
            os::println("wind: New client connected!");
            TRY(clients.try_append(client));
        }
    }
}