// Parser descendente recursivo: una función por cada regla de la gramática.
//
//   program   → statement*
//   statement → "let" NAME "=" expr
//             | "print" expr
//             | "def" NAME "(" [ NAME ("," NAME)* ] ")" "=" expr
//   expr      → "if" expr "then" expr "else" expr | compare
//   compare   → sum [ ("<" | "<=" | ">" | ">=" | "==" | "!=") sum ]
//   sum       → term (("+" | "-") term)*
//   term      → unary (("*" | "/") unary)*
//   unary     → "-" unary | primary
//   primary   → NUMBER | NAME | NAME "(" [ expr ("," expr)* ] ")" | "(" expr ")"
//
// Cada parse_* mira p->current, consume lo que le toca y devuelve un árbol del que quien
// llama es owner. Si falla, deja el error llenado, libera lo suyo y devuelve NULL.
#include "parser.h"

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

#include "lexer.h"

typedef struct {
    Lexer *lexer;
    Token current;      // el token que toca mirar, todavía sin consumir
    ParseError *error;
} Parser;

static Expr *parse_expr(Parser *p);

// Pasa al token siguiente.
static void advance(Parser *p) {
    p->current = lexer_next(p->lexer);
}

static void syntax_error(Parser *p, Token token, const char *message) {
    p->error->line = token.line;
    p->error->column = token.column;
    const char *text = token.kind == TOKEN_ERROR ? token.text : message;
    snprintf(p->error->message, sizeof p->error->message, "%s", text);
}

static void no_memory(Parser *p) {
    syntax_error(p, p->current, "no hay memoria");
}

// Si el token actual es de ese tipo, lo consume y devuelve 1. Si no, anota el error y devuelve 0.
static int expect(Parser *p, TokenKind kind, const char *message) {
    if (p->current.kind != kind) {
        syntax_error(p, p->current, message);
        return 0;
    }
    advance(p);
    return 1;
}

// Combina dos árboles. expr_binary ya libera a sus hijos si no hay memoria.
static Expr *make_binary(Parser *p, BinaryOp op, Expr *left, Expr *right) {
    Expr *node = expr_binary(op, left, right);
    if (node == NULL) {
        syntax_error(p, p->current, "no hay memoria");
    }
    return node;
}

// if → "if" expr "then" expr "else" expr
static Expr *parse_if(Parser *p) {
    // --- Por escribir. El token actual es "if". ---
    // Si algo falla a la mitad, liberen lo que ya tengan y devuelvan NULL.
    syntax_error(p, p->current, "falta escribir parse_if");
    return NULL;
}

// call → NAME "(" [ expr ("," expr)* ] ")". El nombre ya se consumió, y el token actual es "(".
static Expr *parse_call(Parser *p, const char *name) {
    // --- Por escribir. Armen un arreglo Expr ** que crece con realloc, como en parse_parameters.
    // Si no hay argumentos, el arreglo queda en NULL. Si algo falla, liberen cada Expr ya leído y
    // el arreglo. Al final, expr_call se vuelve owner de todo. ---
    (void) name;
    syntax_error(p, p->current, "falta escribir parse_call");
    return NULL;
}

static Expr *parse_primary(Parser *p) {
    Token token = p->current;
    if (token.kind == TOKEN_NUMBER || token.kind == TOKEN_NAME) {
        advance(p);
        if (token.kind == TOKEN_NAME && p->current.kind == TOKEN_LPAREN) {
            return parse_call(p, token.text);
        }
        Expr *leaf = token.kind == TOKEN_NUMBER ? expr_number(token.number) : expr_variable(token.text);
        if (leaf == NULL) {
            syntax_error(p, token, "no hay memoria");
        }
        return leaf;
    }
    if (token.kind == TOKEN_LPAREN) {
        advance(p);
        Expr *inside = parse_expr(p);
        if (inside == NULL) {
            return NULL;
        }
        if (p->current.kind != TOKEN_RPAREN) {
            syntax_error(p, p->current, "se esperaba ')'");
            expr_free(inside);
            return NULL;
        }
        advance(p);
        return inside;
    }
    syntax_error(p, token, "se esperaba un número, un nombre o '('");
    return NULL;
}

