/* ************************************************************************** */
/*                                                                            */
/*                                                        :::      ::::::::   */
/*   minirt.c                                           :+:      :+:    :+:   */
/*                                                    +:+ +:+         +:+     */
/*   By: bszilas <bszilas@student.42vienna.com>     +#+  +:+       +#+        */
/*                                                +#+#+#+#+#+   +#+           */
/*   Created: 2024/09/08 16:33:29 by victor            #+#    #+#             */
/*   Updated: 2025/05/31 18:22:50 by victor           ###   ########.fr       */
/*                                                                            */
/* ************************************************************************** */

#include "../minirt.h"
#include <stdio.h>

t_data      data = { 0 };

uint ratio_x = 0;
uint ratio_y = 0;

uint max_res_x = 1;
uint max_res_y = 1;

void create_display() {
    SetConfigFlags(FLAG_WINDOW_UNDECORATED);
    InitWindow(800, 600, "miniRT");
    int monitor = GetCurrentMonitor();
    int w = GetMonitorWidth(monitor);
    int h = GetMonitorHeight(monitor);
    SetWindowSize(w, h);
    SetWindowPosition(0, 0);
    data.scene.w = w;
    data.scene.h = h;
    SetTargetFPS(60);
}

void    data_destroy_func(void *data_ptr)
{
    t_data    *data;

    data = data_ptr;
#ifndef __EMSCRIPTEN__
    data->go = false;

    pthread_rwlock_unlock(&data->rwlock);
    while (data->thread_count--)
        pthread_join(data->threads[data->thread_count].thread, NULL);
    pthread_barrier_destroy(&data->barrier);
    pthread_rwlock_destroy(&data->rwlock);
#endif /* ifndef __EMSCRIPTEN__ */
    UnloadTexture(data->texture);
    CloseWindow();
}

t_container    *menu_init(t_data *data)
{
    t_container    *menu;
    t_item        item;

    menu = ft_calloc(16, sizeof(*menu));
    lst_memory(menu, free, ADD);
    menu[ID_GROUP_MENU_MAIN - 1] = container_create("Main menu", \
            NULL, CONTAINER_GRID);
    item = container_item_button_create("add sphere", \
            NULL, scene_add_sphere_func);
    container_item_add(&menu[ID_GROUP_MENU_MAIN - 1], &item);
    item = container_item_button_create("add plane", NULL, \
            scene_add_plane_func);
    container_item_add(&menu[ID_GROUP_MENU_MAIN - 1], &item);
    item = container_item_button_create("add cylinder", NULL, \
            scene_add_cylinder);
    container_item_add(&menu[ID_GROUP_MENU_MAIN - 1], &item);
    item = container_item_button_create("add disk", NULL, \
            scene_add_disk);
    container_item_add(&menu[ID_GROUP_MENU_MAIN - 1], &item);
    item = container_item_button_create("show_help", NULL, help_menu_draw);
    container_item_add(&menu[ID_GROUP_MENU_MAIN - 1], &item);
    item = container_item_button_create("Load Scene", NULL, explorer_read_dir);
    item.param = (t_param){&data->scene, NULL, PARAM_SCENE};
    container_item_add(&menu[ID_GROUP_MENU_MAIN - 1], &item);
    return (menu);
}

t_container    *menus_create(t_data *data)
{
    t_container    *menu;
    uint        i;

    menu = menu_init(data);
    sphere_menu_create(&menu[ID_GROUP_SPHERE]);
    plane_menu_create(&menu[ID_GROUP_PLANE]);
    cone_menu_create(&menu[ID_GROUP_CONE]);
    disc_menu_create(&menu[ID_GROUP_DISC]);
    cylinder_menu_create(&menu[ID_GROUP_CYLINDER]);
    i = 0;
    while (i < 16)
        menu[i++].data = data;
    return (menu);
}

void    initialize_data(t_data *data, char *path)
{
    create_display();
    scene_create(path, &data->scene);
    data->pixel = pixel_plane_create();
    data->func_ptr = help_menu_draw;
    data->mouse.data = data;
    data->scene.pixel = data->pixel;
    data->menu = menus_create(data);
    Image img = GenImageColor(data->scene.w, data->scene.h, BLACK);
    data->pixel_colors = img.data;
    data->texture = LoadTextureFromImage(img);
    data->should_rerender = true;
    pixels_image_syncronize(data->pixel_colors, data->pixel);
    SetTargetFPS(60);
}

