// 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 "libc/syscall.h"
#include "libc/libui.h"
#include "libc/stdlib.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

#define CANVAS_W 300
#define CANVAS_H 200
#define PAINT_MAGIC 0x544E5042 // 'BPNT'

#define COLOR_BLACK         0xFF000000
#define COLOR_WHITE         0xFFFFFFFF
#define COLOR_RED           0xFFFF0000
#define COLOR_APPLE_GREEN   0xFF4CD964
#define COLOR_APPLE_BLUE    0xFF007AFF
#define COLOR_APPLE_YELLOW  0xFFFFCC00

#define COLOR_DARK_BG       0xFF121212
#define COLOR_DARK_PANEL    0xFF202020
#define COLOR_DARK_BORDER   0xFF404040
#define COLOR_DARK_TEXT     0xFFE0E0E0

static uint32_t *canvas_buffer = NULL;
static uint32_t current_color = COLOR_BLACK;
static int last_mx = -1;
static int last_my = -1;
static char current_file_path[256] = "/Desktop/drawing.pnt";

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

static void debug_print(const char *msg) {
    sys_write(1, msg, 0);
    int i = 0;
    while (msg[i]) i++;
    sys_write(1, msg, i);
    sys_write(1, "\n", 1);
}

static void paint_reset(void) {
    if (canvas_buffer) {
        for (int i = 0; i < CANVAS_W * CANVAS_H; i++) {
            canvas_buffer[i] = COLOR_WHITE;
        }
    }
}

static void paint_paint(ui_window_t win) {
    int canvas_x = 60;
    int canvas_y = 0;
    
    ui_draw_rounded_rect_filled(win, canvas_x - 2, canvas_y - 2, CANVAS_W + 4, CANVAS_H + 4, 4, COLOR_DARK_BG);
    ui_draw_rounded_rect_filled(win, 10, 0, 40, 230, 6, COLOR_DARK_PANEL);
    
    uint32_t colors[] = {COLOR_BLACK, COLOR_RED, COLOR_APPLE_GREEN, COLOR_APPLE_BLUE, COLOR_APPLE_YELLOW, COLOR_WHITE};
    for (int i = 0; i < 6; i++) {
        int cy = 10 + (i * 25);
        ui_draw_rounded_rect_filled(win, 15, cy, 30, 20, 3, colors[i]);
        
        if (current_color == colors[i]) {
            ui_draw_rect(win, 13, cy - 2, 34, 1, COLOR_DARK_TEXT);
            ui_draw_rect(win, 13, cy - 2 + 24, 34, 1, COLOR_DARK_TEXT);
            ui_draw_rect(win, 13, cy - 2, 1, 24, COLOR_DARK_TEXT);
            ui_draw_rect(win, 13 + 34, cy - 2, 1, 24, COLOR_DARK_TEXT);
        }
    }

    ui_draw_rounded_rect_filled(win, 12, 230 - 65, 36, 20, 4, COLOR_DARK_BORDER);
    ui_draw_string(win, 18, 230 - 58, "CLR", COLOR_DARK_TEXT);
    
    ui_draw_rounded_rect_filled(win, 12, 230 - 40, 36, 20, 4, COLOR_DARK_BORDER);
    ui_draw_string(win, 18, 230 - 33, "SAV", COLOR_DARK_TEXT);

    // Draw canvas content
    if (canvas_buffer) {
        for (int y = 0; y < CANVAS_H; y++) {
            for (int x = 0; x < CANVAS_W; x++) {
                uint32_t color = canvas_buffer[y * CANVAS_W + x];
                ui_draw_rect(win, canvas_x + x, canvas_y + y, 1, 1, color);
            }
        }
    }
}

static void paint_put_brush(ui_window_t win, int cx, int cy, int *min_x, int *min_y, int *max_x, int *max_y) {
    if (!canvas_buffer) return;
    for (int dy = 0; dy < 2; dy++) {
        for (int dx = 0; dx < 2; dx++) {
            int px = cx + dx;
            int py = cy + dy;
            if (px >= 0 && px < CANVAS_W && py >= 0 && py < CANVAS_H) {
                canvas_buffer[py * CANVAS_W + px] = current_color;
                ui_draw_rect(win, 60 + px, 0 + py, 1, 1, current_color);
                
                if (px < *min_x) *min_x = px;
                if (py < *min_y) *min_y = py;
                if (px > *max_x) *max_x = px;
                if (py > *max_y) *max_y = py;
            }
        }
    }
}

