#include "lamp_diagnostic_runtime.h"
#include "lamp_driver_diagnostics.h"
#include "lamp_dark_init.h"
#include "lamp_i2c_clock.h"
#include "stm32g0xx.h"
static lamp_diag_state state;
static uint32_t entered,last_poll;
static bool initialized;
static bool output_pin(GPIO_TypeDef *p,unsigned pin,bool high){
 uint32_t bit=1u<<pin,shift=pin*2u;
 return ((p->MODER>>shift)&3u)==1u && (p->OTYPER&bit)==0u &&
        (p->ODR&bit)==(high?bit:0u) && (p->IDR&bit)==(high?bit:0u);
}
static bool outputs_blank(void){
 return (RCC->IOPENR&(RCC_IOPENR_GPIOAEN|RCC_IOPENR_GPIOBEN))==
         (RCC_IOPENR_GPIOAEN|RCC_IOPENR_GPIOBEN) &&
        (RCC->APBENR2&RCC_APBENR2_SYSCFGEN)!=0u &&
        (SYSCFG->CFGR1&(SYSCFG_CFGR1_PA11_RMP|SYSCFG_CFGR1_PA12_RMP))==0u &&
        (RCC->BDCR&RCC_BDCR_LSEON)==0u &&
        (RCC->IOPENR&RCC_IOPENR_GPIOCEN)!=0u &&
        ((GPIOC->MODER>>28)&3u)==3u && (GPIOC->PUPDR&(3u<<28))==0u &&
        output_pin(GPIOA,4u,false) && output_pin(GPIOA,7u,true) &&
        output_pin(GPIOA,11u,false);
}
static void fault(void){
 lamp_dark_init_abort();
 lamp_driver_diagnostics_abort();
 /* Reassert the reset latch without changing an unexpected pin mux. */
 GPIOB->BSRR=1u<<(9u+16u);__DSB();
 state=LDR_FAULT;
}
void lamp_diagnostic_runtime_init(uint32_t now){
 /* Never restart over a previous transport owner or a latched failure. */
 if(initialized){fault();return;}
 initialized=true;entered=last_poll=now;state=LDR_RESET_HOLD;
 if(!outputs_blank() || !output_pin(GPIOB,9u,false) ||
    !lamp_i2c_clock_available())fault();
}
void lamp_diagnostic_runtime_poll(uint32_t now){
 if(!initialized || state==LDR_FAULT)return;
 if(now-last_poll>5u || !outputs_blank() || !lamp_i2c_clock_available() ||
    !output_pin(GPIOB,9u,state!=LDR_RESET_HOLD)){fault();return;}
 last_poll=now;
 switch(state){
 case LDR_RESET_HOLD:
  /* 3 tick differences imply >=2 ms at a 1 ms SysTick, far above 2.5 us.
   * Physical clock accuracy and reset waveform remain unqualified. */
  if(now-entered>=3u){GPIOB->BSRR=1u<<9u;__DSB();entered=now;state=LDR_RESET_RECOVERY;}
  break;
 case LDR_RESET_RECOVERY:
  /* >=2 ms at 1 ms resolution exceeds datasheet 1.5 ms recovery. */
  if(now-entered>=3u){
   if(!lamp_dark_init_begin(now)){fault();return;}
   state=LDR_CONFIGURING;lamp_dark_init_poll(now);
  }
  break;
 case LDR_CONFIGURING:
  lamp_dark_init_poll(now);
  if(lamp_dark_init_failed()){fault();return;}
  if(lamp_dark_init_done()){
   const uint32_t eligible[3]={0u,0u,0u};
   if(!lamp_driver_diagnostics_init(eligible,now)){fault();return;}
   state=LDR_READING;
   lamp_driver_diagnostics_poll(now);
   if(lamp_driver_diagnostics_failed())fault();
  }
  break;
 case LDR_READING:
  lamp_driver_diagnostics_poll(now);
  if(lamp_driver_diagnostics_failed())fault();
  break;
 default:fault();break;
 }
}
lamp_diag_state lamp_diagnostic_runtime_state(void){return state;}
bool lamp_diagnostic_runtime_needs_poll(void){return state==LDR_READING || state==LDR_CONFIGURING;}
bool lamp_diagnostic_runtime_get(uint32_t now,pca_async_snapshot *out){
 return state==LDR_READING && outputs_blank() &&
        output_pin(GPIOB,9u,true) && lamp_i2c_clock_available() &&
        lamp_driver_diagnostics_get(now,out);
}
