#include "ui.h"
#include "buttons.h"
#include "display.h"
#include "sd_manager.h"
#include "motion.h"
#include "pen.h"
#include "gcode_parser.h"
#include "uart_mode.h"
#include <string.h>

static AppState    s_state = AppState::MAIN_MENU;
static PrintStatus s_status = {};

// Menu startowe
static uint8_t s_menuSelected = 0;
static bool    s_sdInited     = false;

// File list
static char    s_files[SD_MAX_FILES][FILENAME_MAX_LEN];
static uint8_t s_fileCount    = 0;
static uint8_t s_selectedFile = 0;

// Print state
static float    s_lastX    = 0.0f;
static float    s_lastY    = 0.0f;
static float    s_lastFeed = SPEED_DEFAULT_MM_S * 60.0f;
static GCodeCmd s_lastGCmd = GCodeCmd::G0;

// Confirm dialog
static bool s_confirmYes = true;

// Error state
static char s_errLine1[DISPLAY_LINE_CHARS + 1];
static char s_errLine2[DISPLAY_LINE_CHARS + 1];

// Axis test
static char    s_testAxis       = 'X';
static uint8_t s_lastLimitMask  = 0xFF; // 0xFF = nieznany (wymusi log przy starcie)

// ── Helpers ───────────────────────────────────────────────────────────────────

static uint8_t limitMask() {
    return ((digitalRead(PIN_LIMIT_X_MIN) == LOW) ? 0x01 : 0) |
           ((digitalRead(PIN_LIMIT_X_MAX) == LOW) ? 0x02 : 0) |
           ((digitalRead(PIN_LIMIT_Y_MIN) == LOW) ? 0x04 : 0) |
           ((digitalRead(PIN_LIMIT_Y_MAX) == LOW) ? 0x08 : 0);
}

static void logAxisTest(const char* event, char axis, float posX, float posY) {
    uint8_t lm = limitMask();
    Serial.printf("[TestOsi] Os:%c  %s  X=%.1f Y=%.1f  XMIN=%d XMAX=%d YMIN=%d YMAX=%d\n",
        axis, event,
        (double)posX, (double)posY,
        (lm & 0x01) ? 1 : 0,
        (lm & 0x02) ? 1 : 0,
        (lm & 0x04) ? 1 : 0,
        (lm & 0x08) ? 1 : 0);
}

static void loadFileList() {
    s_fileCount    = sd_list_files(s_files, SD_MAX_FILES);
    s_selectedFile = 0;
}

static void abortPrint() {
    pen_up();
    motion_disable();
    sd_close_file();
    s_status.isPrinting = false;
}

static void stopPrinting() {
    abortPrint();
    motion_home();
    s_state = AppState::MAIN_MENU;
    display_force_update();
    display_show_main_menu(s_menuSelected);
}

static void startPrinting() {
    const char* fname = s_files[s_selectedFile];

    if (!sd_open_file(fname)) {
        ui_set_error("Blad odczytu SD", "Nie otwarto pliku");
        return;
    }

    uint32_t lines = sd_count_lines();
    if (lines == 0) {
        sd_close_file();
        ui_set_error("Blad pliku", "Plik jest pusty");
        return;
    }

    strncpy(s_status.filename, fname, FILENAME_MAX_LEN - 1);
    s_status.filename[FILENAME_MAX_LEN - 1] = '\0';
    s_status.totalLines      = lines;
    s_status.currentLine     = 0;
    s_status.speedMultiplier = SPEED_MULT_DEFAULT;
    s_status.isPrinting      = true;

    s_lastX    = 0.0f;
    s_lastY    = 0.0f;
    s_lastFeed = SPEED_DEFAULT_MM_S * 60.0f;
    s_lastGCmd = GCodeCmd::G0;

    motion_enable();
    pen_up();

    s_state = AppState::PRINTING;
    display_force_update();
}

// ── Public API ────────────────────────────────────────────────────────────────

void ui_set_error(const char* line1, const char* line2) {
    abortPrint();
    strncpy(s_errLine1, line1 ? line1 : "", DISPLAY_LINE_CHARS);
    strncpy(s_errLine2, line2 ? line2 : "", DISPLAY_LINE_CHARS);
    s_errLine1[DISPLAY_LINE_CHARS] = '\0';
    s_errLine2[DISPLAY_LINE_CHARS] = '\0';
    s_state = AppState::ERROR;
    display_show_error(s_errLine1, s_errLine2);
}

void ui_init() {
    s_state        = AppState::MAIN_MENU;
    s_menuSelected = 0;
    s_sdInited     = false;
    display_force_update();
    display_show_main_menu(0);
}

void ui_init_uart_mode() {
    s_state = AppState::UART_MODE;
    display_force_update();
}

AppState ui_get_state() {
    return s_state;
}

const PrintStatus& ui_get_print_status() {
    return s_status;
}

