// Kyler Olsen
// YREA SLS
// Pico Main File
// December 2025

#include <stdio.h>
#include <stddef.h>
#include <string.h>

#include "pico/stdlib.h"
#include "pico/stdio_usb.h"
#include "hardware/uart.h"
#include "hardware/gpio.h"

#include "sls/meta.h"
#include "sls/bool.h"
#include "sls/string.h"
#include "sls/errors.h"
#include "sls/lexer.h"
#include "sls/interpreter.h"

// Pico onboard LED
#define LED_PIN 25

static const SlsStr PICO_REPL_NAME = SLS_STR_CONST("<PICO-REPL>");

void print_top_of_stack(InterpreterState *interpreter_state) {
    if (interpreter_state->stack == NULL) {
        printf("#0: <STACK IS EMPTY>\n");
        return;
    }
    switch (interpreter_state->stack->type) {
    case STACK_IDENTIFIER:
        printf("#0: ::%s\n", interpreter_state->stack->identifier.name.str);
        break;
    case STACK_I64:
        printf("#0: %ld:i64\n", interpreter_state->stack->i64);
        break;
    case STACK_I32:
        printf("#0: %d\n", interpreter_state->stack->i32);
        break;
    case STACK_I16:
        printf("#0: %d:i16\n", interpreter_state->stack->i16);
        break;
    case STACK_I8:
        printf("#0: %d:i8\n", interpreter_state->stack->i8);
        break;
    case STACK_U64:
        printf("#0: %lu:u64\n", interpreter_state->stack->u64);
        break;
    case STACK_U32:
        printf("#0: %u:u32\n", interpreter_state->stack->u32);
        break;
    case STACK_U16:
        printf("#0: %u:u16\n", interpreter_state->stack->u16);
        break;
    case STACK_U8:
        printf("#0: %u:u8\n", interpreter_state->stack->u8);
        break;
    case STACK_FLOAT:
        printf("#0: %f:f32\n", interpreter_state->stack->f32);
        break;
    case STACK_DOUBLE:
        printf("#0: %f\n", interpreter_state->stack->f64);
        break;
    case STACK_CHARACTER:
        printf("#0: %c\n", interpreter_state->stack->character);
        break;
    case STACK_BOOLEAN:
        if (interpreter_state->stack->boolean) printf("#0: TRUE\n");
        else printf("#0: FALSE\n");
        break;
    case STACK_TOKEN_STRING:
        printf("#0: <TOKEN STRING>\n");
        break;
    case STACK_CALLABLE:
        printf("#0: <CALLABLE>\n");
        break;
    }
}

void led_blink(int count) {
    for (int i = 0; i < count; i++) {
        gpio_put(LED_PIN, 1);
        sleep_ms(100);
        gpio_put(LED_PIN, 0);
        sleep_ms(100);
    }
}

int pico_repl() {
    // Initialize LED
    gpio_init(LED_PIN);
    gpio_set_dir(LED_PIN, GPIO_OUT);

    // Blink to show we're ready
    led_blink(3);

    // Wait for a key press to start the repl
    while (true) {
        int c = getchar_timeout_us(100000);
        if (c == PICO_ERROR_TIMEOUT) {
            continue;
        } else if (c == '\r' || c == '\n' || c == 8 || (c >= 32 && c < 128)) {
            printf("\n");
            break;
        }
    }

    led_blink(1);

    print_version();
    printf("===== YREA SLS PICO REPL =====\n");
    printf("Type `#exit` to exit.\n");
    fflush(stdout);

    LexerInfo lexer_info;
    InterpreterState *interpreter_state = interpreter_create();
    if (interpreter_state == NULL) {
        printf("ERROR: Failed to create interpreter!\n");
        return 1;
    }

    char buf[256];
    int buf_pos = 0;

    while (true) {
        // Read character by character for better embedded behavior
        int c = getchar_timeout_us(100000); // 100ms timeout

        if (c == PICO_ERROR_TIMEOUT) {
            continue;
        }

        if (c == '\r' || c == '\n') {
            if (buf_pos == 0) continue; // Empty line

            buf[buf_pos] = '\0';
            printf("\n"); // Echo newline

            if (strncmp(buf, "#exit", 5) == 0) {
                printf("Exiting REPL...\n");
                interpreter_delete(interpreter_state);
                led_blink(2);
                return 0;
            }

            SlsStr code = sls_str_malloc(buf, buf_pos);
            init_lexer(&lexer_info, PICO_REPL_NAME, code);
            LexerResult result = lexical_analysis(&lexer_info);

            if (result.type == SLS_ERROR) {
                printf("%s\n", result.error.message.str);
                sls_str_free(&result.error.message);
            } else {
                LexerTokenResult *head = result.result;
                while (head) {
                    if (head->type == SLS_ERROR) {
                        printf("%s\n", head->error.message.str);
                        break;
                    } else if (!execute(interpreter_state, head)) {
                        printf("A runtime error occurred!\n");
                        break;
                    }
                    head = head->next;
                }
                clean_token_result(result.result);
                print_top_of_stack(interpreter_state);
            }
            sls_str_free(&code);

            buf_pos = 0;

        } else if (c == 127 || c == 8) {
            // Backspace or DEL
            if (buf_pos > 0) {
                buf_pos--;
                printf("\b \b"); // Erase character from terminal
                fflush(stdout);
            }
        } else if (c >= 32 && c < 127 && buf_pos < 255) {
            // Printable character
            buf[buf_pos++] = (char)c;
            putchar(c); // Echo character
            fflush(stdout);
        }
    }

    interpreter_delete(interpreter_state);
    return 1;
}

int main() {
    // Initialize USB/UART stdio
    stdio_init_all();
    // Wait a bit for USB to enumerate
    sleep_ms(5000);

    printf("\n\n=============================\n");
    printf("YREA SLS on Raspberry Pi Pico\n");
    printf("=============================\n\n");
    printf("Press any key to begin...\n");

    return pico_repl();
}
