#include "stm32g4xx.h"

#include <stdint.h>
#include "drill_safe_gpio.h"
#include "drill_clock.h"
#include "drill_pwm_stage.h"
#include "drill_spi_transport.h"
#include "drill_timebase.h"
#include "drill_timing.h"
#include "drill_timing_snapshot.h"
#include "drill_config_binding.h"
#include "drill_adc_capture.h"
#include "drill_dual_adc.h"
_Static_assert(sizeof(dt_snapshot)==104u,"timing snapshot ABI");
volatile dt_snapshot drill_debug_timing_capture;
volatile uint32_t drill_debug_timing_capture_request,drill_debug_timing_capture_epoch;
volatile du_sample drill_debug_stream_sample;
volatile uint32_t drill_debug_stream_epoch;
volatile uint32_t drill_debug_stream_received;
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();
}

/* Shared real foreground step, also exposes the exact linked path for profiling. */
bool drill_foreground_service(uint32_t now) {
        drill_debug_now_us=now;
        drill_spi_poll(now);
        /* READY is terminal: the STREAM guard below checks READY, SPI/driver
         * pins, nFAULT and monotonic time. Polling that same READY monitor here
         * repeats the timer/pin scan. Other config phases remain serviced. */
        if(drill_dual_state!=DU_STREAM || drill_config.state!=DC_READY)drill_config_service(now);
        drill_adc_poll(now);
        /* STREAM take performs its own poll and completion validation. */
        if(drill_dual_state!=DU_STREAM)drill_dual_poll(now);
        du_sample incoming;
        if(drill_dual_stream_take(&incoming,now)){
            ++drill_debug_stream_epoch;__DMB();
            drill_debug_stream_sample=incoming;__DMB();
            ++drill_debug_stream_epoch;
            ++drill_debug_stream_received;
        }
        return !drill_dual_busy() && !drill_adc_busy() && !drill_spi_active() && drill_config.state!=DC_SUBMIT && drill_config.state!=DC_RECEIVE;
}

/* One retained SWD snapshot per explicit request; a fault forces a final
 * snapshot after hardware shutdown. No reset/start/run command is implied. */
static void drill_capture_timing(bool force){
 if(!force&&drill_debug_timing_capture_request!=1u)return;
 ++drill_debug_timing_capture_epoch;__DMB();
 drill_debug_timing_capture.timing=drill_debug_timing;
 drill_debug_timing_capture.sample=drill_debug_stream_sample;
 drill_debug_timing_capture.dual_state=(uint32_t)drill_dual_state;
 drill_debug_timing_capture.config_state=(uint32_t)drill_config.state;
 drill_debug_timing_capture.stream_received=drill_debug_stream_received;
 drill_debug_timing_capture.stream_epoch=drill_debug_stream_epoch;
 __DMB();++drill_debug_timing_capture_epoch;drill_debug_timing_capture_request=0;
}

/* Measured window stays odd across service/stream publication. A reader cannot
 * accept the intermediate stream snapshot before timing is known to pass. */
bool drill_timed_foreground(bool *idle){
 uint32_t now;
 if(!idle){(void)drill_timing_begin((uint32_t *)0);}
 else if(drill_timing_begin(&now)){
  drill_debug_timebase_available=1;
  *idle=drill_foreground_service(now);
  if(drill_timing_end(*idle)){drill_capture_timing(false);return true;}
 }
 drill_dual_abort();drill_adc_abort();drill_spi_abort();
 drill_config.state=DC_FAILED;drill_config.error=DC_TIME;
 drill_capture_timing(true);return false;
}

int main(void) {
    drill_safe_boot();
    if(!drill_clock_init())wait_safe();
    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();
    if(!drill_timing_init()){drill_spi_abort();wait_safe();}
    for(;;){
        bool idle=false;
        if(!drill_timed_foreground(&idle))wait_safe();
        if(idle)__WFI();
    }
}
