#include "drawing.h"
#include "multiboot.h"
#include "string.h"
#include "heap.h"
#include "vmm.h"
#include "font.h"
#define TRANSPARENT_COLOR 0xFF00FF

uint32_t* fb_ptr = 0;
uint32_t* backbuffer = 0;
uint32_t screen_width = 0;
uint32_t screen_height = 0;

void draw_init(struct multiboot_info* mbi) {
    if (!(mbi->flags & (1 << 12))) {
        return;
    }

    uint32_t fb_phys = (uint32_t)mbi->framebuffer_addr;
    uint32_t width = mbi->framebuffer_width;
    uint32_t height = mbi->framebuffer_height;

    uint32_t size = width * height * 4;
    uint32_t num_pages = (size + 0xFFF) / 0x1000;

    for (uint32_t i = 0; i < num_pages; i++) {
        vmm_map_page(fb_phys + (i * 0x1000),
                     fb_phys + (i * 0x1000),
                     0x3 | 0x10);
    }

    fb_ptr = (uint32_t*)fb_phys;
    screen_width = width;
    screen_height = height;

    backbuffer = (uint32_t*)kmalloc(1024 * 768 * sizeof(uint32_t));
}

void put_pixel(int x, int y, uint32_t color) {
    if (x >= 0 && x < (int)screen_width && y >= 0 && y < (int)screen_height) {
        backbuffer[y * screen_width + x] = color;
    }
}

void draw_rect(int x, int y, int w, int h, uint32_t color) {
    for (int i = y; i < y + h; i++) {
        for (int j = x; j < x + w; j++) {
            put_pixel(j, i, color);
        }
    }
}

void clear_screen(uint32_t color) {
    for (uint32_t i = 0; i < screen_width * screen_height; i++) {
        fb_ptr[i] = color;
    }
}

#pragma pack(push, 1)
struct bmp_header {
    uint16_t type;
    uint32_t size;
    uint16_t reserved1;
    uint16_t reserved2;
    uint32_t offset;
};

struct dib_header {
    uint32_t size;
    int32_t  width;
    int32_t  height;
    uint16_t planes;
    uint16_t bpp;
    uint32_t compression;
    uint32_t image_size;
    int32_t  x_ppm;
    int32_t  y_ppm;
    uint32_t colors_used;
    uint32_t colors_important;
};
#pragma pack(pop)

void draw_bmp(uint8_t* data, int x, int y) {
    struct bmp_header* bh = (struct bmp_header*)data;
    struct dib_header* dh = (struct dib_header*)(data + sizeof(struct bmp_header));

    int w = dh->width;
    int h = dh->height;

    int row_size = (w * 3 + 3) & ~3;
    uint8_t* pixels = data + bh->offset;

    for (int i = 0; i < h; i++) {
        int draw_y = y + (h - 1 - i);

        for (int j = 0; j < w; j++) {
            uint8_t* pixel_ptr = pixels + (i * row_size) + (j * 3);
            uint8_t b = pixel_ptr[0];
            uint8_t g = pixel_ptr[1];
            uint8_t r = pixel_ptr[2];

            uint32_t color = (r << 16) | (g << 8) | b;

            put_pixel(x + j, draw_y, color);
        }
    }
}

void draw_cursor_sprite(uint8_t* bmp_data, int x, int y) {
    struct bmp_header* bh = (struct bmp_header*)bmp_data;
    struct dib_header* dh = (struct dib_header*)(bmp_data + sizeof(struct bmp_header));

    uint8_t* pixels = bmp_data + bh->offset;
    int w = dh->width;
    int h = dh->height;
    int row_size = (w * 3 + 3) & ~3;

    for (int i = 0; i < h; i++) {
        int draw_y = y + (h - 1 - i);
        for (int j = 0; j < w; j++) {
            uint8_t* p = pixels + (i * row_size) + (j * 3);
            uint32_t color = (p[2] << 16) | (p[1] << 8) | p[0];

            if (color != TRANSPARENT_COLOR) {
                put_pixel(x + j, draw_y, color);
            }
        }
    }
}

void put_char(char c, int x, int y, uint32_t color) {
    if (c < 32 || c > 126) c = 32;

    const uint8_t* glyph = font8x8[c - 32];

    for (int i = 0; i < 8; i++) {
        for (int j = 0; j < 8; j++) {
            if (glyph[i] & (1 << (7 - j))) {
                put_pixel(x + j, y + i, color);
            }
        }
    }
}

void draw_string(const char* str, int x, int y, uint32_t color) {
    int cursor_x = x;
    int cursor_y = y;

    while (*str != '\0') {
        if (*str == '\n') {
            cursor_x = x;
            cursor_y += 10;
        } else {
            put_char(*str, cursor_x, cursor_y, color);
            cursor_x += 9;
        }
        str++;
    }
}

void flip_buffer() {
    memcpy((void*)fb_ptr, (void*)backbuffer, 1024 * 768 * 4);
}
