#include "sipi_electron.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>

#define MAX_VARS 256
#define MAX_FUNCS 64
#define MAX_PILA 64

typedef struct { char nombre[128]; Valor valor; } Variable;
typedef struct { char nombre[128]; Nodo *def; } Funcion;

typedef struct {
    Variable vars[MAX_VARS];
    int n_vars;
} Entorno;

static Entorno pila[MAX_PILA];
static int n_pila = 0;

static Funcion funciones[MAX_FUNCS];
static int n_funciones = 0;

typedef enum { FLUJO_NORMAL, FLUJO_ROMPER, FLUJO_CONTINUAR, FLUJO_DEVOLVER } Flujo;
static Flujo flujo_actual;
static Valor valor_devuelto;

/* ---------- utilidades de Valor ---------- */

static Valor v_numero(double n) { Valor v; v.tipo = V_NUMERO; v.numero = n; v.texto[0] = '\0'; return v; }
static Valor v_bool(int b) { Valor v; v.tipo = V_BOOL; v.numero = b ? 1 : 0; v.texto[0] = '\0'; return v; }
static Valor v_texto(const char *s) {
    Valor v; v.tipo = V_TEXTO; v.numero = 0;
    strncpy(v.texto, s, sizeof(v.texto) - 1); v.texto[sizeof(v.texto) - 1] = '\0';
    return v;
}
static Valor v_nulo(void) { Valor v; v.tipo = V_NULO; v.numero = 0; v.texto[0] = '\0'; return v; }

static int valor_es_verdadero(Valor v) {
    if (v.tipo == V_BOOL || v.tipo == V_NUMERO) return v.numero != 0;
    if (v.tipo == V_TEXTO) return v.texto[0] != '\0';
    return 0;
}

static void valor_a_texto(Valor v, char *buf, size_t n) {
    switch (v.tipo) {
        case V_NUMERO:
            if (v.numero == (long long)v.numero) snprintf(buf, n, "%lld", (long long)v.numero);
            else snprintf(buf, n, "%g", v.numero);
            break;
        case V_TEXTO: snprintf(buf, n, "%s", v.texto); break;
        case V_BOOL: snprintf(buf, n, "%s", v.numero != 0 ? "verdadero" : "falso"); break;
        default: snprintf(buf, n, "nulo"); break;
    }
}

/* ---------- entorno / variables ---------- */

static Entorno *tope(void) { return &pila[n_pila - 1]; }

static Variable *buscar_var_en(Entorno *e, const char *nombre) {
    for (int i = 0; i < e->n_vars; i++)
        if (strcmp(e->vars[i].nombre, nombre) == 0) return &e->vars[i];
    return NULL;
}

static Variable *buscar_var(const char *nombre) {
    /* busca primero en el entorno local, despues en el global (pila[0]) */
    Variable *v = buscar_var_en(tope(), nombre);
    if (v) return v;
    if (n_pila > 1) return buscar_var_en(&pila[0], nombre);
    return NULL;
}

static void definir_var(const char *nombre, Valor valor) {
    Entorno *e = tope();
    Variable *v = buscar_var_en(e, nombre);
    if (!v) {
        if (e->n_vars >= MAX_VARS) { fprintf(stderr, "Demasiadas variables\n"); exit(1); }
        v = &e->vars[e->n_vars++];
        strncpy(v->nombre, nombre, sizeof(v->nombre) - 1);
    }
    v->valor = valor;
}

static void asignar_var(const char *nombre, Valor valor) {
    Variable *v = buscar_var(nombre);
    if (!v) { fprintf(stderr, "Variable no definida: %s\n", nombre); exit(1); }
    v->valor = valor;
}

static Funcion *buscar_funcion(const char *nombre) {
    for (int i = 0; i < n_funciones; i++)
        if (strcmp(funciones[i].nombre, nombre) == 0) return &funciones[i];
    return NULL;
}

/* ---------- funciones incorporadas de electronica ---------- */

static int es_builtin(const char *nombre) {
    static const char *nombres[] = {
        "pin_modo", "pin_escribir", "pin_leer", "analogico_leer",
        "pwm_escribir", "esperar_ms", "esperar_segundos",
        "raiz_cuadrada", "azar_entre", "redondear", "absoluto", NULL
    };
    for (int i = 0; nombres[i]; i++) if (strcmp(nombre, nombres[i]) == 0) return 1;
    return 0;
}

