#include "tokenlist.h"
#include "error.h"
#include "lexer.h"
#include <stdlib.h>

error_t *tokenlist_alloc(tokenlist_t **output) {
    *output = nullptr;

    tokenlist_t *list = calloc(1, sizeof(tokenlist_t));
    if (list == nullptr)
        return err_allocation_failed;

    list->head = nullptr;
    list->tail = nullptr;

    *output = list;
    return nullptr;
}

error_t *tokenlist_entry_alloc(tokenlist_entry_t **output) {
    *output = nullptr;

    tokenlist_entry_t *entry = calloc(1, sizeof(tokenlist_entry_t));
    if (entry == nullptr)
        return err_allocation_failed;

    entry->next = nullptr;
    entry->prev = nullptr;

    *output = entry;
    return nullptr;
}

void tokenlist_append(tokenlist_t *list, tokenlist_entry_t *entry) {
    if (list->head == nullptr) {
        list->head = entry;
        list->tail = entry;
        entry->next = nullptr;
        entry->prev = nullptr;
    } else {
        entry->prev = list->tail;
        entry->next = nullptr;
        list->tail->next = entry;
        list->tail = entry;
    }
}

void tokenlist_entry_free(tokenlist_entry_t *entry) {
    lexer_token_cleanup(&entry->token);
    free(entry);
}

void tokenlist_free(tokenlist_t *list) {
    if (list == nullptr)
        return;

    tokenlist_entry_t *current = list->head;
    while (current) {
        tokenlist_entry_t *next = current->next;
        tokenlist_entry_free(current);
        current = next;
    }

    free(list);
}

error_t *tokenlist_fill(tokenlist_t *list, lexer_t *lex) {
    error_t *err = nullptr;
    lexer_token_t token = {};
    while ((err = lexer_next(lex, &token)) == nullptr) {
        tokenlist_entry_t *entry;
        err = tokenlist_entry_alloc(&entry);
        if (err) {
            lexer_token_cleanup(&token);
            return err;
        }
        entry->token = token;
        tokenlist_append(list, entry);
    }
    if (err != err_eof)
        return err;
    return nullptr;
}

bool is_trivia(tokenlist_entry_t *trivia) {
    switch (trivia->token.id) {
    case TOKEN_WHITESPACE:
    case TOKEN_COMMENT:
    case TOKEN_NEWLINE:
        return true;
    default:
        return false;
    }
}

tokenlist_entry_t *tokenlist_skip_trivia(tokenlist_entry_t *current) {
    while (current && is_trivia(current))
        current = current->next;
    return current;
}

tokenlist_entry_t *tokenlist_next(tokenlist_entry_t *current) {
    if (!current)
        return nullptr;
    return tokenlist_skip_trivia(current->next);
}