#include "TestUnitCardKB2.h"
#include "GroveLookup.h"
#include "UiScale.h"
#include <tactility/device.h>
#include <tactility/drivers/uart_controller.h>
#include <tactility/lvgl_fonts.h>
#include <cstring>

// ---------------------------------------------------------------------------
// Physical key layout - 5 rows (4 real + arrow row).
// matchChar=0 = modifier key (no highlight target).
// Arrow keys produced by Fn+Z/X/D/C in I2C mode; by key-id in UART mode.
// ---------------------------------------------------------------------------
static const struct { const char* label; uint8_t matchChar; int grow; } LAYOUT[][12] = {
    // Row 0: 1 2 3 4 5 6 7 8 9 0 Del  (+ Esc shown but Esc = Fn+1, no direct key-id)
    { {"1",'1',1},{"2",'2',1},{"3",'3',1},{"4",'4',1},{"5",'5',1},{"6",'6',1},
      {"7",'7',1},{"8",'8',1},{"9",'9',1},{"0",'0',1},{"Del",0x08,1},{nullptr,0,0} },
    // Row 1: q w e r t y u i o p Del
    { {"q",'q',1},{"w",'w',1},{"e",'e',1},{"r",'r',1},{"t",'t',1},{"y",'y',1},
      {"u",'u',1},{"i",'i',1},{"o",'o',1},{"p",'p',1},{"Del",0x08,1},{nullptr,0,0} },
    // Row 2: Aa a s d f g h j k l Enter
    { {"Aa",0,1},{"a",'a',1},{"s",'s',1},{"d",'d',1},{"f",'f',1},{"g",'g',1},
      {"h",'h',1},{"j",'j',1},{"k",'k',1},{"l",'l',1},{"Ent",0x0A,1},{nullptr,0,0} },
    // Row 3: Fn Sym z x c v b n m Spc
    { {"Fn",0,1},{"Sym",0,1},{"z",'z',1},{"x",'x',1},{"c",'c',1},{"v",'v',1},
      {"b",'b',1},{"n",'n',1},{"m",'m',1},{"Spc",0x20,3},{nullptr,0,0},{nullptr,0,0} },
    // Row 4: arrow keys (ASCII from Fn combos in I2C; produced by Fn key-ids in UART)
    { {"<",0x1D,1},{"v",0x1F,1},{"^",0x1E,1},{">",0x1C,1},
      {nullptr,0,0},{nullptr,0,0},{nullptr,0,0},{nullptr,0,0},{nullptr,0,0},{nullptr,0,0},{nullptr,0,0},{nullptr,0,0} },
};
static constexpr int ROW_COUNT = 5;
static constexpr int COL_COUNT = 12;

// ---------------------------------------------------------------------------
// Grid construction
// ---------------------------------------------------------------------------

