#pragma once

// ansi_parser — parser ANSI/VT100 minimo, byte-a-byte, sin heap allocs por evento.
//
// Soporta:
//   SGR: colores FG/BG 16 colores (30-37, 40-47, 90-97, 100-107), bold (1), reset (0).
//   CUP (H): cursor absolute position row,col.
//   CUU (A), CUD (B), CUF (C), CUB (D): cursor relative moves.
//   ED  (J): erase in display (param=2 → clear screen).
//   EL  (K): erase in line   (param=2 → clear line).
//   Carriage Return (\r), Newline (\n), Backspace (\x08).
//   Text: caracteres imprimibles (excl. control bytes).
//
// No soportado (v1, anti-scope):
//   256/24-bit color, italics, underline, Unicode wide, OSC, DCS, SOS, PM, APC,
//   CSI sequences > 16 parametros, character sets (SI/SO), private modes.
//
// Uso:
//   fn_term::AnsiParser p;
//   p.feed(data, n, [](const fn_term::AnsiEvent& ev) { /* handle */ });
//
// Thread-safety: NO. Cada instancia debe usarse desde un solo hilo.

#include <cstddef>
#include <cstdint>
#include <functional>

namespace fn_term {

// Codigos de color ANSI → index 0-15 en paleta CGA/xterm-16.
//   0-7:   colores normales (black, red, green, yellow, blue, magenta, cyan, white)
//   8-15:  colores brillantes (idem + bright)
//   16:    color por defecto (FG o BG)
static constexpr uint8_t kColorDefault = 16;

// Paleta xterm-16 en RGBA8888 (A=0xFF), misma que la mayoria de terminales.
// Acceso: kPalette16[index], index in [0,15].
extern const uint32_t kPalette16[17]; // [16] = color "default" (blanco/negro)

// Una celda del terminal virtual.
struct AnsiCell {
    char32_t ch   = U' ';               // codepoint Unicode (solo BMP en v1)
    uint8_t  fg   = kColorDefault;      // indice paleta 0-16 (16 = default)
    uint8_t  bg   = kColorDefault;
    uint8_t  bold = 0;
    uint8_t  _pad = 0;
};

// Tipos de evento emitidos por el parser.
enum class AnsiEventType : uint8_t {
    Char,           // un caracter imprimible (AnsiEvent.cell.ch valido)
    CursorMove,     // AnsiEvent.row / .col delta o absoluto segun subtype
    CursorAbsolute, // CUP: posicion absoluta 0-based (row, col)
    EraseDisplay,   // ED(2): limpiar pantalla completa
    EraseLine,      // EL(2): limpiar linea actual completa
    CarriageReturn, // \r
    Newline,        // \n
    Backspace,      // \x08
};

// Subtipos de CursorMove.
enum class CursorDir : uint8_t { Up, Down, Forward, Back };

struct AnsiEvent {
    AnsiEventType type;
    union {
        AnsiCell cell;             // type == Char
        struct {
            CursorDir dir;
            int       n;           // pasos (>= 1)
        } cursor_rel;              // type == CursorMove
        struct {
            int row;               // 0-based
            int col;               // 0-based
        } cursor_abs;              // type == CursorAbsolute
        // EraseDisplay, EraseLine, CarriageReturn, Newline, Backspace: sin datos extra.
    };

    AnsiEvent() : type(AnsiEventType::Char), cell{} {}
};

// Clase principal. Stateful — mantiene el estado del parser entre llamadas a feed().
class AnsiParser {
public:
    AnsiParser();
    ~AnsiParser() = default;
    AnsiParser(const AnsiParser&)            = delete;
    AnsiParser& operator=(const AnsiParser&) = delete;

    // Procesa `n` bytes de `data`. Emite eventos via `cb` en orden.
    // cb puede ser llamada 0 o más veces por feed().
    // Sin alloc heap por byte ni por evento.
    void feed(const char* data, size_t n,
              const std::function<void(const AnsiEvent&)>& cb);

    // Resetea el estado del parser (útil al limpiar pantalla).
    void reset();

    // Atributos SGR actuales (se actualizan al procesar secuencias SGR).
    uint8_t current_fg()   const { return cur_fg_; }
    uint8_t current_bg()   const { return cur_bg_; }
    uint8_t current_bold() const { return cur_bold_; }

private:
    enum class State : uint8_t {
        Ground,     // estado normal: procesar texto
        Escape,     // recibido ESC
        CsiEntry,   // recibido ESC [
        CsiParam,   // acumulando parametros CSI
    };

    State   state_    = State::Ground;
    uint8_t cur_fg_   = kColorDefault;
    uint8_t cur_bg_   = kColorDefault;
    uint8_t cur_bold_ = 0;

    // Buffer de parametros CSI (max 16 params de 4 digitos cada uno).
    static constexpr int kMaxParams = 16;
    int  params_[kMaxParams];
    int  param_count_ = 0;
    int  cur_param_   = 0; // valor del param que se esta acumulando

    void process_byte(unsigned char c,
                      const std::function<void(const AnsiEvent&)>& cb);
    void flush_param();
    void dispatch_csi(unsigned char final_byte,
                      const std::function<void(const AnsiEvent&)>& cb);
    void apply_sgr(const std::function<void(const AnsiEvent&)>& cb);
};

} // namespace fn_term
