#include "stm32g0xx.h"

#include "fan_manager.h"
#include "vacuum_board.h"

#include <stdbool.h>
#include <stdint.h>

static volatile uint32_t g_millis;
static vacuum_fan_manager_t g_fan_manager;
static const vacuum_fan_io_t g_fan_io = {
    .context = 0,
    .set_fan_power = vacuum_board_set_fan_power,
    .set_control_duty_permille = vacuum_board_set_fan_duty,
};

void SysTick_Handler(void)
{
    ++g_millis;
}

void EXTI0_1_IRQHandler(void)
{
    if ((EXTI->RPR1 & EXTI_RPR1_RPIF1) != 0u) {
        EXTI->RPR1 = EXTI_RPR1_RPIF1;
        vacuum_board_note_tach_edge();
    }
}

static bool init_timebase(void)
{
    SystemCoreClockUpdate();
    g_millis = 0u;
    return SysTick_Config(SystemCoreClock / 1000u) == 0u;
}

static vacuum_fan_status_t service_fan_policy(const vacuum_board_sample_t *sample)
{
    const vacuum_fan_input_t input = {
        .sample_age_ms = 0u,
        .vbus_mv = sample->estimated_vbus_mv,
        .fan_current_ma = sample->estimated_current_ma,
        .control_duty_permille = 0u,
        /* Nominal ADC conversions are diagnostic only until bench calibration. */
        .measurement_valid = false,
        .fan_profile_valid = false,
        /* Treat PA2 as a provisional run request only to exercise fail-closed policy. */
        .run_requested = sample->key_pressed,
    };
    const vacuum_fan_limits_t limits = {0u, 0u, 0u, 0u};

    return vacuum_fan_manager_step(&g_fan_manager, &input, &limits, &g_fan_io);
}

int main(void)
{
    vacuum_board_sample_t latest = {0};
    uint32_t next_sample_ms = 0u;

    vacuum_board_force_off();
    if (!init_timebase() || !vacuum_board_init()) {
        vacuum_board_force_off();
        for (;;) {
            __WFI();
        }
    }

    vacuum_fan_manager_init(&g_fan_manager, &g_fan_io);
    next_sample_ms = g_millis + 100u;

    for (;;) {
        const uint32_t now_ms = g_millis;

        if ((int32_t)(now_ms - next_sample_ms) >= 0) {
            next_sample_ms = now_ms + 100u;
            if (!vacuum_board_sample(&latest, now_ms)) {
                vacuum_board_force_off();
                for (;;) {
                    __WFI();
                }
            }
            latest.fan_status = (uint8_t)service_fan_policy(&latest);
            latest.fan_power_enabled = g_fan_manager.fan_power_enabled;
            latest.fan_power_command_confirmed = g_fan_manager.fan_power_command_confirmed;
            latest.fan_fault_latched = g_fan_manager.fault_latched;
            vacuum_board_publish_diagnostics(&latest);
        }

        /* Independent target-side hard lock also rejects any nonzero request. */
        vacuum_board_force_fan_disabled();
        __WFI();
    }
}