void TestUnitCardKB2::buildGrid(lv_obj_t* parent) {
    gridCount_ = 0;
    for (int row = 0; row < ROW_COUNT; row++) {
        lv_obj_t* rowCont = lv_obj_create(parent);
        lv_obj_set_width(rowCont, LV_PCT(100));
        lv_obj_set_height(rowCont, LV_SIZE_CONTENT);
        lv_obj_set_layout(rowCont, LV_LAYOUT_FLEX);
        lv_obj_set_flex_flow(rowCont, LV_FLEX_FLOW_ROW);
        lv_obj_set_flex_align(rowCont, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
        lv_obj_set_style_pad_column(rowCont, uiPad() / 4 + 1, 0);
        lv_obj_set_style_pad_all(rowCont, 0, 0);
        lv_obj_set_style_bg_opa(rowCont, LV_OPA_TRANSP, 0);
        lv_obj_set_style_border_width(rowCont, 0, 0);

        for (int col = 0; col < COL_COUNT; col++) {
            if (LAYOUT[row][col].label == nullptr) break;
            lv_obj_t* btn = lv_button_create(rowCont);
            lv_obj_set_style_pad_hor(btn, uiPad() / 2, 0);
            lv_obj_set_style_pad_ver(btn, uiRowGap(), 0);
            lv_obj_set_style_radius(btn, 4, 0);
            lv_obj_set_flex_grow(btn, LAYOUT[row][col].grow);

            lv_obj_t* lbl = lv_label_create(btn);
            lv_obj_set_style_text_font(lbl, lvgl_get_text_font(FONT_SIZE_SMALL), 0);
            lv_label_set_text(lbl, LAYOUT[row][col].label);
            lv_obj_center(lbl);

            if (gridCount_ < GRID_KEY_COUNT) {
                grid_[gridCount_++] = { LAYOUT[row][col].label, LAYOUT[row][col].matchChar, btn, lbl };
            }
        }
    }
}

// ---------------------------------------------------------------------------
// Connection overlay
// ---------------------------------------------------------------------------

void TestUnitCardKB2::showConnectOverlay() {
    int pad    = uiPad();
    int rowGap = uiRowGap();
    const lv_font_t* fnt = lvgl_get_text_font(uiFont());

    connectOverlay_ = lv_obj_create(parentRef_);
    lv_obj_set_size(connectOverlay_, LV_PCT(100), LV_PCT(100));
    lv_obj_set_pos(connectOverlay_, 0, 0);
    lv_obj_set_layout(connectOverlay_, LV_LAYOUT_FLEX);
    lv_obj_set_flex_flow(connectOverlay_, LV_FLEX_FLOW_COLUMN);
    lv_obj_set_flex_align(connectOverlay_, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
    lv_obj_set_style_pad_all(connectOverlay_, pad, 0);
    lv_obj_set_style_pad_row(connectOverlay_, rowGap * 2, 0);

    lv_obj_t* title = lv_label_create(connectOverlay_);
    lv_obj_set_style_text_font(title, fnt, 0);
    lv_label_set_text(title, "Select connection mode");

    lv_obj_t* hint = lv_label_create(connectOverlay_);
    lv_obj_set_style_text_font(hint, lvgl_get_text_font(FONT_SIZE_SMALL), 0);
    lv_obj_set_style_text_color(hint, lv_color_hex(0x888888), 0);
    lv_label_set_text(hint, "Fn+Sym+1 = I2C  |  Fn+Sym+2 = UART");

    auto makeBtn = [&](const char* label, lv_event_cb_t cb) {
        lv_obj_t* btn = lv_button_create(connectOverlay_);
        lv_obj_set_width(btn, LV_PCT(60));
        lv_obj_add_event_cb(btn, cb, LV_EVENT_CLICKED, this);
        lv_obj_t* lbl = lv_label_create(btn);
        lv_obj_set_style_text_font(lbl, fnt, 0);
        lv_label_set_text(lbl, label);
        lv_obj_center(lbl);
    };
    makeBtn("I2C (Grove port)", onI2CBtn);
    makeBtn("UART (Grove port)", onUartBtn);
}

// ---------------------------------------------------------------------------
// Connect handlers
// ---------------------------------------------------------------------------

void TestUnitCardKB2::connectI2C() {
    lv_obj_delete(connectOverlay_);
    connectOverlay_ = nullptr;

    Device* i2c = findGroveI2cDevice();
    if (!i2c) {
        buildMainUI();
        lv_label_set_text(lblHistory_, "grove0_i2c not found");
        return;
    }

    bool ok = false;
    if (unit_.begin(i2c)) {
        usingPaHub_ = false;
        ok = true;
    } else if (hub_.begin(i2c)) {
        usingPaHub_ = true;
        for (uint8_t ch = 0; ch < UnitPaHub::NUM_CHANNELS && !ok; ch++) {
            hub_.select(ch);
            if (unit_.begin(i2c)) ok = true;
        }
        if (!ok) hub_.deselect();
    }

    buildMainUI();
    if (!ok) lv_label_set_text(lblHistory_, "CardKB2 not found");
    else     timer_ = lv_timer_create(onTimer, 50, this);
}

void TestUnitCardKB2::connectUart() {
    lv_obj_delete(connectOverlay_);
    connectOverlay_ = nullptr;

    Device* uart = findGroveUartDevice();
    buildMainUI();
    if (!uart) {
        lv_label_set_text(lblHistory_, "grove0_uart not found");
        return;
    }
    if (!unit_.beginUart(uart)) {
        lv_label_set_text(lblHistory_, "UART open failed");
        return;
    }
    timer_ = lv_timer_create(onTimer, 50, this);
}

// ---------------------------------------------------------------------------
// Main content UI (built after connection type selected)
// ---------------------------------------------------------------------------

void TestUnitCardKB2::buildMainUI() {
    memset(history_, 0, sizeof(history_));
    histLen_    = 0;
    gridCount_  = 0;
    activeBtn_  = nullptr;

    lv_obj_t* cont = lv_obj_create(parentRef_);
    lv_obj_set_width(cont, LV_PCT(100));
    lv_obj_set_flex_grow(cont, 1);
    lv_obj_set_layout(cont, LV_LAYOUT_FLEX);
    lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_COLUMN);
    lv_obj_set_flex_align(cont, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START);
    lv_obj_set_style_pad_row(cont, 3, 0);
    lv_obj_set_style_pad_all(cont, uiPad(), 0);
    lv_obj_set_style_bg_opa(cont, LV_OPA_TRANSP, 0);
    lv_obj_set_style_border_width(cont, 0, 0);

    if (uiW() >= 200) buildGrid(cont);

    lblHistory_ = lv_label_create(cont);
    lv_obj_set_style_text_font(lblHistory_, lvgl_get_text_font(uiFont()), 0);
    lv_label_set_text(lblHistory_, "");
    lv_obj_set_width(lblHistory_, LV_PCT(100));
    lv_label_set_long_mode(lblHistory_, LV_LABEL_LONG_WRAP);
    lv_obj_set_flex_grow(lblHistory_, 1);
}

// ---------------------------------------------------------------------------
// Lifecycle
// ---------------------------------------------------------------------------

void TestUnitCardKB2::onStart(lv_obj_t* parent, AppHandle handle, M5UnitTest* app) {
    app_       = app;
    parentRef_ = parent;
    handleRef_ = handle;

    createToolbar(parent, handle, "CardKB2");
    createBanner(parent, "CardKB2", "I2C/UART", COLOR_I2C);

    showConnectOverlay();
}

void TestUnitCardKB2::onStop() {
    if (timer_) { lv_timer_delete(timer_); timer_ = nullptr; }
    if (usingPaHub_ && hub_.isPresent()) hub_.deselect();
    unit_.end();
    lblHistory_     = nullptr;
    connectOverlay_ = nullptr;
    activeBtn_      = nullptr;
    gridCount_      = 0;
}

// ---------------------------------------------------------------------------
// Static callbacks
// ---------------------------------------------------------------------------

void TestUnitCardKB2::onI2CBtn(lv_event_t* e) {
    static_cast<TestUnitCardKB2*>(lv_event_get_user_data(e))->connectI2C();
}

void TestUnitCardKB2::onUartBtn(lv_event_t* e) {
    static_cast<TestUnitCardKB2*>(lv_event_get_user_data(e))->connectUart();
}

void TestUnitCardKB2::onTimer(lv_timer_t* t) {
    static_cast<TestUnitCardKB2*>(lv_timer_get_user_data(t))->update();
}

// ---------------------------------------------------------------------------
// PaHub helper
// ---------------------------------------------------------------------------

void TestUnitCardKB2::selectIfNeeded() {
    if (usingPaHub_ && hub_.isPresent())
        hub_.select(hub_.currentChannel());
}

// ---------------------------------------------------------------------------
// Update (called from timer)
// ---------------------------------------------------------------------------

void TestUnitCardKB2::update() {
    if (!unit_.isPresent()) return;
    if (unit_.mode() == UnitCardKB2::Mode::I2C) selectIfNeeded();

    char c = unit_.getKey();

    // Grid highlight
    if (gridCount_ > 0) {
        lv_obj_t* newActive = nullptr;
        if (c != 0) {
            for (int i = 0; i < gridCount_; i++) {
                if (grid_[i].matchChar == 0 || grid_[i].btn == nullptr) continue;
                uint8_t mc = grid_[i].matchChar;
                // Match lower and uppercase variants of letter keys
                bool match = (mc == (uint8_t)c) ||
                             (mc >= 'a' && mc <= 'z' && mc == (uint8_t)(c | 0x20));
                if (match) { newActive = grid_[i].btn; break; }
            }
        }
        if (newActive != activeBtn_) {
            if (activeBtn_) lv_obj_remove_state(activeBtn_, LV_STATE_PRESSED);
            if (newActive)  lv_obj_add_state(newActive, LV_STATE_PRESSED);
            activeBtn_ = newActive;
        }
    }

    // History strip - printable chars only
    if (c != 0 && c >= 0x20 && c < 0x7F) {
        if (histLen_ < sizeof(history_) - 1) {
            history_[histLen_++] = c;
        } else {
            memmove(history_, history_ + 1, histLen_ - 1);
            history_[histLen_ - 1] = c;
        }
        history_[histLen_] = '\0';
        lv_label_set_text(lblHistory_, history_);
    }
}