char*     validate_file_extension(int argc, char **argv)
{
    if (argc == 2 && ft_strlen(argv[1]) > 3 && ft_memcmp(&argv[1][ft_strlen(argv[1]) - 3], ".rt\0", 4) == 0)
        return argv[1];
    ft_fprintf(STDERR_FILENO, "Invalid Argument to Program!\nExiting...\n");
    fflush(stderr);
    exit(EXIT_FAILURE);
}

void    get_key_code(int keys[]) {
    uint c = 0;

    if (IsKeyPressed(KEY_ESCAPE)) {
        keys[c++] = KEY_ESCAPE;
        if (c == 3) return;
    }
    if ((IsKeyPressed(KEY_H) || IsKeyDown(KEY_H))) {
        keys[c++] = KEY_H;
        if (c == 3) return;
    }
    if ((IsKeyPressed(KEY_L) || IsKeyDown(KEY_L))) {
        keys[c++] = KEY_L;
        if (c == 3) return ;
    }
    if ((IsKeyPressed(KEY_K) || IsKeyDown(KEY_K))) {
        keys[c++] = KEY_K;
        if (c == 3) return ;
    }
    if ((IsKeyPressed(KEY_I) || IsKeyDown(KEY_I))) {
        keys[c++] = KEY_J;
        if (c == 3) return ;
    }
    if ((IsKeyPressed(KEY_O) || IsKeyDown(KEY_O))) {
        keys[c++] = KEY_K;
        if (c == 3) return ;
    }
    if ((IsKeyPressed(KEY_J) || IsKeyDown(KEY_J))) {
        keys[c++] = KEY_J;
        if (c == 3) return ;
    }
    if ((IsKeyPressed(KEY_X))) {
        keys[c++] = KEY_X;
        if (c == 3) return ;
    }
    if ((IsKeyPressed(KEY_W) || IsKeyDown(KEY_W))) {
        keys[c++] = KEY_W;
        if (c == 3) return;
    }
    if ((IsKeyPressed(KEY_E) || IsKeyDown(KEY_E))) {
        keys[c++] = KEY_E;
        if (c == 3) return ;
    }
    if ((IsKeyPressed(KEY_Q) || IsKeyDown(KEY_Q))) {
        keys[c++] = KEY_Q;
        if (c == 3) return ;
    }
    if ((IsKeyPressed(KEY_A) || IsKeyDown(KEY_A))) {
        keys[c++] = KEY_A;
        if (c == 3) return ;
    }
    if ((IsKeyPressed(KEY_S) || IsKeyDown(KEY_S))) {
        keys[c++] = KEY_S;
        if (c == 3) return ;
    }
    if ((IsKeyPressed(KEY_D) || IsKeyDown(KEY_D))) {
        keys[c++] = KEY_D;
        if (c == 3) return ;
    }
    if (IsKeyPressed(KEY_ENTER)) {
        keys[c++] = KEY_ENTER;
        if (c == 3) return ;
    }
    if (IsKeyPressed(KEY_BACKSPACE)) {
        keys[c++] = KEY_BACKSPACE;
        if (c == 3) return ;
    }
    if (IsKeyPressed(KEY_SLASH)) {
        keys[c++] = KEY_SLASH;
        if (c == 3) return ;
    }
    if (IsKeyPressed(KEY_PERIOD)) {
        keys[c++] = KEY_PERIOD;
        if (c == 3) return ;
    }
    if (IsKeyPressed(KEY_M)) {
        keys[c++] = KEY_M;
        if (c == 3) return ;
    }
    if (IsKeyPressed(KEY_N)) {
        keys[c++] = KEY_N;
        if (c == 3) return ;
    }
    if (IsKeyPressed(KEY_P)) {
        keys[c++] = KEY_P;
        if (c == 3) return ;
    }
    if (IsKeyPressed(KEY_EQUAL)) {
        keys[c++] = KEY_EQUAL;
        if (c == 3) return ;
    }
    if (IsKeyPressed(KEY_Y)) {
        keys[c++] = KEY_Y;
        if (c == 3) return ;
    }

    if (IsKeyPressed(KEY_MINUS)) {
        keys[c++] = KEY_MINUS;
        if (c == 3) return ;
    }
    if (IsKeyDown(KEY_ONE)) {
        keys[c++] = KEY_ONE;
        if (c == 3) return ;
    }
    if (IsKeyDown(KEY_TWO)) {
        keys[c++] = KEY_TWO;
        if (c == 3) return ;
    }
    if (IsKeyDown(KEY_THREE)) {
        keys[c++] = KEY_THREE;
        if (c == 3) return ;
    }
    if (IsKeyDown(KEY_FOUR)) {
        keys[c++] = KEY_FOUR;
        if (c == 3) return ;
    }
    if ((IsKeyPressed(KEY_UP) || IsKeyDown(KEY_UP))) {
        keys[c++] = KEY_UP;
        if (c == 3) return ;
    }
    if ((IsKeyPressed(KEY_DOWN) || IsKeyDown(KEY_DOWN))) {
        keys[c++] = KEY_DOWN;
        if (c == 3) return ;
    }
    if ((IsKeyPressed(KEY_LEFT) || IsKeyDown(KEY_LEFT))) {
        keys[c++] = KEY_LEFT;
        if (c == 3) return ;
    }
    if ((IsKeyPressed(KEY_RIGHT) || IsKeyDown(KEY_RIGHT))) {
        keys[c++] = KEY_RIGHT;
        if (c == 3) return ;
    }
}

