#include "exception.h"

#include <stdarg.h>
#include <stdlib.h>
#include <string.h>

//// GLOBAL ERROR STATE ////

static exception_t exception_type;
static char *exception_error;

//// EVALUATION ERROR HANDLING ////

void exception_set(exception_t type, char *message) {
    free(exception_error);

    exception_type = type;
    exception_error = strdup(message);
}

void exception_set_format(exception_t type, char *format, ...) {
    va_list args;
    va_start(args, format);
    int result = vsnprintf(NULL, 0, format, args);
    va_end(args);
    if (result < 0) {
        fprintf(stderr, "failed to format exception message with format '%s'\n", format);
        abort();
    }

    char buf[result + 1];
    va_start(args, format);
    vsprintf(buf, format, args);
    va_end(args);
    exception_set(type, buf);
}
void exception_clear() {
    free(exception_error);

    exception_type = EXC_NONE;
    exception_error = NULL;
}

bool exception_occurred() {
    return exception_type != EXC_NONE;
}

static char *exception_type_to_str(exception_t type) {
    switch (type) {
        case EXC_NONE:          return "<none>";
        case EXC_SYNTAX_ERROR:  return "SyntaxError";
        case EXC_NAME_ERROR:    return "NameError";
        case EXC_TYPE_ERROR:    return "TypeError";
        case EXC_VALUE_ERROR:   return "ValueError";
        case EXC_INDEX_ERROR:   return "IndexError";
        case EXC_KEY_ERROR:     return "KeyError";
        case EXC_MEMORY_ERROR:  return "MemoryError";
        case EXC_INTERNAL:      return "<internal>";
    }
    return "<unknown>";
}
void exception_print(FILE *stream) {
    if (exception_occurred()) {
        fprintf(stream, "%s: %s\n",
                exception_type_to_str(exception_type), exception_error);
    }
}