#include "sipi_electron.h"
#include <string.h>
#include <ctype.h>
#include <stdlib.h>

void lexer_iniciar(Lexer *lx, const char *fuente) {
    lx->fuente = fuente;
    lx->pos = 0;
    lx->linea = 1;
}

static char actual(Lexer *lx) { return lx->fuente[lx->pos]; }
static char siguiente_char(Lexer *lx) { return lx->fuente[lx->pos + 1]; }
static char avanzar(Lexer *lx) {
    char c = lx->fuente[lx->pos];
    if (c != '\0') lx->pos++;
    return c;
}

static Token token_simple(TipoToken tipo, int linea) {
    Token t; t.tipo = tipo; t.texto[0] = '\0'; t.numero = 0; t.linea = linea;
    return t;
}

typedef struct { const char *palabra; TipoToken tipo; } PalabraClave;

static PalabraClave PALABRAS[] = {
    {"programa", TOK_PROGRAMA}, {"decir", TOK_DECIR},
    {"variable", TOK_VARIABLE}, {"const", TOK_CONST},
    {"si", TOK_SI}, {"sino", TOK_SINO}, {"fin", TOK_FIN},
    {"mientras", TOK_MIENTRAS}, {"repetir", TOK_REPETIR}, {"veces", TOK_VECES},
    {"romper", TOK_ROMPER}, {"continuar", TOK_CONTINUAR},
    {"funcion", TOK_FUNCION}, {"devolver", TOK_DEVOLVER},
    {"llamar_valor", TOK_LLAMAR_VALOR}, {"llamar", TOK_LLAMAR},
    {"verdadero", TOK_VERDADERO}, {"falso", TOK_FALSO},
    {"y", TOK_Y}, {"o", TOK_O}, {"no", TOK_NO},
    {NULL, TOK_EOF}
};

Token lexer_siguiente(Lexer *lx) {
    /* saltar espacios y comentarios, pero respetar saltos de linea */
    for (;;) {
        char c = actual(lx);
        if (c == ' ' || c == '\t' || c == '\r') { avanzar(lx); continue; }
        if (c == '\n') { avanzar(lx); lx->linea++; return token_simple(TOK_NEWLINE, lx->linea - 1); }
        if (c == '/' && siguiente_char(lx) == '/') {
            while (actual(lx) != '\n' && actual(lx) != '\0') avanzar(lx);
            continue;
        }
        if (c == '/' && siguiente_char(lx) == '*') {
            avanzar(lx); avanzar(lx);
            while (!(actual(lx) == '*' && siguiente_char(lx) == '/') && actual(lx) != '\0') {
                if (actual(lx) == '\n') lx->linea++;
                avanzar(lx);
            }
            if (actual(lx) != '\0') { avanzar(lx); avanzar(lx); }
            continue;
        }
        break;
    }

    int linea = lx->linea;
    char c = actual(lx);
    if (c == '\0') return token_simple(TOK_EOF, linea);

    /* numeros */
    if (isdigit((unsigned char)c)) {
        size_t inicio = lx->pos;
        while (isdigit((unsigned char)actual(lx))) avanzar(lx);
        if (actual(lx) == '.' && isdigit((unsigned char)siguiente_char(lx))) {
            avanzar(lx);
            while (isdigit((unsigned char)actual(lx))) avanzar(lx);
        }
        size_t len = lx->pos - inicio;
        char buf[64]; if (len >= sizeof(buf)) len = sizeof(buf) - 1;
        memcpy(buf, lx->fuente + inicio, len); buf[len] = '\0';
        Token t = token_simple(TOK_NUMERO, linea);
        t.numero = atof(buf);
        return t;
    }

    /* texto entre comillas */
    if (c == '"') {
        avanzar(lx);
        size_t inicio = lx->pos;
        while (actual(lx) != '"' && actual(lx) != '\0') {
            if (actual(lx) == '\n') lx->linea++;
            avanzar(lx);
        }
        size_t len = lx->pos - inicio;
        if (actual(lx) == '"') avanzar(lx);
        Token t = token_simple(TOK_TEXTO, linea);
        if (len >= sizeof(t.texto)) len = sizeof(t.texto) - 1;
        memcpy(t.texto, lx->fuente + inicio, len); t.texto[len] = '\0';
        return t;
    }

    /* identificadores / palabras clave (aceptan letras con tilde y _) */
    if (isalpha((unsigned char)c) || c == '_' || (unsigned char)c >= 0x80) {
        size_t inicio = lx->pos;
        while (isalnum((unsigned char)actual(lx)) || actual(lx) == '_' || (unsigned char)actual(lx) >= 0x80)
            avanzar(lx);
        size_t len = lx->pos - inicio;
        char buf[128]; if (len >= sizeof(buf)) len = sizeof(buf) - 1;
        memcpy(buf, lx->fuente + inicio, len); buf[len] = '\0';

        for (int i = 0; PALABRAS[i].palabra != NULL; i++) {
            if (strcmp(buf, PALABRAS[i].palabra) == 0)
                return token_simple(PALABRAS[i].tipo, linea);
        }
        Token t = token_simple(TOK_IDENT, linea);
        strncpy(t.texto, buf, sizeof(t.texto) - 1);
        t.texto[sizeof(t.texto) - 1] = '\0';
        return t;
    }

    /* operadores y puntuacion */
    avanzar(lx);
    switch (c) {
        case '-':
            if (actual(lx) == '>') { avanzar(lx); return token_simple(TOK_FLECHA, linea); }
            return token_simple(TOK_MENOS, linea);
        case '+': return token_simple(TOK_MAS, linea);
        case '*': return token_simple(TOK_POR, linea);
        case '/': return token_simple(TOK_DIV, linea);
        case '%': return token_simple(TOK_MOD, linea);
        case '(': return token_simple(TOK_PAREN_ABRE, linea);
        case ')': return token_simple(TOK_PAREN_CIERRA, linea);
        case ',': return token_simple(TOK_COMA, linea);
        case ':': return token_simple(TOK_DOSPUNTOS, linea);
        case '=':
            if (actual(lx) == '=') { avanzar(lx); return token_simple(TOK_IGUAL_IGUAL, linea); }
            return token_simple(TOK_IGUAL, linea);
        case '!':
            if (actual(lx) == '=') { avanzar(lx); return token_simple(TOK_DISTINTO, linea); }
            return token_simple(TOK_ERROR, linea);
        case '<':
            if (actual(lx) == '=') { avanzar(lx); return token_simple(TOK_MENOR_IGUAL, linea); }
            return token_simple(TOK_MENOR, linea);
        case '>':
            if (actual(lx) == '=') { avanzar(lx); return token_simple(TOK_MAYOR_IGUAL, linea); }
            return token_simple(TOK_MAYOR, linea);
        default:
            return token_simple(TOK_ERROR, linea);
    }
}