static Expr *parse_unary(Parser *p) {
    if (p->current.kind != TOKEN_MINUS) {
        return parse_primary(p);
    }
    advance(p);
    Expr *operand = parse_unary(p);
    if (operand == NULL) {
        return NULL;
    }
    Expr *zero = expr_number(0.0);
    if (zero == NULL) {
        expr_free(operand);
        syntax_error(p, p->current, "no hay memoria");
        return NULL;
    }
    return make_binary(p, OP_SUB, zero, operand);
}

static Expr *parse_term(Parser *p) {
    Expr *left = parse_unary(p);
    while (left != NULL && (p->current.kind == TOKEN_STAR || p->current.kind == TOKEN_SLASH)) {
        BinaryOp op = p->current.kind == TOKEN_STAR ? OP_MUL : OP_DIV;
        advance(p);
        Expr *right = parse_unary(p);
        if (right == NULL) {
            expr_free(left);
            return NULL;
        }
        left = make_binary(p, op, left, right);
    }
    return left;
}

static Expr *parse_sum(Parser *p) {
    Expr *left = parse_term(p);
    while (left != NULL && (p->current.kind == TOKEN_PLUS || p->current.kind == TOKEN_MINUS)) {
        BinaryOp op = p->current.kind == TOKEN_PLUS ? OP_ADD : OP_SUB;
        advance(p);
        Expr *right = parse_term(p);
        if (right == NULL) {
            expr_free(left);
            return NULL;
        }
        left = make_binary(p, op, left, right);
    }
    return left;
}

// Si el token es un comparador, guarda su operación y devuelve 1.
static int comparison_op(TokenKind kind, BinaryOp *op) {
    switch (kind) {
        case TOKEN_LESS: *op = OP_LESS; return 1;
        case TOKEN_LESS_EQUAL: *op = OP_LESS_EQUAL; return 1;
        case TOKEN_GREATER: *op = OP_GREATER; return 1;
        case TOKEN_GREATER_EQUAL: *op = OP_GREATER_EQUAL; return 1;
        case TOKEN_EQUAL: *op = OP_EQUAL; return 1;
        case TOKEN_NOT_EQUAL: *op = OP_NOT_EQUAL; return 1;
        default: return 0;
    }
}

static Expr *parse_compare(Parser *p) {
    Expr *left = parse_sum(p);
    BinaryOp op;
    if (left == NULL || !comparison_op(p->current.kind, &op)) {
        return left;
    }
    advance(p);
    Expr *right = parse_sum(p);
    if (right == NULL) {
        expr_free(left);
        return NULL;
    }
    return make_binary(p, op, left, right);
}

static Expr *parse_expr(Parser *p) {
    if (p->current.kind == TOKEN_IF) {
        return parse_if(p);
    }
    return parse_compare(p);
}

// Lee los parámetros de un def, hasta el ")" incluido. El "(" ya se consumió.
// Devuelve 1 y entrega el arreglo, que es NULL si son 0. Si falla, no deja nada reservado.
static int parse_parameters(Parser *p, char ***params, int *param_count) {
    char **names = NULL;
    int count = 0;
    int capacity = 0;
    int more = p->current.kind != TOKEN_RPAREN;
    while (more) {
        if (p->current.kind != TOKEN_NAME) {
            syntax_error(p, p->current, "se esperaba el nombre de un parámetro");
            free_names(names, count);
            return 0;
        }
        if (count == capacity) {
            int new_capacity = capacity == 0 ? 4 : capacity * 2;
            char **grown = realloc(names, (size_t) new_capacity * sizeof *grown);
            if (grown == NULL) {
                no_memory(p);
                free_names(names, count);
                return 0;
            }
            names = grown;
            capacity = new_capacity;
        }
        names[count] = malloc(strlen(p->current.text) + 1);
        if (names[count] == NULL) {
            no_memory(p);
            free_names(names, count);
            return 0;
        }
        strcpy(names[count], p->current.text);
        count++;
        advance(p);
        more = p->current.kind == TOKEN_COMMA;
        if (more) {
            advance(p);
        }
    }
    if (!expect(p, TOKEN_RPAREN, "se esperaba ')'")) {
        free_names(names, count);
        return 0;
    }
    *params = names;
    *param_count = count;
    return 1;
}

