#include <stdint.h>
#include "font.h"
#include <stdarg.h>

#include "render.h"
#include "mm/memory.h"

uint32_t *fb;
uint32_t fb_width;
uint32_t fb_height;
uint32_t fb_pitch;




static int cursor_x = 0;
static int cursor_y = 0;

void put_pixel(int x, int y, uint32_t color) {
    fb[y * fb_pitch + x] = color;
}

static void scroll_up(void) {
    uint32_t line_pixels = FONT_HEIGHT * fb_pitch;
    uint32_t total_pixels = fb_height * fb_pitch;

    // Shift everything up by one line
    memmove(fb, fb + line_pixels, (total_pixels - line_pixels) * sizeof(uint32_t));

    // Clear the last line
    memset(fb + (total_pixels - line_pixels), 0, line_pixels * sizeof(uint32_t));

    cursor_y -= FONT_HEIGHT;
}

void draw_char_colored(int x, int y, char c, uint32_t color) {
    PSF1_Header *font = (PSF1_Header*)(uintptr_t)zap_light16_psf;
    uint8_t *glyphs = zap_light16_psf + sizeof(PSF1_Header);
    int charsize = font->charsize;
    uint8_t *glyph = glyphs + ((unsigned char)c * charsize);

    for (int row = 0; row < charsize; row++) {
        for (int col = 0; col < 8; col++) {
            if (glyph[row] & (0x80 >> col)) {
                fb[(y + row) * fb_pitch + (x + col)] = color;
            }
        }
    }
}

void putchar(char c) {
    if (c == '\n') {
        cursor_x = 0;
        cursor_y += FONT_HEIGHT;
        if (cursor_y + FONT_HEIGHT > fb_height) scroll_up();
        return;
    }

    draw_char_colored(cursor_x, cursor_y, c, 0xFFFFFF);
    cursor_x += FONT_WIDTH;

    if (cursor_x >= (int)fb_width) {
        cursor_x = 0;
        cursor_y += FONT_HEIGHT;
        if (cursor_y + FONT_HEIGHT > fb_height) scroll_up();
    }
}


void putchar_colored(char c, uint32_t color) {
    if (c == '\n') {
        cursor_x = 0;
        cursor_y += FONT_HEIGHT;
        if (cursor_y + FONT_HEIGHT > fb_height) scroll_up();
        return;
    }

    draw_char_colored(cursor_x, cursor_y, c, color);
    cursor_x += FONT_WIDTH;

    if (cursor_x >= (int)fb_width) {
        cursor_x = 0;
        cursor_y += FONT_HEIGHT;
        if (cursor_y + FONT_HEIGHT > fb_height) scroll_up();
    }
}

void print(const char *str) {
    while (*str) {
        putchar(*str++);
    }
}

void itoa(int value, char *buf, int base) {
    char *ptr = buf, *ptr1 = buf, tmp_char;
    int tmp_value;

    do {
        tmp_value = value;
        value /= base;
        *ptr++ = "0123456789ABCDEF"[tmp_value - value * base];
    } while (value);

    *ptr-- = '\0';

    while (ptr1 < ptr) {
        tmp_char = *ptr;
        *ptr-- = *ptr1;
        *ptr1++ = tmp_char;
    }
}

static inline uint32_t sample(uint32_t *src, int w, int h, int x, int y)
{
    if (x < 0) x = 0;
    if (y < 0) y = 0;
    if (x >= w) x = w - 1;
    if (y >= h) y = h - 1;

    return src[y * w + x];
}

void clear_screen(uint32_t color)
{
    uint32_t total = fb_width * fb_height;

    for (uint32_t i = 0; i < total; i++)
    {
        fb[i] = color;
    }
    cursor_y = 0;
}