bool key_was_pressed = false;

void main_loop() {
    int         keys[3] = { 0 };

    Vector2 mouse_pos = GetMousePosition();
    if (data.mouse.left_is_pressed || data.mouse.right_is_pressed)
    {
        mouse_move(mouse_pos.x, mouse_pos.y, &data);
        mouse_release(mouse_pos.x, mouse_pos.y, &data);
    }
    else
        mouse_press(mouse_pos.x, mouse_pos.y, &data);
    bzero(keys, sizeof(keys));
    get_key_code(keys);
    for (int i = 0; i < 3; i++)
    {
        if (keys[i])
        {
            key_was_pressed = true;
            data.should_rerender = true;
#ifdef __EMSCRIPTEN__
            data.scene.resolution_x = ratio_x * 2;
            data.scene.resolution_y = ratio_y * 2;
#else
            data.scene.resolution_x = ratio_x;
            data.scene.resolution_y = ratio_y;
#endif /* ifdef __EMSCRIPTEN__ */
            key_press(keys[i], &data);
        }
    }

    rendering_loop(&data);

    data.scene.resolution_x = max_res_x;
    data.scene.resolution_y = max_res_y;

    if (key_was_pressed) {
        key_was_pressed = false;
    } else {
        data.should_rerender = false;
        data.func_ptr = NULL;
    }
}

int gcd(int a, int b) {
    while (b != 0) {
        int temp = b;
        b = a % b;
        a = temp;
    }
    return a;
}

int     main(int argc, char **argv)
{
    char        *path;

#ifndef __EMSCRIPTEN__
    t_thread    thread[THREAD_COUNT];
#endif /* ifndef __EMSCRIPTEN__ */

    if (argc == 1) {
        path = SCENES_PATH"/multilight.rt";
    }
    else
        path = validate_file_extension(argc, argv);

    initialize_data(&data, path);
    lst_memory(&data, data_destroy_func, ADD);

    int divisor = gcd(data.scene.w, data.scene.h);

    ratio_x = data.scene.w / divisor;
    ratio_y = data.scene.h / divisor;

#ifndef __EMSCRIPTEN__
    threads_init(thread, &data);
    data.threads = thread;
#endif

#ifdef __EMSCRIPTEN__
    max_res_y = ratio_y;
    max_res_x = ratio_x;
    emscripten_set_main_loop(main_loop, 0, 1);
#else
    while(!WindowShouldClose()) {
        main_loop();
    }
#endif /* ifdef __EMSCRIPTEN__ */

    return (0);
}
