#include "stm32g0xx.h"
#include "lamp_board_inputs.h"
#include "lamp_ir_filter.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
GPIO_TypeDef a,b,c;
RCC_TypeDef r;
static void seed(unsigned prior,unsigned clocks) {
 memset(&a,0,sizeof a);memset(&b,0,sizeof b);memset(&c,prior?255:0,sizeof c);memset(&r,0,sizeof r);
 c.LCKR=0;r.BDCR=clocks|RCC_BDCR_RTCEN;r.IOPENR=RCC_IOPENR_GPIOAEN|RCC_IOPENR_GPIOBEN;
 a.IDR=(1u<<12)|(1u<<0);
}
static lamp_board_input_snapshot sample(uint32_t t,bool high) {
 c.IDR=high?(1u<<15):0u;return lamp_board_inputs_read(t);
}
static lg_inputs simulated_other_safety_ready(lamp_board_input_snapshot s) {
 /* Host-only calibration/driver fixtures: target never makes these true. */
 s.guard_inputs.temperature_ok=true;s.guard_inputs.supply_ok=true;s.guard_inputs.led_driver_ok=true;
 return s.guard_inputs;
}
int main(void) {
 unsigned clockcases=0;
 for(unsigned prior=0;prior<2;prior++) for(unsigned clk=0;clk<8;clk++) {
  const unsigned flags=((clk&1)?RCC_BDCR_LSEON:0)|((clk&2)?RCC_BDCR_LSERDY:0)|((clk&4)?RCC_BDCR_LSEBYP:0);
  seed(prior,flags);uint32_t bd=r.BDCR,moder=c.MODER,pup=c.PUPDR,af=c.AFR[1];
  assert(lamp_board_ir_init()==(flags==0));assert(r.BDCR==bd);
  if(!flags) {
   assert((r.IOPENR&RCC_IOPENR_GPIOCEN)!=0);
   assert(c.MODER==(moder&~(3u<<30)));assert(c.PUPDR==(pup&~(3u<<30)));assert(c.AFR[1]==(af&~(15u<<28)));
  } else {assert(c.MODER==moder && c.PUPDR==pup && c.AFR[1]==af);assert(!(r.IOPENR&RCC_IOPENR_GPIOCEN));assert(!sample(100,true).guard_inputs.infrared_hand_present);}
  clockcases++;
 }
 seed(1,0);c.LCKR=(1u<<16)|(1u<<15);assert(!lamp_board_ir_init());assert(!sample(100,true).infrared_gpio_available);
 seed(0,0);assert(lamp_board_ir_init());
 for(uint32_t t=0;t<=10;t++){lamp_board_input_snapshot s=sample(t,true);assert(s.infrared_gpio_available && s.infrared_raw_high);assert(s.guard_inputs.infrared_hand_present==(t==10));assert(!s.guard_inputs.temperature_ok && !s.guard_inputs.supply_ok && !s.guard_inputs.led_driver_ok);}
 assert(!sample(11,false).guard_inputs.infrared_hand_present);
 for(uint32_t t=12;t<=22;t++)assert(sample(t,true).guard_inputs.infrared_hand_present==(t==22));
 r.BDCR|=RCC_BDCR_LSEON;assert(!sample(23,true).infrared_gpio_available);r.BDCR&=~RCC_BDCR_LSEON;
 for(uint32_t t=24;t<=34;t++)assert(sample(t,true).guard_inputs.infrared_hand_present==(t==34));
 assert(!sample(100,true).guard_inputs.infrared_hand_present);
 for(uint32_t t=101;t<=110;t++)assert(sample(t,true).guard_inputs.infrared_hand_present==(t==110));
 assert(sample(110,true).guard_inputs.infrared_hand_present); /* Same timestamp not a new duration. */
 for(unsigned which=0;which<5;which++) {
  seed(0,0);assert(lamp_board_ir_init());for(uint32_t t=0;t<=10;t++)sample(t,true);
  if(which==0)c.MODER|=1u<<30;if(which==1)c.PUPDR|=1u<<30;if(which==2)c.AFR[1]|=1u<<28;
  if(which==3)r.IOPENR&=~RCC_IOPENR_GPIOCEN;if(which==4)c.LCKR=(1u<<16)|(1u<<15);
  assert(!sample(11,true).infrared_gpio_available);
 }
 seed(0,0);assert(lamp_board_ir_init());uint32_t base=UINT32_MAX-5u;
 for(uint32_t n=0;n<=10;n++)assert(sample(base+n,true).guard_inputs.infrared_hand_present==(n==10));
 assert(!sample(base+11,false).guard_inputs.infrared_hand_present);
 lamp_ir_filter f={0};for(unsigned n=0;n<100;n++)assert(!lamp_ir_filter_update(&f,true,true,0));
 for(uint32_t t=1;t<=10;t++)assert(lamp_ir_filter_update(&f,true,true,t)==(t==10));
 assert(!lamp_ir_filter_update(&f,true,true,5)); /* Backward/ambiguous timestamp. */
 f=(lamp_ir_filter){0};assert(!lamp_ir_filter_update(&f,true,true,0));assert(!lamp_ir_filter_update(&f,true,true,5));assert(lamp_ir_filter_update(&f,true,true,10));
 f=(lamp_ir_filter){0};assert(!lamp_ir_filter_update(&f,true,true,0));assert(!lamp_ir_filter_update(&f,true,true,6));assert(!lamp_ir_filter_update(&f,true,true,11));assert(lamp_ir_filter_update(&f,true,true,16));
 f=(lamp_ir_filter){0};for(uint32_t t=0;t<10;t++)assert(!lamp_ir_filter_update(&f,true,true,t));assert(!lamp_ir_filter_update(&f,true,false,10));
 assert(!lamp_ir_filter_update(NULL,true,true,0));
 /* Physical sampler -> guard path, with other safety inputs only simulated. */
 seed(0,0);assert(lamp_board_ir_init());lg_guard g;const lg_config cfg={.channels=1,.max_run_ms=100,.ticket_lifetime_ms=20,.max_poll_gap_ms=5,.channel_input_mw={1},.input_budget_mw=1,.calibration_id=1};
 assert(lg_init(&g,&cfg,1,0));for(uint32_t t=0;t<=10;t++)lg_poll(&g,t,simulated_other_safety_ready(sample(t,true)));
 assert(lg_arm(&g,LG_AI,50,10));lg_frame ticket;assert(lg_issue_ticket(&g,10,&ticket));ticket.source_frame_seq=1;ticket.hand_present=true;ticket.pwm[0]=1;
 assert(lg_accept_ai(&g,&ticket,10) && g.output_enable);
 lg_poll(&g,11,simulated_other_safety_ready(sample(11,false)));assert(!g.output_enable && !g.pending_ticket && g.armed);assert(!lg_accept_ai(&g,&ticket,11));
 for(uint32_t t=12;t<=22;t++)lg_poll(&g,t,simulated_other_safety_ready(sample(t,true)));
 assert(!g.output_enable);assert(!lg_accept_ai(&g,&ticket,22));
 assert(lg_issue_ticket(&g,22,&ticket));ticket.source_frame_seq=2;ticket.capture_baseline_seq=1;ticket.hand_present=true;ticket.pwm[0]=1;
 assert(lg_accept_ai(&g,&ticket,22) && g.output_enable);
 r.BDCR|=RCC_BDCR_LSEON;lg_poll(&g,23,simulated_other_safety_ready(sample(23,true)));assert(!g.output_enable);
 printf("PASS: %u init clock/prior GPIO cases; PC15 mapping, no RTC mutation, locked/config/clock conflicts, 10ms assertion/instant low/stall/reentry/wrap; physical sampler-to-guard MMIO integration.\n",clockcases);
}
