// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#include "nanojpeg.h"
#include "libc/syscall.h"
#include "libc/libui.h"
#include "libc/stdlib.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

#define VIEWER_MAX_W 800
#define VIEWER_MAX_H 600

static uint32_t *viewer_pixels = NULL;
static int viewer_img_w = 0;
static int viewer_img_h = 0;
static char viewer_title[64] = "Viewer";
static bool viewer_has_image = false;
static char viewer_file_path[256];

static int win_w = 500;
static int win_h = 400;

static void viewer_strcpy(char *dst, const char *src) {
    while (*src) *dst++ = *src++;
    *dst = 0;
}

static int viewer_strlen(const char *s) {
    int len = 0;
    while (s[len]) len++;
    return len;
}

static void viewer_scale_rgb_to_argb(const unsigned char *rgb, int src_w, int src_h,
                                     uint32_t *dst, int dst_w, int dst_h) {
    for (int y = 0; y < dst_h; y++) {
        int src_y = y * src_h / dst_h;
        if (src_y >= src_h) src_y = src_h - 1;
        for (int x = 0; x < dst_w; x++) {
            int src_x = x * src_w / dst_w;
            if (src_x >= src_w) src_x = src_w - 1;
            int idx = (src_y * src_w + src_x) * 3;
            unsigned char r = rgb[idx];
            unsigned char g = rgb[idx + 1];
            unsigned char b = rgb[idx + 2];
            dst[y * dst_w + x] = 0xFF000000 | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
        }
    }
}

static void viewer_paint(ui_window_t win) {
    int cx = 4;
    int cy = 0;
    int cw = win_w - 8;
    int ch = win_h - 28;

    ui_draw_rect(win, cx, cy, cw, ch, 0xFF1A1A1A);

    if (!viewer_has_image) {
        ui_draw_string(win, cx + 20, cy + ch / 2, "No image loaded", 0xFF888888);
        return;
    }

    int disp_w = viewer_img_w;
    int disp_h = viewer_img_h;

    if (disp_w > cw - 8) {
        disp_h = disp_h * (cw - 8) / disp_w;
        disp_w = cw - 8;
    }
    if (disp_h > ch - 40) {
        disp_w = disp_w * (ch - 40) / disp_h;
        disp_h = ch - 40;
    }

    int ox = cx + (cw - disp_w) / 2;
    int oy = cy + (ch - disp_h - 30) / 2;

    uint32_t *temp_buf = malloc(disp_w * disp_h * sizeof(uint32_t));
    if (temp_buf) {
        for (int y = 0; y < disp_h; y++) {
            int src_y = y * viewer_img_h / disp_h;
            if (src_y >= viewer_img_h) src_y = viewer_img_h - 1;
            for (int x = 0; x < disp_w; x++) {
                int src_x = x * viewer_img_w / disp_w;
                if (src_x >= viewer_img_w) src_x = viewer_img_w - 1;
                temp_buf[y * disp_w + x] = viewer_pixels[src_y * viewer_img_w + src_x];
            }
        }
        ui_draw_image(win, ox, oy, disp_w, disp_h, temp_buf);
        free(temp_buf);
    }

    int btn_w = 160;
    int btn_h = 22;
    int btn_x = cx + (cw - btn_w) / 2;
    int btn_y = (win_h - 20) - 30;
    ui_draw_rounded_rect_filled(win, btn_x, btn_y, btn_w, btn_h, 6, 0xFF2D2D2D);
    ui_draw_string(win, btn_x + 10, btn_y + 6, "Set as Wallpaper", 0xFFF0F0F0);
}

static void viewer_handle_click(ui_window_t win, int x, int y) {
    if (!viewer_has_image) return;

    int cx = 4;
    int cw = win_w - 8;
    int btn_w = 160;
    int btn_x = cx + (cw - btn_w) / 2;
    int btn_y = (win_h - 25) - 30; // Matches the hitboxes
    
    if (x >= btn_x && x < btn_x + btn_w && y >= btn_y && y < btn_y + 22) {
        // SYSTEM_CMD_SET_WALLPAPER_PATH is 31
        sys_system(31, (uint64_t)viewer_file_path, 0, 0, 0);
    }
}