static Valor llamar_builtin(const char *nombre, Valor *args, int n) {
    if (strcmp(nombre, "pin_modo") == 0 && n >= 2) {
        hal_pin_modo((int)args[0].numero, (int)args[1].numero);
        return v_nulo();
    }
    if (strcmp(nombre, "pin_escribir") == 0 && n >= 2) {
        hal_pin_escribir((int)args[0].numero, (int)args[1].numero);
        return v_nulo();
    }
    if (strcmp(nombre, "pin_leer") == 0 && n >= 1)
        return v_numero(hal_pin_leer((int)args[0].numero));
    if (strcmp(nombre, "analogico_leer") == 0 && n >= 1)
        return v_numero(hal_analogico_leer((int)args[0].numero));
    if (strcmp(nombre, "pwm_escribir") == 0 && n >= 2) {
        hal_pwm_escribir((int)args[0].numero, (int)args[1].numero);
        return v_nulo();
    }
    if (strcmp(nombre, "esperar_ms") == 0 && n >= 1) {
        hal_esperar_ms((int)args[0].numero);
        return v_nulo();
    }
    if (strcmp(nombre, "esperar_segundos") == 0 && n >= 1) {
        hal_esperar_ms((int)(args[0].numero * 1000));
        return v_nulo();
    }
    if (strcmp(nombre, "raiz_cuadrada") == 0 && n >= 1) return v_numero(sqrt(args[0].numero));
    if (strcmp(nombre, "redondear") == 0 && n >= 1) return v_numero(round(args[0].numero));
    if (strcmp(nombre, "absoluto") == 0 && n >= 1) return v_numero(fabs(args[0].numero));
    if (strcmp(nombre, "azar_entre") == 0 && n >= 2) {
        int a = (int)args[0].numero, b = (int)args[1].numero;
        if (b < a) { int t = a; a = b; b = t; }
        return v_numero(a + rand() % (b - a + 1));
    }
    return v_nulo();
}

/* ---------- decir con interpolacion "{variable}" ---------- */

static void ejecutar_decir(const char *texto) {
    const char *p = texto;
    while (*p) {
        if (*p == '{') {
            const char *cierre = strchr(p, '}');
            if (cierre) {
                char nombre[128]; size_t len = cierre - p - 1;
                if (len >= sizeof(nombre)) len = sizeof(nombre) - 1;
                memcpy(nombre, p + 1, len); nombre[len] = '\0';
                Variable *v = buscar_var(nombre);
                if (v) {
                    char buf[256]; valor_a_texto(v->valor, buf, sizeof(buf));
                    printf("%s", buf);
                } else {
                    printf("{%s}", nombre);
                }
                p = cierre + 1;
                continue;
            }
        }
        putchar(*p);
        p++;
    }
    putchar('\n');
}

/* ---------- evaluacion de expresiones ---------- */

static Valor evaluar(Nodo *n);
static Flujo ejecutar_bloque(Nodo *primero);

static Valor evaluar_llamada(Nodo *n) {
    Valor args[16]; int nargs = 0;
    for (Nodo *a = n->args; a && nargs < 16; a = a->siguiente)
        args[nargs++] = evaluar(a);

    if (es_builtin(n->texto)) return llamar_builtin(n->texto, args, nargs);

    Funcion *f = buscar_funcion(n->texto);
    if (!f) { fprintf(stderr, "Funcion no definida: %s\n", n->texto); exit(1); }

    if (n_pila >= MAX_PILA) { fprintf(stderr, "Demasiada recursion\n"); exit(1); }
    Entorno *e = &pila[n_pila++];
    e->n_vars = 0;
    int i = 0;
    for (Nodo *par = f->def->args; par; par = par->siguiente, i++)
        definir_var(par->texto, i < nargs ? args[i] : v_nulo());

    Flujo f_bloque = ejecutar_bloque(f->def->cuerpo);
    Valor resultado = (f_bloque == FLUJO_DEVOLVER) ? valor_devuelto : v_nulo();
    n_pila--;
    flujo_actual = FLUJO_NORMAL;
    return resultado;
}

static Valor evaluar(Nodo *n) {
    switch (n->tipo) {
        case N_NUMERO: return v_numero(n->numero);
        case N_TEXTO:  return v_texto(n->texto);
        case N_BOOL:   return v_bool((int)n->numero);
        case N_IDENT: {
            Variable *v = buscar_var(n->texto);
            if (!v) { fprintf(stderr, "Variable no definida: %s\n", n->texto); exit(1); }
            return v->valor;
        }
        case N_LLAMADA_EXPR: return evaluar_llamada(n);
        case N_UNARIA: {
            Valor a = evaluar(n->izq);
            if (n->operador == TOK_MENOS) return v_numero(-a.numero);
            if (n->operador == TOK_NO) return v_bool(!valor_es_verdadero(a));
            return v_nulo();
        }
        case N_BINARIA: {
            if (n->operador == TOK_Y) return v_bool(valor_es_verdadero(evaluar(n->izq)) && valor_es_verdadero(evaluar(n->der)));
            if (n->operador == TOK_O) return v_bool(valor_es_verdadero(evaluar(n->izq)) || valor_es_verdadero(evaluar(n->der)));
            Valor a = evaluar(n->izq), b = evaluar(n->der);
            if (n->operador == TOK_MAS && (a.tipo == V_TEXTO || b.tipo == V_TEXTO)) {
                char ba[256], bb[256], res[512];
                valor_a_texto(a, ba, sizeof(ba)); valor_a_texto(b, bb, sizeof(bb));
                snprintf(res, sizeof(res), "%s%s", ba, bb);
                return v_texto(res);
            }
            switch (n->operador) {
                case TOK_MAS: return v_numero(a.numero + b.numero);
                case TOK_MENOS: return v_numero(a.numero - b.numero);
                case TOK_POR: return v_numero(a.numero * b.numero);
                case TOK_DIV:
                    if (b.numero == 0) { fprintf(stderr, "Error: division por cero (linea %d)\n", n->linea); exit(1); }
                    return v_numero(a.numero / b.numero);
                case TOK_MOD: return v_numero(fmod(a.numero, b.numero));
                case TOK_MENOR: return v_bool(a.numero < b.numero);
                case TOK_MAYOR: return v_bool(a.numero > b.numero);
                case TOK_MENOR_IGUAL: return v_bool(a.numero <= b.numero);
                case TOK_MAYOR_IGUAL: return v_bool(a.numero >= b.numero);
                case TOK_IGUAL_IGUAL:
                    if (a.tipo == V_TEXTO || b.tipo == V_TEXTO) return v_bool(strcmp(a.texto, b.texto) == 0);
                    return v_bool(a.numero == b.numero);
                case TOK_DISTINTO:
                    if (a.tipo == V_TEXTO || b.tipo == V_TEXTO) return v_bool(strcmp(a.texto, b.texto) != 0);
                    return v_bool(a.numero != b.numero);
                default: return v_nulo();
            }
        }
        default: return v_nulo();
    }
}

