#include "stm32g4xx.h"

#include <stdint.h>
#include "drill_safe_gpio.h"
#include "drill_pwm_stage.h"
#include "drill_spi_transport.h"
#include "drill_timebase.h"
#include "drill_config_binding.h"
#include "drill_adc_capture.h"
#include "drill_dual_adc.h"
volatile uint32_t drill_debug_timebase_available,drill_debug_now_us;
volatile uint32_t drill_debug_spi_available;
volatile uint32_t drill_debug_pwm_stage_available;

/* STM32G431CBT6 A32 PF1 pin map: only hard-off outputs are configured here.
 * TIM1 is configured stopped after boot; ADC/SPI/I2C/charger and motor run stay disabled. */
_Noreturn static void wait_safe(void) {
    for (;;) __WFI();
}

void drill_safe_boot(void) {
    /* PF1 is OSC_OUT: this image requires cold-reset HSI operation.
     * Reject HSE-enabled entry before changing the shared pad function. */
    if (RCC->CR & (RCC_CR_HSEON | RCC_CR_HSEBYP)) wait_safe();
    const uint16_t gpio_a_outputs =
        (1u << 5u) | (1u << 7u) | (1u << 8u) |
        (1u << 9u) | (1u << 10u) | (1u << 12u);
    const uint16_t gpio_b_outputs = (1u << 0u) | (1u << 1u);
    const uint16_t gpio_f_outputs = (1u << 1u);
    const drill_safe_gpio_set safe_pins = {{
        {&GPIOA->MODER, &GPIOA->OTYPER, &GPIOA->OSPEEDR,
         &GPIOA->PUPDR, &GPIOA->BSRR, gpio_a_outputs, (1u << 12u)},
        {&GPIOB->MODER, &GPIOB->OTYPER, &GPIOB->OSPEEDR,
         &GPIOB->PUPDR, &GPIOB->BSRR, gpio_b_outputs, 0u},
        {&GPIOF->MODER, &GPIOF->OTYPER, &GPIOF->OSPEEDR,
         &GPIOF->PUPDR, &GPIOF->BSRR, gpio_f_outputs, 0u}
    }};

    RCC->AHB2ENR |= RCC_AHB2ENR_GPIOAEN |
                    RCC_AHB2ENR_GPIOBEN |
                    RCC_AHB2ENR_GPIOFEN;
    (void)RCC->AHB2ENR;
    if (!drill_safe_gpio_validate(&safe_pins)) wait_safe();

    /* BSRR is written before MODER so no active pulse occurs on mode change. */
    if (!drill_safe_gpio_preload(&safe_pins)) wait_safe();
    __DSB();
    if (!drill_safe_gpio_configure_outputs(&safe_pins)) wait_safe();
    __DSB();
}

int main(void) {
    drill_safe_boot();
    drill_debug_pwm_stage_available=drill_pwm_stage_init()?1u:0u;
    drill_debug_timebase_available=drill_debug_pwm_stage_available && drill_timebase_init()?1u:0u;
    uint32_t now=0u;
    if(!drill_debug_timebase_available||!drill_timebase_get(&now))wait_safe();
    drill_debug_spi_available=drill_spi_init(now)?1u:0u;
    if(!drill_debug_spi_available)wait_safe();
    for(;;){
        drill_debug_timebase_available=drill_timebase_get(&now)?1u:0u;
        if(!drill_debug_timebase_available){drill_spi_abort();wait_safe();}
        drill_debug_now_us=now;
        drill_spi_poll(now);
        drill_config_service(now);
        drill_adc_poll(now);
        drill_dual_poll(now);
        if(!drill_dual_busy() && !drill_adc_busy() && !drill_spi_active() && drill_config.state!=DC_SUBMIT && drill_config.state!=DC_RECEIVE)__WFI();
    }
}
