#include "stmt.h"

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

static char *copy_string(const char *text) {
    char *copy = malloc(strlen(text) + 1);
    if (copy != NULL) {
        strcpy(copy, text);
    }
    return copy;
}

static Stmt *new_stmt(StmtKind kind) {
    Stmt *s = malloc(sizeof *s);
    if (s != NULL) {
        s->kind = kind;
        s->next = NULL;
    }
    return s;
}

Stmt *stmt_let(const char *name, Expr *value) {
    Stmt *s = value != NULL ? new_stmt(STMT_LET) : NULL;
    char *copy = s != NULL ? copy_string(name) : NULL;
    if (copy == NULL) {
        free(s);
        expr_free(value);
        return NULL;
    }
    s->as.let.name = copy;
    s->as.let.value = value;
    return s;
}

Stmt *stmt_print(Expr *value) {
    Stmt *s = value != NULL ? new_stmt(STMT_PRINT) : NULL;
    if (s == NULL) {
        expr_free(value);
        return NULL;
    }
    s->as.print.value = value;
    return s;
}

Stmt *stmt_def(const char *name, char **params, int param_count, Expr *body) {
    Stmt *s = body != NULL ? new_stmt(STMT_DEF) : NULL;
    char *copy = s != NULL ? copy_string(name) : NULL;
    if (copy == NULL) {
        free(s);
        free_names(params, param_count);
        expr_free(body);
        return NULL;
    }
    s->as.def.name = copy;
    s->as.def.params = params;
    s->as.def.param_count = param_count;
    s->as.def.body = body;
    return s;
}

void free_names(char **names, int count) {
    for (int i = 0; i < count; i++) {
        free(names[i]);
    }
    free(names);
}

static void free_one(Stmt *s) {
    switch (s->kind) {
    case STMT_LET:
        free(s->as.let.name);
        expr_free(s->as.let.value);
        break;
    case STMT_PRINT:
        expr_free(s->as.print.value);
        break;
    case STMT_DEF:
        free(s->as.def.name);
        free_names(s->as.def.params, s->as.def.param_count);
        expr_free(s->as.def.body);
        break;
    }
    free(s);
}

// Con un ciclo: un programa largo no debe gastar un marco por instrucción.
void stmt_free(Stmt *program) {
    while (program != NULL) {
        Stmt *next = program->next;
        free_one(program);
        program = next;
    }
}
