/*******************************************************************************
 * @file    led_control.c
 * @brief   Direct LED control driver
 * @date    2026-03-30
 *
 * Implements dual-color LED (green/orange) blink patterns with a single app_timer.
 * Simple on/off states use immediate GPIO control without a timer.
 * Complex patterns (e.g. error) are handled by a phase-based state machine.
 ******************************************************************************/

#include "led_control.h"
#include "nrf_gpio.h"
#include "app_timer.h"

/*==============================================================================
 * Internal constants
 *============================================================================*/
#define MS_TO_TICKS(ms)     APP_TIMER_TICKS(ms)

/*==============================================================================
 * Colors
 *============================================================================*/
#define COLOR_NONE      0
#define COLOR_GREEN     1
#define COLOR_ORANGE    2

/*==============================================================================
 * Error pattern constants (No.7)
 * 3Hz blink x3 = 166ms on + 166ms off x 3 = ~1s, then off for 1s
 *============================================================================*/
#define ERROR_BLINK_ON_MS       166
#define ERROR_BLINK_OFF_MS      166
#define ERROR_BLINK_COUNT       3
#define ERROR_PAUSE_MS          1000

/*==============================================================================
 * Pattern table (for simple blink)
 *============================================================================*/
typedef struct
{
    uint32_t on_ms;         /* LED on duration (ms) */
    uint32_t off_ms;        /* LED off duration (ms) */
    uint8_t  color;         /* COLOR_GREEN or COLOR_ORANGE */
    bool     repeat;        /* true: repeat forever, false: once then OFF */
} led_pattern_t;

static const led_pattern_t m_patterns[LED_STATE_COUNT] =
{
    [LED_STATE_OFF]              = { 0,    0,    COLOR_NONE,   false },
    [LED_STATE_POWER_ON]         = { 2000, 0,    COLOR_GREEN,  false },  /* Green on 2s, stay on */
    [LED_STATE_POWER_OFF]        = { 2000, 0,    COLOR_GREEN,  false },  /* Green on 2s, then OFF */
    [LED_STATE_ADVERTISING]      = { 500, 500, COLOR_GREEN,  true  },  /* Green blink 1s interval */
    [LED_STATE_DETACH_WARNING]   = { 1000, 3000, COLOR_GREEN,  true  },  /* Green 1s on / 3s off */
    [LED_STATE_ALIGN_SEARCHING]  = { 1000, 1000, COLOR_ORANGE, true  },  /* Orange blink 1s interval */
    [LED_STATE_ALIGN_COMPLETE]   = { 0,    0,    COLOR_GREEN,  false },  /* Green always on */
    [LED_STATE_ERROR]            = { 0,    0,    COLOR_ORANGE, true  }   /* Separate state machine */
};

/*==============================================================================
 * Module variables
 *============================================================================*/
APP_TIMER_DEF(m_led_timer);

static led_state_t  m_current_state = LED_STATE_OFF;
static bool         m_phase_on;         /* true: LED on phase, false: off phase */

/* Error pattern only */
static uint8_t      m_error_blink_cnt;  /* Blink count so far */
static uint8_t      m_error_phase;      /* 0: blink-on, 1: blink-off, 2: pause */

/*==============================================================================
 * GPIO helpers
 *============================================================================*/

static inline void led_green_on(void)
{
#if LED_ACTIVE_LOW
    nrf_gpio_pin_clear(LED_PIN_GREEN);
#else
    nrf_gpio_pin_set(LED_PIN_GREEN);
#endif
}

static inline void led_green_off(void)
{
#if LED_ACTIVE_LOW
    nrf_gpio_pin_set(LED_PIN_GREEN);
#else
    nrf_gpio_pin_clear(LED_PIN_GREEN);
#endif
}

static inline void led_orange_on(void)
{
#if LED_ACTIVE_LOW
    nrf_gpio_pin_clear(LED_PIN_ORANGE);
#else
    nrf_gpio_pin_set(LED_PIN_ORANGE);
#endif
}

static inline void led_orange_off(void)
{
#if LED_ACTIVE_LOW
    nrf_gpio_pin_set(LED_PIN_ORANGE);
#else
    nrf_gpio_pin_clear(LED_PIN_ORANGE);
#endif
}

static void led_all_off(void)
{
    led_green_off();
    led_orange_off();
}

