#include <pch.h>
#include <stdio.h>
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <app.h>
#include <platform_legacy/legacy_platform_state.h>
#include <platform_legacy/legacy_platform_services.h>
#include <libgen.h>
#include <pwd.h>
#include <unistd.h>

static App app;
glm::vec2 g_cursor_pos;

int mkpath(const std::string& dir, mode_t mode = DEFFILEMODE)
{
    struct stat sb;

    if (dir.empty()) {
        errno = EINVAL;
        return 1;
    }

    if (!stat(dir.c_str(), &sb))
        return 0;

    mkpath(dirname(strdupa(dir.c_str())), mode);

    int ret = mkdir(dir.c_str(), mode);
    chmod(dir.c_str(), S_IRWXU);
    if (ret != 0)
        LOG("mkdir failed with error %d on %s", errno, dir.c_str());
    return ret;
}

std::string linux_home_path()
{
    struct passwd *pw = getpwuid(getuid());
    return pw->pw_dir;
}

void error_log(int code, const char * s)
{
    printf("glfw error: %s", s);
}

int main(int argc, char** args)
{
    GLFWwindow* wnd = nullptr;
    glfwSetErrorCallback(error_log);
    if (!glfwInit())
    {
        printf("could not initialize glfw\n");
        return 1;
    }

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    //glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_FORWARD_COMPAT);

    wnd = glfwCreateWindow(800, 600, "PanoPainter", nullptr, nullptr);
    if (!wnd)
    {
        printf("could not create window\n");
        return 1;
    }
    pp::platform::linux_desktop::set_fps_title_callback([wnd](std::string title) {
        glfwSetWindowTitle(wnd, title.c_str());
    });
    auto platform_services = pp::platform::legacy::create_platform_services({
        .glfw_shell = {
            .acquire_render_context = [wnd] { glfwMakeContextCurrent(wnd); },
            .present_render_context = [wnd] { glfwSwapBuffers(wnd); },
            .request_app_close = [wnd] { glfwSetWindowShouldClose(wnd, GLFW_TRUE); },
        },
    });

    glfwSetCursorPosCallback(wnd, [](GLFWwindow* wnd, double x, double y){
        g_cursor_pos = glm::vec2(x, y);
        app.ui_task_async([=]{
            app.mouse_move(x, y, 1.f, kEventSource::Mouse, false);
        });
    });
    glfwSetMouseButtonCallback(wnd, [](GLFWwindow* wnd, int button, int action, int mods){
        app.ui_task_async([=]{
            if (action == GLFW_PRESS)
                app.mouse_down(button, g_cursor_pos.x, g_cursor_pos.y, 1.f, kEventSource::Mouse, false);
            else if (action == GLFW_RELEASE)
                app.mouse_up(button, g_cursor_pos.x, g_cursor_pos.y, kEventSource::Mouse, false);
        });
    });
    glfwSetWindowSizeCallback(wnd, [](GLFWwindow* wnd, int width, int height){
        app.ui_task_async([=]{
            app.resize(width, height);
        });
    });
    glfwSetWindowCloseCallback(wnd, [](GLFWwindow* wnd){
        app.ui_task([wnd] {
            if (!app.request_close())
                glfwSetWindowShouldClose(wnd, GLFW_FALSE);
        });
    });
    glfwSetWindowRefreshCallback(wnd, [](GLFWwindow* wnd){
        app.ui_task_async([=]{
            app.redraw = true;
        }, true);
    });

    glfwMakeContextCurrent(wnd);
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        printf("Failed to initialize OpenGL context\n");
        return -1;
    }

    printf("GL: %s\n", glGetString(GL_VERSION));
    printf("Renderer: %s\n", glGetString(GL_RENDER));
    printf("Vendor: %s\n", glGetString(GL_VENDOR));
    glfwShowWindow(wnd);

    umask(0);

    App::I = &app;
    app.set_platform_services(platform_services.get());
    app.initLog();
    app.create();
    app.width = 800;
    app.height = 600;
    app.render_thread_start();
    app.ui_thread_start();

    while (!glfwWindowShouldClose(wnd))
    {
        glfwWaitEvents();
    }

    app.ui_thread_stop();
    app.render_thread_stop();
    app.terminate();
    pp::platform::linux_desktop::set_fps_title_callback({});

    return 0;
}