void ui_update() {
    ButtonEvent ev = buttons_update();

    switch (s_state) {

    // ── Menu startowe ──────────────────────────────────────────────────────────
    case AppState::MAIN_MENU:
        if (ev == ButtonEvent::UP_PRESS) {
            s_menuSelected = (s_menuSelected + 2) % 3;
            display_force_update();
        } else if (ev == ButtonEvent::DOWN_PRESS) {
            s_menuSelected = (s_menuSelected + 1) % 3;
            display_force_update();
        } else if (ev == ButtonEvent::OK_PRESS) {
            if (s_menuSelected == 0) {
                // Drukowanie → inicjuj SD jeśli potrzeba, przejdź do listy
                if (!s_sdInited) {
                    if (!sd_init()) {
                        ui_set_error("Blad SD!", "Wloz karte SD");
                        break;
                    }
                    s_sdInited = true;
                }
                loadFileList();
                s_state = AppState::FILE_LIST;
                display_force_update();
                display_show_file_list(s_files, s_fileCount, s_selectedFile);
            } else if (s_menuSelected == 1) {
                // Tryb UART
                uart_mode_init();
                s_state = AppState::UART_MODE;
                display_force_update();
            } else {
                // Test osi
                s_testAxis = 'X';
                motion_enable();
                pen_up();
                s_state = AppState::AXIS_TEST;
                display_force_update();
            }
        }
        display_show_main_menu(s_menuSelected);
        break;

    // ── Lista plików ───────────────────────────────────────────────────────────
    case AppState::FILE_LIST:
        if (s_fileCount == 0) {
            display_show_file_list(s_files, s_fileCount, s_selectedFile);
            break;
        }
        if (ev == ButtonEvent::UP_PRESS) {
            if (s_selectedFile > 0) s_selectedFile--;
            else s_selectedFile = s_fileCount - 1;
            display_force_update();
        } else if (ev == ButtonEvent::DOWN_PRESS) {
            if (s_selectedFile < s_fileCount - 1) s_selectedFile++;
            else s_selectedFile = 0;
            display_force_update();
        } else if (ev == ButtonEvent::OK_PRESS) {
            startPrinting();
            break;
        }
        display_show_file_list(s_files, s_fileCount, s_selectedFile);
        break;

    // ── Drukowanie ─────────────────────────────────────────────────────────────
    case AppState::PRINTING: {
        if (ev == ButtonEvent::UP_PRESS) {
            float m = s_status.speedMultiplier + SPEED_MULT_STEP;
            if (m > SPEED_MULT_MAX) m = SPEED_MULT_MAX;
            s_status.speedMultiplier = m;
            motion_set_speed_multiplier(m);
            display_force_update();
        } else if (ev == ButtonEvent::DOWN_PRESS) {
            float m = s_status.speedMultiplier - SPEED_MULT_STEP;
            if (m < SPEED_MULT_MIN) m = SPEED_MULT_MIN;
            s_status.speedMultiplier = m;
            motion_set_speed_multiplier(m);
            display_force_update();
        } else if (ev == ButtonEvent::OK_PRESS) {
            s_confirmYes = true;
            s_state = AppState::CONFIRM_STOP;
            display_force_update();
            display_show_confirm_stop(s_confirmYes);
            break;
        }
        uint8_t pct = (s_status.totalLines > 0)
            ? (uint8_t)((s_status.currentLine * 100UL) / s_status.totalLines) : 0;
        display_show_printing(s_status.filename, pct, s_status.speedMultiplier);
        break;
    }

    // ── Potwierdzenie zatrzymania ──────────────────────────────────────────────
    case AppState::CONFIRM_STOP:
        if (ev == ButtonEvent::UP_PRESS || ev == ButtonEvent::DOWN_PRESS) {
            s_confirmYes = !s_confirmYes;
            display_force_update();
        } else if (ev == ButtonEvent::OK_PRESS) {
            if (s_confirmYes) {
                stopPrinting();
            } else {
                s_state = AppState::PRINTING;
                display_force_update();
            }
            break;
        }
        display_show_confirm_stop(s_confirmYes);
        break;

    // ── Tryb UART ──────────────────────────────────────────────────────────────
    case AppState::UART_MODE:
        uart_mode_update();
        display_show_uart_mode();
        break;

    // ── Test osi ───────────────────────────────────────────────────────────────
    case AppState::AXIS_TEST: {
        bool limitActive = motion_any_limit_triggered();
        float posX = motion_get_x_mm();
        float posY = motion_get_y_mm();

        // Log zmiany stanu krańcówek
        uint8_t lm = limitMask();
        if (lm != s_lastLimitMask) {
            s_lastLimitMask = lm;
            logAxisTest(lm ? "KRANC!" : "OK    ", s_testAxis, posX, posY);
        }

        // Automatyczne cofanie gdy krańcówka wyzwolona i silniki stoją
        if (limitActive && motion_is_idle()) {
            const float backoff = 3.0f;
            float nx = posX;
            float ny = posY;
            if (digitalRead(PIN_LIMIT_X_MIN) == LOW) nx += backoff;
            if (digitalRead(PIN_LIMIT_X_MAX) == LOW) nx -= backoff;
            if (digitalRead(PIN_LIMIT_Y_MIN) == LOW) ny += backoff;
            if (digitalRead(PIN_LIMIT_Y_MAX) == LOW) ny -= backoff;
            if (nx < 0.0f) nx = 0.0f;
            if (nx > WORKSPACE_WIDTH_MM)  nx = WORKSPACE_WIDTH_MM;
            if (ny < 0.0f) ny = 0.0f;
            if (ny > WORKSPACE_HEIGHT_MM) ny = WORKSPACE_HEIGHT_MM;
            motion_enable();
            motion_move_to(nx, ny, TEST_SPEED_MM_S * 60.0f);
            logAxisTest("COFANIE", s_testAxis, nx, ny);
            display_force_update();
        }

        // Przyciski aktywne tylko gdy silniki stoją i żadna krańcówka nie wyzwolona
        if (!limitActive && motion_is_idle()) {
            if (ev == ButtonEvent::UP_PRESS) {
                float nx = posX + (s_testAxis == 'X' ? TEST_STEP_MM : 0.0f);
                float ny = posY + (s_testAxis == 'Y' ? TEST_STEP_MM : 0.0f);
                if (nx > WORKSPACE_WIDTH_MM)  nx = WORKSPACE_WIDTH_MM;
                if (ny > WORKSPACE_HEIGHT_MM) ny = WORKSPACE_HEIGHT_MM;
                motion_move_to(nx, ny, TEST_SPEED_MM_S * 60.0f);
                logAxisTest("GORA  ", s_testAxis, nx, ny);
                display_force_update();
            } else if (ev == ButtonEvent::DOWN_PRESS) {
                float nx = posX - (s_testAxis == 'X' ? TEST_STEP_MM : 0.0f);
                float ny = posY - (s_testAxis == 'Y' ? TEST_STEP_MM : 0.0f);
                if (nx < 0.0f) nx = 0.0f;
                if (ny < 0.0f) ny = 0.0f;
                motion_move_to(nx, ny, TEST_SPEED_MM_S * 60.0f);
                logAxisTest("DOL   ", s_testAxis, nx, ny);
                display_force_update();
            } else if (ev == ButtonEvent::OK_PRESS) {
                s_testAxis = (s_testAxis == 'X') ? 'Y' : 'X';
                logAxisTest("ZMIANA", s_testAxis, posX, posY);
                display_force_update();
            }
        }

        display_show_axis_test(s_testAxis, posX, posY, limitActive);
        break;
    }

    // ── Błąd ──────────────────────────────────────────────────────────────────
    case AppState::ERROR:
        display_show_error(s_errLine1, s_errLine2);
        if (ev == ButtonEvent::OK_PRESS) {
            s_state = AppState::MAIN_MENU;
            display_force_update();
            display_show_main_menu(s_menuSelected);
        }
        break;
    }
}