/* ---------- ejecucion de sentencias ---------- */

static Flujo ejecutar_sentencia(Nodo *n) {
    switch (n->tipo) {
        case N_DECIR: {
            Valor v = evaluar(n->izq);
            if (v.tipo == V_TEXTO) ejecutar_decir(v.texto);
            else { char buf[256]; valor_a_texto(v, buf, sizeof(buf)); ejecutar_decir(buf); }
            return FLUJO_NORMAL;
        }
        case N_VARIABLE:
        case N_CONST:
            definir_var(n->texto, evaluar(n->izq));
            return FLUJO_NORMAL;
        case N_ASIGNAR:
            asignar_var(n->texto, evaluar(n->izq));
            return FLUJO_NORMAL;
        case N_SI:
            if (valor_es_verdadero(evaluar(n->izq))) return ejecutar_bloque(n->cuerpo);
            else if (n->cuerpo2) return ejecutar_bloque(n->cuerpo2);
            return FLUJO_NORMAL;
        case N_MIENTRAS:
            while (valor_es_verdadero(evaluar(n->izq))) {
                Flujo f = ejecutar_bloque(n->cuerpo);
                if (f == FLUJO_ROMPER) break;
                if (f == FLUJO_DEVOLVER) return f;
            }
            return FLUJO_NORMAL;
        case N_REPETIR: {
            int veces = (int)evaluar(n->izq).numero;
            for (int i = 0; i < veces; i++) {
                Flujo f = ejecutar_bloque(n->cuerpo);
                if (f == FLUJO_ROMPER) break;
                if (f == FLUJO_DEVOLVER) return f;
            }
            return FLUJO_NORMAL;
        }
        case N_FUNCION:
            if (n_funciones < MAX_FUNCS) {
                strncpy(funciones[n_funciones].nombre, n->texto, sizeof(funciones[n_funciones].nombre) - 1);
                funciones[n_funciones].nombre[sizeof(funciones[n_funciones].nombre) - 1] = '\0';
                funciones[n_funciones].def = n;
                n_funciones++;
            }
            return FLUJO_NORMAL;
        case N_DEVOLVER:
            valor_devuelto = n->izq ? evaluar(n->izq) : v_nulo();
            return FLUJO_DEVOLVER;
        case N_LLAMAR:
            evaluar(n->izq);
            return FLUJO_NORMAL;
        case N_LLAMAR_VALOR:
            definir_var(n->texto, evaluar(n->izq));
            return FLUJO_NORMAL;
        case N_ROMPER: return FLUJO_ROMPER;
        case N_CONTINUAR: return FLUJO_CONTINUAR;
        default: return FLUJO_NORMAL;
    }
}

static Flujo ejecutar_bloque(Nodo *primero) {
    for (Nodo *s = primero; s; s = s->siguiente) {
        Flujo f = ejecutar_sentencia(s);
        if (f == FLUJO_ROMPER || f == FLUJO_DEVOLVER) return f;
        if (f == FLUJO_CONTINUAR) continue;
    }
    return FLUJO_NORMAL;
}

/* ---------- punto de entrada ---------- */

int interpretar_programa(Nodo *raiz) {
    srand((unsigned int)time(NULL));
    n_pila = 0;
    pila[n_pila++].n_vars = 0;
    n_funciones = 0;

    /* primera pasada: registrar todas las funciones antes de ejecutar */
    for (Nodo *s = raiz->cuerpo; s; s = s->siguiente)
        if (s->tipo == N_FUNCION) ejecutar_sentencia(s);

    for (Nodo *s = raiz->cuerpo; s; s = s->siguiente) {
        if (s->tipo == N_FUNCION) continue;
        Flujo f = ejecutar_sentencia(s);
        if (f == FLUJO_DEVOLVER || f == FLUJO_ROMPER) break;
    }
    return 0;
}
