#include "lamp_diagnostic_runtime.h"
#include "lamp_driver_diagnostics.h"
#include "lamp_dark_init.h"
#include "lamp_i2c_clock.h"
#include "lamp_i2c_owner.h"
#include "lamp_output_pins.h"
#include "stm32g0xx.h"
static lamp_frame_stage frame_stage;
static const lg_guard *frame_guard;
static const lamp_current_profile *frame_profile;
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_output_pins_request_off(); /* Physical requests precede bus cleanup. */
 lamp_frame_stage_abort(&frame_stage);
 lamp_dark_init_abort();
 lamp_driver_diagnostics_abort();
 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)return;
 if(state==LDR_FAULT){lamp_output_pins_request_off();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_FRAME_STAGING:
  lamp_frame_stage_poll(&frame_stage,frame_guard,frame_profile,now);
  if(frame_stage.state==LFS_FAILED){fault();return;}
  if(lamp_frame_stage_done(&frame_stage,frame_guard,frame_profile,now)){
   if(!lamp_driver_diagnostics_resume(now)){fault();return;}
   state=LDR_READING;
  }
  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 || state==LDR_FRAME_STAGING;}
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);
}

bool lamp_diagnostic_runtime_prepare_frame(const lg_guard *g,const lamp_current_profile *p,uint32_t now){
 pca_async_snapshot snapshot;
 if(state!=LDR_READING || !outputs_blank() || !output_pin(GPIOB,9u,true) ||
    !lamp_i2c_clock_available() || !lamp_i2c_owner_idle() ||
    !lamp_driver_diagnostics_get(now,&snapshot))return false;
 if(!lamp_frame_stage_begin(&frame_stage,g,p,now))return false;
 if(!lamp_driver_diagnostics_pause(now)){fault();return false;}
 frame_guard=g;frame_profile=p;state=LDR_FRAME_STAGING;return true;
}
bool lamp_diagnostic_runtime_frame_ready(uint32_t now){
 return state==LDR_READING && outputs_blank() && output_pin(GPIOB,9u,true) &&
 lamp_i2c_clock_available() && lamp_frame_stage_done(&frame_stage,frame_guard,frame_profile,now);
}