void viewer_open_file(const char *path) {
    int fd = sys_open(path, "r");
    if (fd < 0) return;

    // We can't use stat yet, so read chunks until EOF
    // Alternatively, use a large buffer if sys_read handles large chunks.
    int alloc_size = 2 * 1024 * 1024;
    unsigned char *buf = malloc(alloc_size);
    if (!buf) {
        sys_close(fd);
        return;
    }

    int total_read = 0;
    while (total_read < alloc_size) {
        int chunk = sys_read(fd, (char*)buf + total_read, alloc_size - total_read);
        if (chunk <= 0) break;
        total_read += chunk;
    }
    sys_close(fd);

    if (total_read <= 0) {
        free(buf);
        return;
    }

    njInit();
    nj_result_t result = njDecode(buf, total_read);
    if (result != NJ_OK) {
        njDone();
        free(buf);
        return;
    }

    int img_w = njGetWidth();
    int img_h = njGetHeight();
    unsigned char *rgb = njGetImage();

    if (!rgb || img_w <= 0 || img_h <= 0) {
        njDone();
        free(buf);
        return;
    }

    int fit_w = img_w;
    int fit_h = img_h;
    if (fit_w > VIEWER_MAX_W) {
        fit_h = fit_h * VIEWER_MAX_W / fit_w;
        fit_w = VIEWER_MAX_W;
    }
    if (fit_h > VIEWER_MAX_H) {
        fit_w = fit_w * VIEWER_MAX_H / fit_h;
        fit_h = VIEWER_MAX_H;
    }

    viewer_pixels = malloc(fit_w * fit_h * sizeof(uint32_t));
    if (viewer_pixels) {
        viewer_scale_rgb_to_argb(rgb, img_w, img_h, viewer_pixels, fit_w, fit_h);
        viewer_img_w = fit_w;
        viewer_img_h = fit_h;
        viewer_has_image = true;
    }

    njDone();
    free(buf);

    viewer_strcpy(viewer_file_path, path);

    const char *fname = path;
    int plen = viewer_strlen(path);
    for (int i = plen - 1; i >= 0; i--) {
        if (path[i] == '/') {
            fname = &path[i + 1];
            break;
        }
    }
    
    viewer_title[0] = 'V'; viewer_title[1] = 'i'; viewer_title[2] = 'e';
    viewer_title[3] = 'w'; viewer_title[4] = 'e'; viewer_title[5] = 'r';
    viewer_title[6] = ' '; viewer_title[7] = '-'; viewer_title[8] = ' ';
    int ti = 9;
    for (int i = 0; fname[i] && ti < 60; i++) {
        viewer_title[ti++] = fname[i];
    }
    viewer_title[ti] = 0;

    win_w = fit_w + 16;
    if (win_w < 200) win_w = 200;
    win_h = fit_h + 64;
    if (win_h < 100) win_h = 100;
}

int main(int argc, char **argv) {
    if (argc > 1) {
        viewer_open_file(argv[1]);
    }

    ui_window_t win = ui_window_create(viewer_title, 100, 50, win_w, win_h);
    if (!win) return 1;

    gui_event_t ev;
    while (1) {
        if (ui_get_event(win, &ev)) {
            if (ev.type == GUI_EVENT_PAINT) {
                viewer_paint(win);
                ui_mark_dirty(win, 0, 0, win_w, win_h - 20);
            } else if (ev.type == GUI_EVENT_CLICK) {
                viewer_handle_click(win, ev.arg1, ev.arg2);
            } else if (ev.type == GUI_EVENT_CLOSE) {
                sys_exit(0);
            }
        }
    }
    return 0;
}