void ui_on_motion_idle() {
    if (s_state != AppState::PRINTING) return;

    static char lineBuf[128];

    while (true) {
        if (!sd_read_line(lineBuf, sizeof(lineBuf))) {
            if (!sd_is_file_open()) {
                ui_set_error("Blad karty SD", "Utracono plik");
            } else {
                stopPrinting();
            }
            return;
        }
        s_status.currentLine++;

        GCodeLine gl;
        if (!gcode_parse_line(lineBuf, gl)) continue;

        GCodeCmd cmd = gl.cmd;
        if (cmd == GCodeCmd::NONE) {
            cmd = s_lastGCmd;
        } else if (cmd == GCodeCmd::G0 || cmd == GCodeCmd::G1) {
            s_lastGCmd = cmd;
        }

        float x = gl.hasX ? gl.x : s_lastX;
        float y = gl.hasY ? gl.y : s_lastY;
        float f = gl.hasF ? gl.f : s_lastFeed;

        if (x < 0 || x > WORKSPACE_WIDTH_MM || y < 0 || y > WORKSPACE_HEIGHT_MM) {
            ui_set_error("Blad GCode", "Poza polem rob.");
            return;
        }

        s_lastX    = x;
        s_lastY    = y;
        s_lastFeed = f;

        switch (cmd) {
        case GCodeCmd::G0:
            pen_up();
            motion_move_to(x, y, SPEED_MAX_MM_S * 60.0f);
            return;
        case GCodeCmd::G1:
            pen_down();
            motion_move_to(x, y, f);
            return;
        case GCodeCmd::G28:
            motion_home();
            return;
        case GCodeCmd::M3:
            pen_down();
            continue;
        case GCodeCmd::M5:
            pen_up();
            continue;
        default:
            continue;
        }
    }
}
