#include "app_key.h"
#include <stddef.h>

/* 按键行为说明：
 * K1 单击         -> 回零（朝光电开关方向，低速）
 * K2 长按/松开    -> JOG+ 启动 / 减速停止
 * K3 长按/松开    -> JOG- 启动 / 减速停止
 * K4 单击         -> 速度档位循环
 * K5 单击         -> 立即停止
 */
#define SPEED_LEVEL_COUNT (3u)
#define JOG_SPEED_LOW_MM_S (5.0f)
#define JOG_SPEED_MID_MM_S (25.0f)
#define JOG_SPEED_HIGH_MM_S (60.0f)
#define HOME_SPEED_MM_S (5.0f)
#define HOME_TRAVEL_MM (500.0f)

static const float s_speed_levels[SPEED_LEVEL_COUNT] = {
    JOG_SPEED_LOW_MM_S,
    JOG_SPEED_MID_MM_S,
    JOG_SPEED_HIGH_MM_S,
};

static uint8_t s_speed_idx = 0u;
static uint8_t s_jog_key = KEY_NONE;
static uint8_t s_home_active = 0u;

/* 光电零位开关：低电平有效。 */
static uint8_t key_home_switch_active(void)
{
    return (HAL_GPIO_ReadPin(X_ZERO_GPIO_Port, X_ZERO_Pin) == GPIO_PIN_RESET) ? 1u : 0u;
}

/* 根据方向和已走步数估算当前位置（mm）。 */
static float key_estimate_pos_mm(const Stepper_t *stepper)
{
    float moved_mm = (float)stepper->step_count / STEPS_PER_MM;
    if (stepper->dir == GPIO_PIN_RESET)
    {
        return last_move_start_pos_mm + moved_mm;
    }
    return last_move_start_pos_mm - moved_mm;
}

static void local_motion_start(Stepper_t *stepper, GPIO_PinState dir, float speed_mm_s, float travel_mm)
{
    if (stepper == NULL)
    {
        return;
    }

    if (speed_mm_s < 0.1f)
    {
        speed_mm_s = 0.1f;
    }

    if (stepper->state != STOP)
    {
        Int_TMC2209_stop();
        stepper->state = STOP;
    }

    stepper->dir = dir;
    stepper->distance = travel_mm;
    stepper->speed = (uint16_t)speed_mm_s;
    stepper->acc = 50;
    stepper->dec = 50;
    stepper->start_speed = 1.0f;
    stepper->run_flag = 1u;
    last_move_start_pos_mm = current_absolute_pos_mm;

    Int_TMC2209_start(stepper, &encoder_1);
}

/* 将当前运动切换为减速停止。 */
static void local_motion_stop_decel(Stepper_t *stepper)
{
    if (stepper == NULL)
    {
        return;
    }

    if (stepper->state == STOP)
    {
        stepper->run_flag = 0u;
        return;
    }

    stepper->total_step = stepper->step_count + stepper->acc_step;
    if (stepper->state == ACCELERATE || stepper->state == CONSTANT)
    {
        stepper->state = DECELERATE;
    }
    stepper->run_flag = 0u;
}

/* 立即停止通道：用于 STOP 按键和 HOME 完成后的收尾。 */
static void local_motion_estop(Stepper_t *stepper)
{
    if (stepper == NULL)
    {
        return;
    }

    Int_TMC2209_stop();
    stepper->state = STOP;
    stepper->run_flag = 0u;
    s_jog_key = KEY_NONE;
    s_home_active = 0u;
}

