#include <stdint.h>
#include <stddef.h>
#include <stdarg.h>

// --- VGA Hardware Constants ---
static const size_t VGA_WIDTH = 80;
static const size_t VGA_HEIGHT = 25;
uint16_t* vga_buffer = (uint16_t*) 0xB8000;

// System State
size_t terminal_row = 0;
size_t terminal_col = 0;
uint8_t terminal_color = 7; 
int is_shift_pressed = 0;

// --- Low Level I/O ---

static inline void outb(uint16_t port, uint8_t val) {
    asm volatile ( "outb %0, %1" : : "a"(val), "Nd"(port) );
}

static inline uint8_t inb(uint16_t port) {
    uint8_t ret;
    asm volatile ( "inb %1, %0" : "=a"(ret) : "Nd"(port) );
    return ret;
}

void update_cursor(int x, int y) {
    uint16_t pos = y * VGA_WIDTH + x;
    outb(0x3D4, 0x0F);
    outb(0x3D5, (uint8_t) (pos & 0xFF));
    outb(0x3D4, 0x0E);
    outb(0x3D5, (uint8_t) ((pos >> 8) & 0xFF));
}

// --- Memory & Scrolling ---

void scroll() {
    // Move every row up by one
    for (size_t y = 0; y < VGA_HEIGHT - 1; y++) {
        for (size_t x = 0; x < VGA_WIDTH; x++) {
            vga_buffer[y * VGA_WIDTH + x] = vga_buffer[(y + 1) * VGA_WIDTH + x];
        }
    }
    // Clear the last row
    for (size_t x = 0; x < VGA_WIDTH; x++) {
        vga_buffer[(VGA_HEIGHT - 1) * VGA_WIDTH + x] = (uint16_t)' ' | (uint16_t)terminal_color << 8;
    }
    terminal_row = VGA_HEIGHT - 1;
}

// --- String Utilities ---

int strcmp(const char* s1, const char* s2) {
    while (*s1 && (*s1 == *s2)) { s1++; s2++; }
    return *(unsigned char*)s1 - *(unsigned char*)s2;
}

char* itoa(int value, char* str, int base) {
    char *rc, *ptr, *low;
    if (base < 2 || base > 36) return str;
    rc = ptr = str;
    if (value < 0 && base == 10) *ptr++ = '-';
    low = ptr;
    int v = (value < 0) ? -value : value;
    do {
        *ptr++ = "0123456789abcdefghijklmnopqrstuvwxyz"[v % base];
        v /= base;
    } while (v);
    *ptr-- = '\0';
    while (low < ptr) { char tmp = *low; *low++ = *ptr; *ptr-- = tmp; }
    return rc;
}

// --- Terminal Output ---

void putchar(char c) {
    if (c == '\n') {
        terminal_col = 0;
        terminal_row++;
    } else {
        vga_buffer[terminal_row * VGA_WIDTH + terminal_col] = (uint16_t) c | (uint16_t) terminal_color << 8;
        if (++terminal_col == VGA_WIDTH) {
            terminal_col = 0;
            terminal_row++;
        }
    }

    if (terminal_row >= VGA_HEIGHT) {
        scroll();
    }
    update_cursor(terminal_col, terminal_row);
}

void printf(const char* format, ...) {
    va_list args;
    va_start(args, format);
    for (int i = 0; format[i] != '\0'; i++) {
        if (format[i] == '%') {
            i++;
            if (format[i] == 's') {
                char* s = va_arg(args, char*);
                while (*s) putchar(*s++);
            } else if (format[i] == 'd') {
                char buf[32]; itoa(va_arg(args, int), buf, 10);
                for (int j = 0; buf[j]; j++) putchar(buf[j]);
            }
        } else {
            putchar(format[i]);
        }
    }
    va_end(args);
}

// --- Keyboard Logic ---

char getchar() {
    static unsigned char normal_map[128] = {
        0,  27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b',
        '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n',
        0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, '\\',
        'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, '*', 0, ' '
    };
    static unsigned char shift_map[128] = {
        0,  27, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b',
        '\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n',
        0, 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '\"', '~', 0, '|',
        'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', 0, '*', 0, ' '
    };

    while (1) {
        if (inb(0x64) & 0x01) {
            uint8_t scancode = inb(0x60);
            if (scancode == 0x2A || scancode == 0x36) is_shift_pressed = 1;
            else if (scancode == 0xAA || scancode == 0xB6) is_shift_pressed = 0;
            else if (scancode < 128) {
                unsigned char c = is_shift_pressed ? shift_map[scancode] : normal_map[scancode];
                if (c != 0) return c;
            }
        }
    }
}

void scanf(char* buffer) {
    int i = 0;
    while (1) {
        char c = getchar();
        if (c == '\n') {
            buffer[i] = '\0';
            putchar('\n');
            break;
        } else if (c == '\b' && i > 0) {
            i--;
            if (terminal_col > 0) terminal_col--;
            vga_buffer[terminal_row * VGA_WIDTH + terminal_col] = (uint16_t)' ' | (uint16_t)terminal_color << 8;
            update_cursor(terminal_col, terminal_row);
        } else if (c != '\b') {
            buffer[i++] = c;
            putchar(c);
        }
    }
}

// --- Main Loop ---

void kernel_main(void) {
    for (size_t i = 0; i < VGA_WIDTH * VGA_HEIGHT; i++) vga_buffer[i] = (uint16_t)' ' | (uint16_t)7 << 8;
    update_cursor(0, 0);

    printf("BUGS-1 A-1-i (Study Edition)\n");
    printf("Testing and Study Edition, features unverified.\n");

    while (1) {
        printf("BUGS > ");
        char input[64];
        scanf(input);

        if (strcmp(input, "about") == 0){
            printf("Buggy Unverified Greatish Script - Implementation 1\n");
            printf("Version: BUGS-1 A-1-i \"Study Edition\"\n");
            printf("Made with Gemini, customized and compiled by P7MJ\n");
        } else if (strcmp(input, "clear") == 0) {
            for (size_t i = 0; i < VGA_WIDTH * VGA_HEIGHT; i++) vga_buffer[i] = (uint16_t)' ' | (uint16_t)7 << 8;
            terminal_row = 0; terminal_col = 0;
            update_cursor(0,0);
        } else if (strcmp(input, "color") == 0) {
            terminal_color = (terminal_color + 1) % 15;
            if (terminal_color == 0) terminal_color = 1;
            printf("Text color changed!\n");
        } else if (strcmp(input, "help") == 0) {
            printf("BUILT-IN COMMANDS:\nabout clear color help poweroff test-symbols\n");
        } else if (strcmp(input, "poweroff") == 0) {
            printf("Maybe some day I'll do this.\n");
        } else if (strcmp(input, "test-symbols") == 0) {
            printf("Symbols: !@#$%%^&*()_+\n");
        }
    }
}