#include "core/ansi_parser.h"

namespace fn_term {

// Paleta xterm-16 en ABGR (little-endian: R,G,B,A en memoria = RGBA8888 en lectura).
// Index 0-7 colores normales, 8-15 brillantes, 16 = default.
const uint32_t kPalette16[17] = {
    0xFF000000, // 0  black
    0xFF0000AA, // 1  red
    0xFF00AA00, // 2  green
    0xFF00AAAA, // 3  yellow (dark)
    0xFFAA0000, // 4  blue
    0xFFAA00AA, // 5  magenta
    0xFFAAAA00, // 6  cyan
    0xFFAAAAAA, // 7  white (light grey)
    0xFF555555, // 8  bright black (dark grey)
    0xFF5555FF, // 9  bright red
    0xFF55FF55, // 10 bright green
    0xFF55FFFF, // 11 bright yellow
    0xFFFF5555, // 12 bright blue
    0xFFFF55FF, // 13 bright magenta
    0xFFFFFF55, // 14 bright cyan
    0xFFFFFFFF, // 15 bright white
    0xFFCCCCCC, // 16 default (light grey)
};

AnsiParser::AnsiParser() {
    for (int i = 0; i < kMaxParams; i++) params_[i] = 0;
}

void AnsiParser::reset() {
    state_      = State::Ground;
    cur_fg_     = kColorDefault;
    cur_bg_     = kColorDefault;
    cur_bold_   = 0;
    param_count_ = 0;
    cur_param_   = 0;
    for (int i = 0; i < kMaxParams; i++) params_[i] = 0;
}

void AnsiParser::feed(const char* data, size_t n,
                      const std::function<void(const AnsiEvent&)>& cb) {
    for (size_t i = 0; i < n; i++) {
        process_byte(static_cast<unsigned char>(data[i]), cb);
    }
}

void AnsiParser::flush_param() {
    if (param_count_ < kMaxParams) {
        params_[param_count_++] = cur_param_;
    }
    cur_param_ = 0;
}

void AnsiParser::apply_sgr(const std::function<void(const AnsiEvent&)>& /*cb*/) {
    // Si no hay params → reset (SGR 0).
    int n = (param_count_ == 0) ? 1 : param_count_;
    const int* p = (param_count_ == 0) ? nullptr : params_;

    for (int i = 0; i < n; i++) {
        int code = (p ? p[i] : 0);
        if (code == 0) {
            // Reset todo
            cur_fg_   = kColorDefault;
            cur_bg_   = kColorDefault;
            cur_bold_ = 0;
        } else if (code == 1) {
            cur_bold_ = 1;
        } else if (code == 22) {
            cur_bold_ = 0;
        } else if (code >= 30 && code <= 37) {
            cur_fg_ = static_cast<uint8_t>(code - 30);
        } else if (code == 39) {
            cur_fg_ = kColorDefault;
        } else if (code >= 40 && code <= 47) {
            cur_bg_ = static_cast<uint8_t>(code - 40);
        } else if (code == 49) {
            cur_bg_ = kColorDefault;
        } else if (code >= 90 && code <= 97) {
            cur_fg_ = static_cast<uint8_t>(code - 90 + 8);
        } else if (code >= 100 && code <= 107) {
            cur_bg_ = static_cast<uint8_t>(code - 100 + 8);
        }
        // Otros códigos ignorados silenciosamente (v1 anti-scope).
    }
}

void AnsiParser::dispatch_csi(unsigned char final_byte,
                               const std::function<void(const AnsiEvent&)>& cb) {
    AnsiEvent ev;
    int p0 = (param_count_ > 0) ? params_[0] : 0;
    int p1 = (param_count_ > 1) ? params_[1] : 0;

    switch (final_byte) {
    case 'H': case 'f': {
        // CUP: ESC [ row ; col H  (1-based → convertir a 0-based)
        ev.type = AnsiEventType::CursorAbsolute;
        ev.cursor_abs.row = (p0 > 0 ? p0 - 1 : 0);
        ev.cursor_abs.col = (p1 > 0 ? p1 - 1 : 0);
        cb(ev);
        break;
    }
    case 'A': {
        ev.type = AnsiEventType::CursorMove;
        ev.cursor_rel.dir = CursorDir::Up;
        ev.cursor_rel.n   = (p0 > 0 ? p0 : 1);
        cb(ev);
        break;
    }
    case 'B': {
        ev.type = AnsiEventType::CursorMove;
        ev.cursor_rel.dir = CursorDir::Down;
        ev.cursor_rel.n   = (p0 > 0 ? p0 : 1);
        cb(ev);
        break;
    }
    case 'C': {
        ev.type = AnsiEventType::CursorMove;
        ev.cursor_rel.dir = CursorDir::Forward;
        ev.cursor_rel.n   = (p0 > 0 ? p0 : 1);
        cb(ev);
        break;
    }
    case 'D': {
        ev.type = AnsiEventType::CursorMove;
        ev.cursor_rel.dir = CursorDir::Back;
        ev.cursor_rel.n   = (p0 > 0 ? p0 : 1);
        cb(ev);
        break;
    }
    case 'J': {
        // ED: erase in display. Solo param=2 (clear screen) soportado en v1.
        if (p0 == 2 || p0 == 0) {
            ev.type = AnsiEventType::EraseDisplay;
            cb(ev);
        }
        break;
    }
    case 'K': {
        // EL: erase in line. Solo param=2 (clear entire line) soportado en v1.
        if (p0 == 2 || p0 == 0) {
            ev.type = AnsiEventType::EraseLine;
            cb(ev);
        }
        break;
    }
    case 'm': {
        // SGR: select graphic rendition.
        apply_sgr(cb);
        break;
    }
    default:
        // Secuencia CSI desconocida — ignorar silenciosamente.
        break;
    }
}

void AnsiParser::process_byte(unsigned char c,
                               const std::function<void(const AnsiEvent&)>& cb) {
    switch (state_) {

    case State::Ground:
        if (c == 0x1B) {
            state_ = State::Escape;
        } else if (c == '\r') {
            AnsiEvent ev; ev.type = AnsiEventType::CarriageReturn; cb(ev);
        } else if (c == '\n') {
            AnsiEvent ev; ev.type = AnsiEventType::Newline; cb(ev);
        } else if (c == '\x08') {
            AnsiEvent ev; ev.type = AnsiEventType::Backspace; cb(ev);
        } else if (c >= 0x20 && c < 0x7F) {
            // ASCII imprimible.
            AnsiEvent ev;
            ev.type    = AnsiEventType::Char;
            ev.cell.ch = static_cast<char32_t>(c);
            ev.cell.fg = cur_fg_;
            ev.cell.bg = cur_bg_;
            ev.cell.bold = cur_bold_;
            cb(ev);
        } else if (c >= 0xC0) {
            // Inicio de secuencia UTF-8 multi-byte.
            // En v1 mapeamos todo >= 0x80 a '?' para evitar complejidad Unicode.
            // TODO(0132): soporte Unicode completo en v2.
            AnsiEvent ev;
            ev.type    = AnsiEventType::Char;
            ev.cell.ch = U'?';
            ev.cell.fg = cur_fg_;
            ev.cell.bg = cur_bg_;
            ev.cell.bold = cur_bold_;
            cb(ev);
        } else if (c >= 0x80 && c < 0xC0) {
            // Continuation byte de UTF-8 → ignorar (fragmento de multi-byte).
        }
        // Otros control bytes (0x00-0x1F excl \r\n\x08\x1B) → ignorar.
        break;

    case State::Escape:
        if (c == '[') {
            state_       = State::CsiEntry;
            param_count_ = 0;
            cur_param_   = 0;
        } else {
            // Secuencia ESC desconocida (no-CSI) → volver a Ground.
            state_ = State::Ground;
        }
        break;

    case State::CsiEntry:
        // Primer byte del CSI: puede ser un dígito, ';' o el final byte.
        if (c >= '0' && c <= '9') {
            cur_param_ = c - '0';
            state_     = State::CsiParam;
        } else if (c == ';') {
            // Parámetro vacío → valor 0.
            flush_param();
            cur_param_ = 0;
            state_     = State::CsiParam;
        } else if (c >= 0x40 && c <= 0x7E) {
            // Byte final inmediato sin parámetros.
            dispatch_csi(c, cb);
            state_ = State::Ground;
        } else if (c == '?') {
            // Modos privados (e.g. ESC[?25l cursor hide) → ignorar hasta final byte.
            // Permanecemos en CsiEntry esperando el final byte.
        } else {
            // Byte inesperado → abortar CSI.
            state_ = State::Ground;
        }
        break;

    case State::CsiParam:
        if (c >= '0' && c <= '9') {
            cur_param_ = cur_param_ * 10 + (c - '0');
        } else if (c == ';') {
            flush_param();
            cur_param_ = 0;
        } else if (c >= 0x40 && c <= 0x7E) {
            // Byte final: flush último param y despachar.
            flush_param();
            dispatch_csi(c, cb);
            state_ = State::Ground;
        } else {
            // Byte inesperado → abortar.
            state_ = State::Ground;
        }
        break;
    }
}

} // namespace fn_term