// "let" NAME "=" expr
static Stmt *parse_let(Parser *p) {
    // --- Por escribir. Usen expect para los tokens fijos, y guarden el token del nombre antes de
    // pasarlo. Ojo: guardar un Token copia su text. ---
    syntax_error(p, p->current, "falta escribir parse_let");
    return NULL;
}

// "print" expr
static Stmt *parse_print(Parser *p) {
    // --- Por escribir. ---
    syntax_error(p, p->current, "falta escribir parse_print");
    return NULL;
}

// "def" NAME "(" [ NAME ("," NAME)* ] ")" "=" expr
static Stmt *parse_def(Parser *p) {
    advance(p);
    Token name = p->current;
    if (!expect(p, TOKEN_NAME, "se esperaba un nombre después de 'def'")) {
        return NULL;
    }
    if (!expect(p, TOKEN_LPAREN, "se esperaba '('")) {
        return NULL;
    }
    char **params;
    int param_count;
    if (!parse_parameters(p, &params, &param_count)) {
        return NULL;
    }
    // --- Por escribir: el "=", el cuerpo y stmt_def. Desde aquí, params ya es de ustedes: si algo
    // falla, se libera con free_names. Si stmt_def recibe params, se vuelve su owner. ---
    (void) name;
    free_names(params, param_count);
    syntax_error(p, p->current, "falta terminar parse_def");
    return NULL;
}

// statement → let | print | def. El primer token decide cuál es.
static Stmt *parse_statement(Parser *p) {
    switch (p->current.kind) {
    case TOKEN_LET:
        return parse_let(p);
    case TOKEN_PRINT:
        return parse_print(p);
    case TOKEN_DEF:
        return parse_def(p);
    default:
        syntax_error(p, p->current, "se esperaba 'let', 'print' o 'def'");
        return NULL;
    }
}

// Prepara el parser y lee el primer token. Devuelve 0 si no hay memoria.
static int open_parser(Parser *p, const char *source, ParseError *error) {
    error->line = 0;
    error->column = 0;
    error->message[0] = '\0';
    p->error = error;
    p->lexer = lexer_new(source);
    if (p->lexer == NULL) {
        snprintf(error->message, sizeof error->message, "no hay memoria");
        return 0;
    }
    advance(p);
    return 1;
}

Expr *parse_expression(const char *source, ParseError *error) {
    Parser p;
    if (!open_parser(&p, source, error)) {
        return NULL;
    }
    Expr *e = parse_expr(&p);
    if (e != NULL && p.current.kind != TOKEN_END) {
        syntax_error(&p, p.current, "sobra texto después de la expresión");
        expr_free(e);
        e = NULL;
    }
    lexer_free(p.lexer);
    return e;
}

Stmt *parse_program(const char *source, ParseError *error) {
    Parser p;
    if (!open_parser(&p, source, error)) {
        return NULL;
    }
    Stmt *first = NULL;
    Stmt **tail = &first;   // dónde se engancha la próxima instrucción
    while (p.current.kind != TOKEN_END) {
        Stmt *s = parse_statement(&p);
        if (s == NULL) {
            stmt_free(first);
            lexer_free(p.lexer);
            return NULL;
        }
        *tail = s;
        tail = &s->next;
    }
    lexer_free(p.lexer);
    return first;
}
