#include "ast.h"
#include "astgen.h"
#include "intern_pool.h"
#include "sema.h"
#include "zir.h"

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// API:
// - code = 0: program successfully terminated.
// - code = 1: panicked, panic message in msg. Caller should free msg.
// - code = 2: interpreter error, error in msg. Caller should free msg.
static int zig0Run(const char* program, char** msg) {
    uint32_t len = (uint32_t)strlen(program);
    Ast ast = astParse(program, len);
    if (ast.has_error) {
        *msg = ast.err_msg;
        ast.err_msg = NULL;
        astDeinit(&ast);
        return 2;
    }

    fprintf(stderr, "tokens: %u, nodes: %u, first token: %s\n", ast.tokens.len,
        ast.nodes.len, tokenizerGetTagString(ast.tokens.tags[0]));

    Zir zir = astGen(&ast);
    astDeinit(&ast);

    if (zir.has_compile_errors) {
        const char err[] = "astgen failed";
        *msg = malloc(sizeof(err));
        memcpy(*msg, err, sizeof(err));
        zirDeinit(&zir);
        return 2;
    }

    fprintf(stderr, "zir: %u instructions, %u extra, %u string bytes\n",
        zir.inst_len, zir.extra_len, zir.string_bytes_len);

    InternPool ip = ipInit();
    Sema sema = semaInit(&ip, zir);
    Air air = semaAnalyze(&sema);
    semaDeinit(&sema);
    airDeinit(&air);
    ipDeinit(&ip);
    zirDeinit(&zir);
    return 0;
}

// API: run and:
// code = 3: abnormal error, expect something in stderr.
int zig0RunFile(const char* fname, char** msg) {
    FILE* f = fopen(fname, "r");
    if (f == NULL) {
        perror("fopen");
        return 3;
    }
    fseek(f, 0, SEEK_END);
    long fsizel = ftell(f);
    if (fsizel == -1) {
        perror("ftell");
        fclose(f);
        return 3;
    }
    unsigned long fsize = (unsigned long)fsizel;
    fseek(f, 0, SEEK_SET);

    char* program = malloc(fsize + 1);
    if (program == NULL) {
        perror("malloc");
        fclose(f);
        return 3;
    }

    size_t bytes_read = fread(program, 1, fsize, f);
    if (bytes_read < fsize) {
        if (ferror(f)) {
            perror("fread");
        } else {
            fprintf(stderr, "Unexpected end of file\n");
        }
        free(program);
        fclose(f);
        return 3;
    }
    fclose(f);
    program[fsize] = 0;

    int code = zig0Run(program, msg);
    free(program);
    return code;
}
