/* Keyboard Driver for CubeCactusOS
 * Handles keyboard interrupts and scancode translation
 */

#include "../kernel/kernel.h"

extern void outb(uint16_t port, uint8_t value);
extern uint8_t inb(uint16_t port);

#define KEYBOARD_DATA_PORT 0x60
#define KEYBOARD_STATUS_PORT 0x64
#define KEYBOARD_BUFFER_SIZE 256

/* US QWERTY keyboard layout scancode map */
static const char scancode_to_char[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, /* Control */
    'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`',
    0, /* Left shift */
    '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 
    0, /* Right shift */
    '*',
    0,  /* Alt */
    ' ',  /* Space bar */
    0,  /* Caps lock */
    0,  /* F1 */
    0, 0, 0, 0, 0, 0, 0, 0,
    0,  /* F10 */
    0,  /* Num lock*/
    0,  /* Scroll Lock */
    0,  /* Home key */
    0,  /* Up Arrow */
    0,  /* Page Up */
    '-',
    0,  /* Left Arrow */
    0,
    0,  /* Right Arrow */
    '+',
    0,  /* End key*/
    0,  /* Down Arrow */
    0,  /* Page Down */
    0,  /* Insert Key */
    0,  /* Delete Key */
    0, 0, 0,
    0,  /* F11 Key */
    0,  /* F12 Key */
    0,  /* All other keys are undefined */
};

/* Shifted characters */
static const char scancode_to_char_shifted[128] = {
    0,  27, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b',
    '\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n',
    0, /* Control */
    'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~',
    0, /* Left shift */
    '|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?',
    0, /* Right shift */
    '*',
    0,  /* Alt */
    ' ',  /* Space bar */
    0,  /* Caps lock */
};

/* Circular keyboard input buffer */
static char keyboard_buffer[KEYBOARD_BUFFER_SIZE];
static volatile int buffer_head = 0;
static volatile int buffer_tail = 0;
static volatile int shift_pressed = 0;

/* Interrupt mode flag */
static int interrupt_mode = 0;

/* Check if buffer has data */
int keyboard_has_data() {
    return buffer_head != buffer_tail;
}

/* Read character from buffer */
char keyboard_getchar() {
    if (!keyboard_has_data()) {
        return 0;
    }
    
    char c = keyboard_buffer[buffer_tail];
    buffer_tail = (buffer_tail + 1) % KEYBOARD_BUFFER_SIZE;
    return c;
}

/* Add character to buffer */
static void keyboard_buffer_add(char c) {
    int next_head = (buffer_head + 1) % KEYBOARD_BUFFER_SIZE;
    
    /* Check for buffer overflow */
    if (next_head == buffer_tail) {
        return; /* Buffer full, drop character */
    }
    
    keyboard_buffer[buffer_head] = c;
    buffer_head = next_head;
}

/* Keyboard interrupt handler */
void keyboard_interrupt_handler() {
    uint8_t scancode = inb(KEYBOARD_DATA_PORT);
    
    /* Handle key release (scancode with high bit set) */
    if (scancode & 0x80) {
        scancode &= 0x7F; /* Clear release bit */
        
        /* Track shift key release */
        if (scancode == 0x2A || scancode == 0x36) {
            shift_pressed = 0;
        }
        return;
    }
    
    /* Track shift key press */
    if (scancode == 0x2A || scancode == 0x36) {
        shift_pressed = 1;
        return;
    }
    
    /* Translate scancode to character */
    char c;
    if (shift_pressed && scancode < 128) {
        c = scancode_to_char_shifted[scancode];
    } else if (scancode < 128) {
        c = scancode_to_char[scancode];
    } else {
        return; /* Unknown scancode */
    }
    
    if (c != 0) {
        keyboard_buffer_add(c);
    }
}

/* Initialize keyboard */
void keyboard_init() {
    buffer_head = 0;
    buffer_tail = 0;
    shift_pressed = 0;
    interrupt_mode = 0;
    
    /* Buffer is already zero-initialized in BSS */
}

/* Enable interrupt mode after IDT is set up */
void keyboard_enable_interrupts() {
    interrupt_mode = 1;
}

/* IRQ handler called from interrupt */
void keyboard_irq_handler() {
    uint8_t scancode = inb(KEYBOARD_DATA_PORT);
    
    /* Handle key release */
    if (scancode & 0x80) {
        scancode &= 0x7F;
        if (scancode == 0x2A || scancode == 0x36) {
            shift_pressed = 0;
        }
        return;
    }
    
    /* Track shift */
    if (scancode == 0x2A || scancode == 0x36) {
        shift_pressed = 1;
        return;
    }
    
    /* Translate and buffer */
    if (scancode < 128) {
        char c = shift_pressed ? scancode_to_char_shifted[scancode] : scancode_to_char[scancode];
        if (c != 0) {
            keyboard_buffer_add(c);
        }
    }
}

/* Wait for and read a character (blocking) - SIMPLE VERSION */
char keyboard_getchar_blocking() {
    static int local_shift = 0;
    
    while (1) {
        /* Simple busy wait with yield */
        for (volatile int delay = 0; delay < 10000; delay++);
        
        /* Check status carefully */
        uint8_t status;
        __asm__ __volatile__("inb $0x64, %0" : "=a"(status));
        
        if (!(status & 0x01)) {
            continue; /* No data available */
        }
        
        /* Read scancode carefully */
        uint8_t scancode;
        __asm__ __volatile__("inb $0x60, %0" : "=a"(scancode));
        
        /* Ignore bad scancodes */
        if (scancode == 0 || scancode == 0xFF) {
            continue;
        }
        
        /* Key release */
        if (scancode & 0x80) {
            if ((scancode & 0x7F) == 0x2A || (scancode & 0x7F) == 0x36) {
                local_shift = 0;
            }
            continue;
        }
        
        /* Shift press */
        if (scancode == 0x2A || scancode == 0x36) {
            local_shift = 1;
            continue;
        }
        
        /* Translate to char */
        if (scancode < 128) {
            char c = local_shift ? scancode_to_char_shifted[scancode] : scancode_to_char[scancode];
            if (c != 0) {
                return c;
            }
        }
    }
}
