// Kyler Olsen
// YREA SLS
// Interpreter
// November 2025

#include <stdlib.h>

#include "sls/string.h"
#include "sls/interpreter.h"
#include "sls/lexer.h"
#include "sls/hash_table.h"
#include "sls/builtin.h"

static const size_t HASH_TABLE_BUCKET_COUNT = 256;

const char *STACK_TYPES_NAMES[] = {
    "Identifier",
    "64-bit Integer",
    "32-bit Integer",
    "16-bit Integer",
    "8-bit Integer",
    "64-bit U Integer",
    "32-bit U Integer",
    "16-bit U Integer",
    "8-bit U Integer",
    "Float",
    "Double",
    "Character",
    "Boolean",
    "Token String",
    "Callable",
};

const char *STACK_TYPES_TYPES[] = {
    "Identifier",
    "i64",
    "i32",
    "i16",
    "i8",
    "u64",
    "u32",
    "u16",
    "u8",
    "f32",
    "f64",
    "char",
    "bool",
    "TokenString",
    "Callable",
};

const size_t STACK_TYPE_COUNT = sizeof(STACK_TYPES_NAMES) / sizeof(*STACK_TYPES_NAMES);

static inline Boolean hash_table_put_funcs(HashTable *ht, SlsStr key, FunctionItem *item) {
    return hash_table_put(ht, key, (void *)item);
}

static inline FunctionItem *hash_table_get_funcs(const HashTable *ht, SlsStr key, FunctionItem *default_item) {
    return (FunctionItem*)hash_table_get(ht, key, (void *)default_item);
}

Boolean push_token(InterpreterState *interpreter_state, Token token) {
    StackType type;
    switch (token.type) {
    case TOKEN_EOF:
        return TRUE;
    case TOKEN_IDENTIFIER:
        type = STACK_IDENTIFIER;
        break;
    case TOKEN_INTEGER:
        switch (token.integer_literal.type) {
        case INTEGER_I64:
            type = STACK_I64;
            break;
        case INTEGER_I32:
            type = STACK_I32;
            break;
        case INTEGER_I16:
            type = STACK_I16;
            break;
        case INTEGER_I8:
            type = STACK_I8;
            break;
        case INTEGER_U64:
            type = STACK_U64;
            break;
        case INTEGER_U32:
            type = STACK_U32;
            break;
        case INTEGER_U16:
            type = STACK_U16;
            break;
        case INTEGER_U8:
            type = STACK_U8;
            break;
        }
        break;
    case TOKEN_FLOAT:
        type = STACK_FLOAT;
        break;
    case TOKEN_DOUBLE:
        type = STACK_DOUBLE;
        break;
    case TOKEN_CHARACTER:
        type = STACK_CHARACTER;
        break;
    case TOKEN_STRING:
        return FALSE;
    case TOKEN_BOOLEAN:
        type = STACK_BOOLEAN;
        break;
    case TOKEN_ARRAY:
        return FALSE;
    case TOKEN_TOKEN_STRING:
        type = STACK_TOKEN_STRING;
        break;
    case TOKEN_TYPE_TUPLE:
        return FALSE;
    }

    StackItem *item = (StackItem *)malloc(sizeof(StackItem));
    if (item == NULL) return FALSE;
    item->type = type;
    item->next = interpreter_state->stack;
    interpreter_state->stack = item;

    switch (type) {
    case STACK_IDENTIFIER:
        item->identifier = token.identifier;
        break;
    case STACK_I64:
        item->i64 = (int64_t)token.integer_literal.value;
        break;
    case STACK_I32:
        item->i32 = (int32_t)token.integer_literal.value;
        break;
    case STACK_I16:
        item->i16 = (int16_t)token.integer_literal.value;
        break;
    case STACK_I8:
        item->i8 = (int8_t)token.integer_literal.value;
        break;
    case STACK_U64:
        item->u64 = (uint64_t)token.integer_literal.value;
        break;
    case STACK_U32:
        item->u32 = (uint32_t)token.integer_literal.value;
        break;
    case STACK_U16:
        item->u16 = (uint16_t)token.integer_literal.value;
        break;
    case STACK_U8:
        item->u8 = (uint8_t)token.integer_literal.value;
        break;
    case STACK_FLOAT:
        item->f32 = token.float_literal;
        break;
    case STACK_DOUBLE:
        item->f64 = token.double_literal;
        break;
    case STACK_CHARACTER:
        item->character = token.character_literal;
        break;
    case STACK_BOOLEAN:
        item->boolean = token.boolean_literal;
        break;
    case STACK_TOKEN_STRING:
    case STACK_CALLABLE:
        item->token_string = copy_token_string(token.token_string);
        break;
    }

    return TRUE;
}

Boolean execute_func(InterpreterState *interpreter_state, SlsStr key) {
    FunctionItem *func = hash_table_get_funcs(interpreter_state->functions, key, NULL);
    if (func == NULL) return FALSE;
    switch (func->type) {
    case FUNCTION_BUILTIN:
        return func->builtin(interpreter_state);
    case FUNCTION_TOKEN_STRING:
        return execute_token_string(interpreter_state, func->token_string);
    }
    return FALSE;
}

Boolean execute_token_string(InterpreterState *interpreter_state, TokenString token_string) {
    Boolean return_value = FALSE;
    for (size_t i = 0; i < token_string.length; i++) {
        if (token_string.tokens[i].type == TOKEN_IDENTIFIER && !token_string.tokens[i].identifier.is_literal)
            return_value = execute_func(interpreter_state, token_string.tokens[i].identifier.name);
        else
            return_value = push_token(interpreter_state, token_string.tokens[i]);
        if (!return_value) break;
    }
    return return_value;
}

Boolean execute(InterpreterState *interpreter_state, LexerTokenResult *token) {
    if (token->result.type == TOKEN_IDENTIFIER && !token->result.identifier.is_literal)
        return execute_func(interpreter_state, token->result.identifier.name);
    else
        return push_token(interpreter_state, token->result);
}

InterpreterState *interpreter_create() {
    InterpreterState *interpreter_state = (InterpreterState *)malloc(sizeof(InterpreterState));
    if (interpreter_state == NULL)
        return NULL;
    interpreter_state->stack = NULL;
    interpreter_state->functions = init_hash_table(HASH_TABLE_BUCKET_COUNT);
    if (!load_builtins(interpreter_state)) {
        interpreter_delete(interpreter_state);
        return NULL;
    }
    return interpreter_state;
}

void clean_stack(StackItem *item) {
    if (item == NULL) return;
    if (item->type == STACK_TOKEN_STRING)
        clean_token_string(item->token_string);
    clean_stack(item->next);
    free(item);
}

void interpreter_delete(InterpreterState *interpreter_state) {
    clean_stack(interpreter_state->stack);
    del_hash_table(interpreter_state->functions);
    free(interpreter_state);
}