/* 运行时守卫：运动中执行限位判断与 HOME 触发判断。 */
static void key_guard_check(Stepper_t *stepper)
{
    float est_pos_mm = 0.0f;
    uint8_t xzero_active = 0u;

    if (stepper == NULL || stepper->state == STOP)
    {
        return;
    }

    est_pos_mm = key_estimate_pos_mm(stepper);
    current_absolute_pos_mm = est_pos_mm;
    xzero_active = key_home_switch_active();
    if ((stepper->x_zero == 1u) && (xzero_active == 0u))
    {
        /* 若引脚当前未触发，清除可能因抖动造成的 EXTI 残留置位。 */
        stepper->x_zero = 0u;
    }

    if (s_home_active)
    {
        if ((stepper->x_zero == 1u) && (xzero_active == 1u))
        {
            local_motion_estop(stepper);
            stepper->x_zero = 0u;
            debug_printf("[KEY] HOME done");
        }
        return;
    }

    if ((s_jog_key == KEY_2) && (est_pos_mm >= SOFT_LIMIT_MAX_MM))
    {
        local_motion_stop_decel(stepper);
        current_absolute_pos_mm = SOFT_LIMIT_MAX_MM;
        debug_printf("[KEY] JOG+ reach soft limit %.1f mm", SOFT_LIMIT_MAX_MM);
    }
    else if ((s_jog_key == KEY_3) &&
             ((stepper->x_zero == 1u) && (xzero_active == 1u)))
    {
        local_motion_stop_decel(stepper);
        stepper->x_zero = 0u;
        debug_printf("[KEY] JOG- reach home boundary");
    }
}

void App_key_run(Stepper_t *stepper)
{
    Int_Key_Task();
    key_guard_check(stepper);

    KeyAction_t action;
    while (Int_Key_PopAction(&action))
    {
        switch (action.key)
        {
        case KEY_1:
            if (action.evt == KEY_EVT_CLICK)
            {
                s_jog_key = KEY_NONE;
                s_home_active = 1u;
                stepper->x_zero = 0u;
                local_motion_start(stepper, GPIO_PIN_SET, HOME_SPEED_MM_S, HOME_TRAVEL_MM);
                debug_printf("[KEY] HOME start @ %.1f mm/s", HOME_SPEED_MM_S);
            }
            break;

        case KEY_2:
            if (action.evt == KEY_EVT_LONG_START)
            {
                s_home_active = 0u;
                s_jog_key = KEY_2;
                local_motion_start(stepper, GPIO_PIN_RESET, s_speed_levels[s_speed_idx], HOME_TRAVEL_MM);
                debug_printf("[KEY] JOG+ start @ %.1f mm/s", s_speed_levels[s_speed_idx]);
            }
            else if (action.evt == KEY_EVT_RELEASE && s_jog_key == KEY_2)
            {
                local_motion_stop_decel(stepper);
                s_jog_key = KEY_NONE;
                debug_printf("[KEY] JOG+ release -> decel stop");
            }
            break;

        case KEY_3:
            if (action.evt == KEY_EVT_LONG_START)
            {
                s_home_active = 0u;
                s_jog_key = KEY_3;
                stepper->x_zero = 0u;
                local_motion_start(stepper, GPIO_PIN_SET, s_speed_levels[s_speed_idx], HOME_TRAVEL_MM);
                debug_printf("[KEY] JOG- start @ %.1f mm/s", s_speed_levels[s_speed_idx]);
            }
            else if (action.evt == KEY_EVT_RELEASE && s_jog_key == KEY_3)
            {
                local_motion_stop_decel(stepper);
                s_jog_key = KEY_NONE;
                debug_printf("[KEY] JOG- release -> decel stop");
            }
            break;

        case KEY_4:
            if (action.evt == KEY_EVT_CLICK)
            {
                s_speed_idx++;
                if (s_speed_idx >= SPEED_LEVEL_COUNT)
                {
                    s_speed_idx = 0u;
                }
                debug_printf("[KEY] SPEED -> %.1f mm/s", s_speed_levels[s_speed_idx]);
            }
            break;

        case KEY_5:
            if (action.evt == KEY_EVT_CLICK)
            {
                local_motion_estop(stepper);
                debug_printf("[KEY] STOP");
            }
            break;

        default:
            break;
        }
    }
}