void draw_image_bilinear(uint32_t *img, int x0, int y0, int new_w, int new_h)
{
    int src_w = img[0];
    int src_h = img[1];
    uint32_t *src = &img[2];

    // fixed-point scale factors (16.16)
    uint32_t x_scale = (src_w << 16) / new_w;
    uint32_t y_scale = (src_h << 16) / new_h;

    for (int y = 0; y < new_h; y++)
    {
        int sy_fp = y * y_scale;
        int sy = sy_fp >> 16;
        int fy = (sy_fp >> 8) & 0xFF;

        for (int x = 0; x < new_w; x++)
        {
            int sx_fp = x * x_scale;
            int sx = sx_fp >> 16;
            int fx = (sx_fp >> 8) & 0xFF;

            int x1 = sx + 1;
            int y1 = sy + 1;

            uint32_t c00 = sample(src, src_w, src_h, sx,  sy);
            uint32_t c10 = sample(src, src_w, src_h, x1,  sy);
            uint32_t c01 = sample(src, src_w, src_h, sx,  y1);
            uint32_t c11 = sample(src, src_w, src_h, x1,  y1);

            // unpack
            uint32_t a00 = (c00 >> 24) & 0xFF;
            uint32_t r00 = (c00 >> 16) & 0xFF;
            uint32_t g00 = (c00 >> 8)  & 0xFF;
            uint32_t b00 = (c00)       & 0xFF;

            uint32_t a10 = (c10 >> 24) & 0xFF;
            uint32_t r10 = (c10 >> 16) & 0xFF;
            uint32_t g10 = (c10 >> 8)  & 0xFF;
            uint32_t b10 = (c10)       & 0xFF;

            uint32_t a01 = (c01 >> 24) & 0xFF;
            uint32_t r01 = (c01 >> 16) & 0xFF;
            uint32_t g01 = (c01 >> 8)  & 0xFF;
            uint32_t b01 = (c01)       & 0xFF;

            uint32_t a11 = (c11 >> 24) & 0xFF;
            uint32_t r11 = (c11 >> 16) & 0xFF;
            uint32_t g11 = (c11 >> 8)  & 0xFF;
            uint32_t b11 = (c11)       & 0xFF;

            // bilinear interpolation weights
            uint32_t w00 = (256 - fx) * (256 - fy);
            uint32_t w10 = fx * (256 - fy);
            uint32_t w01 = (256 - fx) * fy;
            uint32_t w11 = fx * fy;

            uint32_t a = (a00*w00 + a10*w10 + a01*w01 + a11*w11) >> 16;
            uint32_t r = (r00*w00 + r10*w10 + r01*w01 + r11*w11) >> 16;
            uint32_t g = (g00*w00 + g10*w10 + g01*w01 + g11*w11) >> 16;
            uint32_t b = (b00*w00 + b10*w10 + b01*w01 + b11*w11) >> 16;

            int dst_x = x0 + x;
            int dst_y = y0 + y;

            if (dst_x < 0 || dst_y < 0 ||
                dst_x >= (int)fb_width ||
                dst_y >= (int)fb_height)
                continue;

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

            // alpha blend with framebuffer
            if (a < 255)
            {
                uint32_t dst = fb[dst_y * fb_pitch + dst_x];

                uint32_t dr = (dst >> 16) & 0xFF;
                uint32_t dg = (dst >> 8) & 0xFF;
                uint32_t db = dst & 0xFF;

                uint32_t inv = 255 - a;

                r = (r * a + dr * inv) / 255;
                g = (g * a + dg * inv) / 255;
                b = (b * a + db * inv) / 255;

                fb[dst_y * fb_pitch + dst_x] =
                    (0xFF << 24) | (r << 16) | (g << 8) | b;
            }
            else
            {
                fb[dst_y * fb_pitch + dst_x] =
                    (0xFF << 24) | (r << 16) | (g << 8) | b;
            }
        }
    }
}

void backspace() {
    if (cursor_x >= FONT_WIDTH) {
        cursor_x -= FONT_WIDTH;

        // clear the character cell
        for (int y = 0; y < FONT_HEIGHT; y++) {
            for (int x = 0; x < FONT_WIDTH; x++) {
                fb[(cursor_y + y) * fb_pitch + (cursor_x + x)] = 0;
            }
        }
    }
}

void draw_image(uint32_t *img, int x0, int y0)
{
    uint32_t w = img[0];
    uint32_t h = img[1];
    uint32_t *pixels = &img[2];

    for (uint32_t y = 0; y < h; y++) {
        for (uint32_t x = 0; x < w; x++) {

            int dst_x = x0 + x;
            int dst_y = y0 + y;

            // bounds check (VERY important in kernel)
            if (dst_x < 0 || dst_y < 0 ||
                dst_x >= (int)fb_width ||
                dst_y >= (int)fb_height)
                continue;

            uint32_t color = pixels[y * w + x];

            // alpha handling (TGA is ARGB in your parser)
            uint8_t a = color >> 24;

            // fully transparent → skip
            if (a == 0)
                continue;

            // optional: simple alpha blend
            if (a < 255) {
                uint32_t dst = fb[dst_y * fb_pitch + dst_x];

                uint8_t sr = (color >> 16) & 0xFF;
                uint8_t sg = (color >> 8) & 0xFF;
                uint8_t sb = (color) & 0xFF;

                uint8_t dr = (dst >> 16) & 0xFF;
                uint8_t dg = (dst >> 8) & 0xFF;
                uint8_t db = (dst) & 0xFF;

                uint8_t inv = 255 - a;

                uint32_t out =
                    ((sr * a + dr * inv) / 255) << 16 |
                    ((sg * a + dg * inv) / 255) << 8  |
                    ((sb * a + db * inv) / 255);

                fb[dst_y * fb_pitch + dst_x] = out;
            }
            else {
                // opaque fast path
                fb[dst_y * fb_pitch + dst_x] = color;
            }
        }
    }
}

/*void printf(const char *fmt, ...) {
    va_list args;
    va_start(args, fmt);

    char buffer[32];

    for (int i = 0; fmt[i] != 0; i++) {
        if (fmt[i] == '%') {
            i++;

            switch (fmt[i]) {
                case 'd': {
                    int val = va_arg(args, int);
                    itoa(val, buffer, 10);
                    print(buffer);
                    break;
                }
                case 'x': {
                    int val = va_arg(args, int);
                    itoa(val, buffer, 16);
                    print(buffer);
                    break;
                }
                case 's': {
                    char *str = va_arg(args, char*);
                    print(str);
                    break;
                }
                case 'c': {
                    char c = (char)va_arg(args, int);
                    putchar(c);
                    break;
                }
            }
        } else {
            putchar(fmt[i]);
        }
    }

    va_end(args);
}*/