// Parser de expresiones lógicas: el ejemplo de la clase.
//
//   or_rule  → and_rule ("or" and_rule)*
//   and_rule → not_rule ("and" not_rule)*
//   not_rule → "not" not_rule | atom
//   atom     → "true" | "false" | "(" or_rule ")"
//
// Usa el analizador léxico de lab/: and, or, not, true y false le llegan como TOKEN_NAME.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "lexer.h"

typedef enum { LOGIC_VALUE, LOGIC_NOT, LOGIC_AND, LOGIC_OR } LogicKind;

typedef struct logic Logic;

struct logic {
    LogicKind kind;
    int value;      // solo en LOGIC_VALUE
    Logic *left;    // LOGIC_NOT usa solo left
    Logic *right;
};

typedef struct {
    int line;
    int column;
    char message[128];
} LogicError;

typedef struct {
    Lexer *lexer;
    Token current;
    LogicError *error;
} Parser;

static void logic_free(Logic *node) {
    if (node == NULL) {
        return;
    }
    logic_free(node->left);
    logic_free(node->right);
    free(node);
}

// Toma posesión de los hijos, incluso si falla.
static Logic *logic_new(LogicKind kind, int value, Logic *left, Logic *right) {
    Logic *node = malloc(sizeof *node);
    if (node == NULL) {
        logic_free(left);
        logic_free(right);
        return NULL;
    }
    node->kind = kind;
    node->value = value;
    node->left = left;
    node->right = right;
    return node;
}

static void advance(Parser *p) {
    p->current = lexer_next(p->lexer);
}

static int is_word(const Parser *p, const char *word) {
    return p->current.kind == TOKEN_NAME && strcmp(p->current.text, word) == 0;
}

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

static Logic *parse_or(Parser *p);

static Logic *parse_atom(Parser *p) {
    if (is_word(p, "true") || is_word(p, "false")) {
        Logic *leaf = logic_new(LOGIC_VALUE, is_word(p, "true"), NULL, NULL);
        if (leaf == NULL) {
            fail(p, "no hay memoria");
            return NULL;
        }
        advance(p);
        return leaf;
    }
    if (p->current.kind == TOKEN_LPAREN) {
        advance(p);
        Logic *inside = parse_or(p);
        if (inside == NULL) {
            return NULL;
        }
        if (p->current.kind != TOKEN_RPAREN) {
            fail(p, "se esperaba ')'");
            logic_free(inside);
            return NULL;
        }
        advance(p);
        return inside;
    }
    fail(p, "se esperaba true, false o '('");
    return NULL;
}

static Logic *parse_not(Parser *p) {
    if (!is_word(p, "not")) {
        return parse_atom(p);
    }
    advance(p);
    Logic *operand = parse_not(p);
    if (operand == NULL) {
        return NULL;
    }
    Logic *node = logic_new(LOGIC_NOT, 0, operand, NULL);
    if (node == NULL) {
        fail(p, "no hay memoria");
    }
    return node;
}

static Logic *parse_and(Parser *p) {
    Logic *left = parse_not(p);
    if (left == NULL) {
        return NULL;
    }
    while (is_word(p, "and")) {
        advance(p);
        Logic *right = parse_not(p);
        if (right == NULL) {
            logic_free(left);
            return NULL;
        }
        left = logic_new(LOGIC_AND, 0, left, right);
        if (left == NULL) {
            fail(p, "no hay memoria");
            return NULL;
        }
    }
    return left;
}

static Logic *parse_or(Parser *p) {
    Logic *left = parse_and(p);
    if (left == NULL) {
        return NULL;
    }
    while (is_word(p, "or")) {
        advance(p);
        Logic *right = parse_and(p);
        if (right == NULL) {
            logic_free(left);
            return NULL;
        }
        left = logic_new(LOGIC_OR, 0, left, right);
        if (left == NULL) {
            fail(p, "no hay memoria");
            return NULL;
        }
    }
    return left;
}

static Logic *parse_logic(const char *source, LogicError *error) {
    Parser parser = {.lexer = lexer_new(source), .error = error};
    if (parser.lexer == NULL) {
        snprintf(error->message, sizeof error->message, "no hay memoria");
        return NULL;
    }
    advance(&parser);
    Logic *tree = parse_or(&parser);
    if (tree != NULL && parser.current.kind != TOKEN_END) {
        fail(&parser, "sobra texto después de la expresión");
        logic_free(tree);
        tree = NULL;
    }
    lexer_free(parser.lexer);
    return tree;
}

// Escribe el árbol con todos sus paréntesis, para que se vea su forma.
static void logic_print(const Logic *node) {
    switch (node->kind) {
        case LOGIC_VALUE:
            printf("%s", node->value ? "true" : "false");
            break;
        case LOGIC_NOT:
            printf("(not ");
            logic_print(node->left);
            printf(")");
            break;
        case LOGIC_AND:
        case LOGIC_OR:
            printf("(");
            logic_print(node->left);
            printf(" %s ", node->kind == LOGIC_AND ? "and" : "or");
            logic_print(node->right);
            printf(")");
            break;
    }
}

static int logic_eval(const Logic *node) {
    switch (node->kind) {
        case LOGIC_VALUE: return node->value;
        case LOGIC_NOT: return !logic_eval(node->left);
        case LOGIC_AND: return logic_eval(node->left) && logic_eval(node->right);
        case LOGIC_OR: return logic_eval(node->left) || logic_eval(node->right);
    }
    return 0;
}

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "uso: %s 'expresión lógica'\n", argv[0]);
        return 1;
    }
    LogicError error;
    Logic *tree = parse_logic(argv[1], &error);
    if (tree == NULL) {
        printf("línea %d, columna %d: %s\n", error.line, error.column, error.message);
        return 1;
    }
    printf("árbol: ");
    logic_print(tree);
    printf("\nvalor: %s\n", logic_eval(tree) ? "true" : "false");
    logic_free(tree);
    return 0;
}