static void led_color_on(uint8_t color)
{
    led_all_off();
    if (color == COLOR_GREEN)       led_green_on();
    else if (color == COLOR_ORANGE) led_orange_on();
}

/*==============================================================================
 * Timer start helper
 *============================================================================*/
static void timer_start_ms(uint32_t ms)
{
    if (ms == 0) return;
    app_timer_start(m_led_timer, MS_TO_TICKS(ms), NULL);
}

/*==============================================================================
 * Error pattern state machine (No.7)
 * phase 0: LED ON  (166ms) → phase 1
 * phase 1: LED OFF (166ms) → cnt++ → cnt<3 ? phase 0 : phase 2
 * phase 2: PAUSE   (1000ms) → cnt=0, phase 0
 *============================================================================*/
static void error_pattern_start(void)
{
    m_error_blink_cnt = 0;
    m_error_phase = 0;
    led_color_on(COLOR_ORANGE);
    timer_start_ms(ERROR_BLINK_ON_MS);
}

static void error_pattern_tick(void)
{
    switch (m_error_phase)
    {
        case 0: /* ON phase done -> OFF */
            led_all_off();
            m_error_phase = 1;
            timer_start_ms(ERROR_BLINK_OFF_MS);
            break;

        case 1: /* OFF phase done */
            m_error_blink_cnt++;
            if (m_error_blink_cnt < ERROR_BLINK_COUNT)
            {
                /* Back to ON */
                m_error_phase = 0;
                led_color_on(COLOR_ORANGE);
                timer_start_ms(ERROR_BLINK_ON_MS);
            }
            else
            {
                /* 3 blinks done -> pause */
                m_error_phase = 2;
                timer_start_ms(ERROR_PAUSE_MS);
            }
            break;

        case 2: /* Pause done -> restart */
            m_error_blink_cnt = 0;
            m_error_phase = 0;
            led_color_on(COLOR_ORANGE);
            timer_start_ms(ERROR_BLINK_ON_MS);
            break;

        default:
            break;
    }
}

/*==============================================================================
 * Timer callback
 *============================================================================*/
static void led_timer_handler(void * p_context)
{
    (void)p_context;

    /* Error state handled separately */
    if (m_current_state == LED_STATE_ERROR)
    {
        error_pattern_tick();
        return;
    }

    const led_pattern_t * p = &m_patterns[m_current_state];

    if (m_phase_on)
    {
        /* ON -> OFF transition */
        led_all_off();
        m_phase_on = false;

        if (p->off_ms > 0)
        {
            timer_start_ms(p->off_ms);
        }
        else if (!p->repeat)
        {
            /* One-shot: if off_ms == 0, stay on (POWER_ON) or turn off (POWER_OFF) */
            if (m_current_state == LED_STATE_POWER_OFF)
            {
                led_all_off();
                m_current_state = LED_STATE_OFF;
            }
            /* POWER_ON: stay lit, no timer */
        }
    }
    else
    {
        /* OFF -> ON transition */
        if (p->repeat)
        {
            led_color_on(p->color);
            m_phase_on = true;
            timer_start_ms(p->on_ms);
        }
        /* No current case where repeat == false && off_ms > 0 */
    }
}

/*==============================================================================
 * Public functions
 *============================================================================*/

void led_init(void)
{
    /* Configure GPIO outputs */
    nrf_gpio_cfg_output(LED_PIN_GREEN);
    nrf_gpio_cfg_output(LED_PIN_ORANGE);
    led_all_off();

    /* Create timer (single-shot) */
    app_timer_create(&m_led_timer, APP_TIMER_MODE_SINGLE_SHOT, led_timer_handler);

    m_current_state = LED_STATE_OFF;
}

void led_set_state(led_state_t state)
{
    if (state >= LED_STATE_COUNT) return;

    /* Stop previous pattern */
    app_timer_stop(m_led_timer);
    led_all_off();

    m_current_state = state;
    m_phase_on = false;

    const led_pattern_t * p = &m_patterns[state];

    switch (state)
    {
        case LED_STATE_OFF:
            /* Already all off */
            break;

        /* Error pattern: separate state machine */
        case LED_STATE_ERROR:
            error_pattern_start();
            break;

        /* All others: start from ON phase */
        default:
            led_color_on(p->color);
            m_phase_on = true;
            if (p->on_ms > 0)
            {
                timer_start_ms(p->on_ms);
            }
            break;
    }
}

led_state_t led_get_state(void)
{
    return m_current_state;
}