void paint_handle_mouse(ui_window_t win, int x, int y) {
    int cx = x - 60;
    int cy = y;

    if (cx < 0 || cx >= CANVAS_W || cy < 0 || cy >= CANVAS_H) {
        last_mx = -1;
        return;
    }

    int min_x = CANVAS_W, min_y = CANVAS_H;
    int max_x = -1, max_y = -1;

    if (last_mx == -1) {
        paint_put_brush(win, cx, cy, &min_x, &min_y, &max_x, &max_y);
    } else {
        int x0 = last_mx, y0 = last_my;
        int x1 = cx, y1 = cy;
        int dx = (x1 - x0 > 0) ? (x1 - x0) : (x0 - x1);
        int dy = (y1 - y0 > 0) ? (y1 - y0) : (y0 - y1);
        int sx = x0 < x1 ? 1 : -1;
        int sy = y0 < y1 ? 1 : -1;
        int err = dx - dy;

        while (1) {
            paint_put_brush(win, x0, y0, &min_x, &min_y, &max_x, &max_y);
            if (x0 == x1 && y0 == y1) break;
            int e2 = 2 * err;
            if (e2 > -dy) { err -= dy; x0 += sx; }
            if (e2 < dx) { err += dx; y0 += sy; }
        }
    }
    
    if (min_x <= max_x && min_y <= max_y) {
        ui_mark_dirty(win, 60 + min_x, 0 + min_y, (max_x - min_x) + 1, (max_y - min_y) + 1);
    }

    last_mx = cx;
    last_my = cy;
}

void paint_reset_last_pos(void) {
    last_mx = -1;
    last_my = -1;
}


static void wm_show_message(const char *title, const char *msg) {

}

static void paint_save(const char *path) {
    int fd = sys_open(path, "w");
    if (fd >= 0) {
        uint32_t header[3] = {PAINT_MAGIC, CANVAS_W, CANVAS_H};
        sys_write_fs(fd, (char*)header, sizeof(header));
        sys_write_fs(fd, (char*)canvas_buffer, CANVAS_W * CANVAS_H * sizeof(uint32_t));
        sys_close(fd);
        wm_show_message("Paint", "Image saved.");
    }
}

void paint_load(const char *path) {
    paint_strcpy(current_file_path, path);
    int fd = sys_open(path, "r");
    if (fd >= 0) {
        uint32_t header[3];
        if (sys_read(fd, (char*)header, sizeof(header)) == sizeof(header)) {
            if (header[0] == PAINT_MAGIC) {
                sys_read(fd, (char*)canvas_buffer, CANVAS_W * CANVAS_H * sizeof(uint32_t));
            }
        }
        sys_close(fd);
    }
}

static void paint_click(ui_window_t win, int x, int y) {
    // Check Buttons
    if (x >= 12 && x < 48) {
        if (y >= 230 - 65 && y < 230 - 45) {
            paint_reset();
            paint_paint(win);
            ui_mark_dirty(win, 0, 0, 380, 230);
            return;
        }
        if (y >= 230 - 40 && y < 230 - 20) {
            paint_save(current_file_path);
            return;
        }
    }

    // Check Palette
    if (x >= 15 && x < 45) {
        for (int i = 0; i < 6; i++) {
            int cy = 10 + (i * 25);
            if (y >= cy && y < cy + 20) {
                uint32_t colors[] = {COLOR_BLACK, COLOR_RED, COLOR_APPLE_GREEN, COLOR_APPLE_BLUE, COLOR_APPLE_YELLOW, COLOR_WHITE};
                current_color = colors[i];
                paint_paint(win);
                ui_mark_dirty(win, 0, 0, 380, 230);
                return;
            }
        }
    }
    paint_handle_mouse(win, x, y);
}

int main(int argc, char **argv) {
    ui_window_t win = ui_window_create("Paint", 150, 100, 380, 260);
    if (!win) return 1;

    canvas_buffer = malloc(CANVAS_W * CANVAS_H * sizeof(uint32_t));
    if (!canvas_buffer) return 1;

    paint_reset();

    if (argc > 1) {
        paint_load(argv[1]);
    }

    gui_event_t ev;
    while (1) {
        if (ui_get_event(win, &ev)) {
            if (ev.type == GUI_EVENT_PAINT) {
                paint_paint(win);
                ui_mark_dirty(win, 0, 0, 380, 240);
            } else if (ev.type == GUI_EVENT_CLICK) {
                paint_click(win, ev.arg1, ev.arg2);
            } else if (ev.type == GUI_EVENT_MOUSE_DOWN) {
                paint_handle_mouse(win, ev.arg1, ev.arg2);
            } else if (ev.type == GUI_EVENT_MOUSE_UP) {
                paint_reset_last_pos();
            } else if (ev.type == GUI_EVENT_MOUSE_MOVE) {
                if (ev.arg3 & 0x01) { // Left button down
                    paint_handle_mouse(win, ev.arg1, ev.arg2);
                } else {
                    paint_reset_last_pos();
                }
            } else if (ev.type == GUI_EVENT_CLOSE) {
                sys_exit(0);
            }
        }
    }
    return 0;
}