/*==============================================================================
 * cmd_device.c - Device state control handlers
 *
 * A. msq? -> rsq:  device power OFF
 * B. mss? -> rss:  device soft reset
 * C. msr? -> rsr:  BLE bond delete + reset      (FEATURE_SECURE_CONNECTION)
 * D. cmd? -> rmd:  direct GPIO pin control      (debug / test only)
 * E. mls? -> rls:  set LED state
 *============================================================================*/

#include "cmd_common.h"
#include "cmd_device.h"
#include "fstorage.h"
#include "led_control.h"
#include "app_raw.h"

/*==============================================================================
 * msq? -> rsq:  Device power OFF
 *
 * Request:  [TAG 4B "msq?"] [val 2B BE] [CRC 2B]
 * Response: [TAG 4B "rsq:"] [val 2B BE] [CRC 2B]
 *
 * Sends BLE response first; the actual power-off is performed from the main loop.
 * Powering off immediately would prevent the response from reaching the host, so a flag + timer pattern is used instead.
 *============================================================================*/
int Cmd_msq(const ParsedCmd *cmd)
{
    uint16_t val = 0;
    dr_get_u16(cmd, 0, &val);
    if (g_plat.log)
    {
        g_plat.log("[Cmd_msq] Power off val=%u\r\n", val);
    }
    single_format_data(ble_bin_buffer, "rsq:", val);
    dr_binary_tx_safe(ble_bin_buffer, 2);
    go_device_power_off = true;
    main_timer_start();
    return 1;
}

/*==============================================================================
 * mss? -> rss:  Device soft reset
 *
 * Request:  [TAG 4B "mss?"] [val 2B BE] [CRC 2B]
 * Response: [TAG 4B "rss:"] [val 2B BE] [CRC 2B]
 *
 * Resets without erasing bond information. Reset status code is persisted to FDS so the boot path can identify the cause.
 *============================================================================*/
int Cmd_mss(const ParsedCmd *cmd)
{
    uint16_t val = 0;
    dr_get_u16(cmd, 0, &val);
    single_format_data(ble_bin_buffer, "rss:", val);
    dr_binary_tx_safe(ble_bin_buffer, 2);

    m_reset_status = 2;
    m_config.reset_status = m_reset_status;
    config_save();
    nrf_delay_ms(5);
    go_NVIC_SystemReset = true;
    main_timer_start();
    return 1;
}

/*==============================================================================
 * msr? -> rsr:  Delete BLE bonding info + system reset
 *
 * Request:  [TAG 4B "msr?"] [val 2B BE] [CRC 2B]
 * Response: [TAG 4B "rsr:"] [val 2B BE] [CRC 2B]
 *
 * 1. Send BLE response
 * 2. Persist bond-delete flag and reset status to FDS
 * 3. Wait 5 ms for FDS write completion
 * 4. Set NVIC system reset flag
 *============================================================================*/
#if FEATURE_SECURE_CONNECTION
int Cmd_msr(const ParsedCmd *cmd)
{
    uint16_t val = 0;
    dr_get_u16(cmd, 0, &val);
    single_format_data(ble_bin_buffer, "rsr:", val);
    dr_binary_tx_safe(ble_bin_buffer, 2);

    bond_data_delete = true;
    m_config.bond_data_delete = (uint8_t)bond_data_delete;
    m_reset_status = 2;
    m_config.reset_status = m_reset_status;
    config_save();
    nrf_delay_ms(5);
    go_NVIC_SystemReset = true;
    main_timer_start();
    return 1;
}
#endif

/*==============================================================================
 * cmd? -> rmd:  Direct GPIO pin control (debug / test)
 *
 * Request:  [TAG 4B "cmd?"] [port 2B BE] [pin 2B BE] [state 2B BE] [CRC 2B]
 * Response: [TAG 4B "rmd:"] [port 2B BE] [pin 2B BE] [state 2B BE] [CRC 2B]
 * Error:    rmd: + 0  (insufficient data)
 *
 * port : GPIO port (0 or 1)
 * pin  : pin number (0..31)
 * state: 1=HIGH, 0=LOW
 *
 * Misuse can damage hardware - intended for debug only.
 *============================================================================*/
int Cmd_cmd(const ParsedCmd *cmd)
{
    uint16_t v1, v2, v3;
    uint32_t pin_number;

    if (cmd->data_len < 6)
    {
        dr_ble_return_1("rmd:", 0);
        return 1;
    }

    if (!dr_get_u16(cmd, 0, &v1))
    {
        v1 = 0;
    }
    if (!dr_get_u16(cmd, 1, &v2))
    {
        v2 = 0;
    }
    if (!dr_get_u16(cmd, 2, &v3))
    {
        v3 = 0;
    }

    pin_number = NRF_GPIO_PIN_MAP(v1, v2);
    nrf_gpio_cfg_output(pin_number);

    if (v3 == 1)
    {
        nrf_gpio_pin_set(pin_number);
    }
    else
    {
        nrf_gpio_pin_clear(pin_number);
    }

    dr_ble_return_3("rmd:", v1, v2, v3);
    return 1;
}

/*==============================================================================
 * mls? -> rls:  Set LED state (app -> device)
 *
 * Request:  [TAG 4B "mls?"] [state 2B BE] [CRC 2B]
 *   state: led_state_t enum value: 0=OFF, 4=DETACH_WARNING, 5=ALIGN_SEARCHING, 6=ALIGN_COMPLETE
 * Response: [TAG 4B "rls:"] [state 2B] [CRC 2B]
 * Error:    rls: + 0xFFFF (insufficient data)
 *           rls: + 0xFFFE (state out of range)
 *============================================================================*/
int Cmd_mls(const ParsedCmd *cmd)
{
    if (cmd->data_len < 2)
    {
        dr_ble_return_1("rls:", 0xFFFF);
        return 1;
    }

    uint16_t state;
    dr_get_u16(cmd, 0, &state);

    if (state > LED_STATE_ERROR)
    {
        dr_ble_return_1("rls:", 0xFFFE);
        return 1;
    }

    led_set_state((led_state_t)state);
    if ((led_state_t)state == LED_STATE_OFF)
    {
        if (imu_fifo_capture_is_active())
        {
            imu_fifo_capture_disable();
        }
    }

    dr_ble_return_1("rls:", state);
    return 1;
}
