#include "stm32g0xx.h"
#include "lamp_board_inputs.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
GPIO_TypeDef a,b,c;SYSCFG_TypeDef s;RCC_TypeDef r;
static void seed(unsigned bits){
 memset(&a,0,sizeof a);memset(&b,0,sizeof b);memset(&c,0,sizeof c);memset(&s,0,sizeof s);memset(&r,0,sizeof r);
 r.IOPENR=RCC_IOPENR_GPIOAEN|RCC_IOPENR_GPIOBEN|RCC_IOPENR_GPIOCEN;r.APBENR2=RCC_APBENR2_SYSCFGEN;
 a.MODER=0xffffffffu&~((3u<<0)|(3u<<24));a.PUPDR=(2u<<0)|(2u<<24);
 b.MODER=0xffffffffu&~3u;b.PUPDR=1u;c.MODER=0xffffffffu;
 a.IDR=((bits&1u)?1u:0u)|((bits&2u)?1u<<12:0u)|((bits&16u)?1u<<10:0u);
 b.IDR=(bits&4u)?0u:1u;c.IDR=(bits&8u)?1u<<15:0u;
 assert(lamp_board_ir_init());
}
static lamp_board_input_snapshot sample(uint32_t now){
 GPIO_TypeDef pa=a,pb=b,pc=c;SYSCFG_TypeDef ps=s;RCC_TypeDef pr=r;
 lamp_board_input_snapshot v=lamp_board_inputs_read(now);
 assert(memcmp(&a,&pa,sizeof a)==0 && memcmp(&b,&pb,sizeof b)==0 && memcmp(&c,&pc,sizeof c)==0 && memcmp(&s,&ps,sizeof s)==0 && memcmp(&r,&pr,sizeof r)==0);
 assert(!v.guard_inputs.temperature_ok && !v.guard_inputs.supply_ok && !v.guard_inputs.led_driver_ok);
 return v;
}
static void unavailable(unsigned input){
 lamp_board_input_snapshot v=sample(0);
 if(input==0)assert(!v.interlock_gpio_available && !v.hardware_interlock_closed && !v.guard_inputs.hardware_interlock_ok);
 if(input==1)assert(!v.thermal_gpio_available && !v.thermal_contact_closed);
 if(input==2)assert(!v.key_gpio_available && !v.user_key_pressed);
}
int main(void){
 unsigned cases=0;
 for(unsigned bits=0;bits<32;bits++){
  seed(bits);lamp_board_input_snapshot v=sample(0);
  assert(v.interlock_gpio_available && v.thermal_gpio_available && v.key_gpio_available && v.infrared_gpio_available);
  assert(v.hardware_interlock_closed==((bits&1u)!=0));assert(v.thermal_contact_closed==((bits&2u)!=0));
  assert(v.user_key_pressed==((bits&4u)!=0));assert(v.infrared_raw_high==((bits&8u)!=0));
  assert(!v.guard_inputs.infrared_hand_present);sample(5);v=sample(10);
  assert(v.guard_inputs.infrared_hand_present==((bits&8u)!=0));cases++;
 }
 for(unsigned input=0;input<3;input++)for(unsigned mode=0;mode<4;mode++)for(unsigned pull=0;pull<4;pull++){
  seed(15);GPIO_TypeDef *port=input==2?&b:&a;unsigned n=input==0?0:(input==1?12:0),shift=n*2;
  port->MODER=(port->MODER&~(3u<<shift))|(mode<<shift);port->PUPDR=(port->PUPDR&~(3u<<shift))|(pull<<shift);
  bool valid=mode==0 && pull==(input==2?1:2);
  if(!valid)unavailable(input);else {lamp_board_input_snapshot v=sample(0);assert(input==0?v.hardware_interlock_closed:(input==1?v.thermal_contact_closed:v.user_key_pressed));}
  cases++;
 }
 for(unsigned alias=0;alias<3;alias++)for(unsigned mode=0;mode<4;mode++)for(unsigned pull=0;pull<4;pull++){
  seed(15);GPIO_TypeDef *port=alias==2?&a:&b;unsigned n=alias==2?8:alias+1,shift=n*2;
  port->MODER=(port->MODER&~(3u<<shift))|(mode<<shift);port->PUPDR=(port->PUPDR&~(3u<<shift))|(pull<<shift);
  if(mode==3 && pull==0)assert(sample(0).user_key_pressed);else unavailable(2);cases++;
 }
 seed(15);r.IOPENR&=~RCC_IOPENR_GPIOAEN;unavailable(0);unavailable(1);unavailable(2);cases++;
 seed(15);r.IOPENR&=~RCC_IOPENR_GPIOBEN;unavailable(2);cases++;
 seed(15);r.APBENR2&=~RCC_APBENR2_SYSCFGEN;unavailable(1);cases++;
 seed(15);s.CFGR1|=SYSCFG_CFGR1_PA12_RMP;unavailable(1);cases++;
 seed(15);s.CFGR1|=SYSCFG_CFGR1_PA11_RMP;assert(sample(0).thermal_contact_closed);cases++;
 seed(15);a.IDR&=~(1u<<12);a.IDR|=1u<<10;assert(!sample(0).thermal_contact_closed);cases++;
 seed(15);sample(0);sample(5);assert(sample(10).guard_inputs.infrared_hand_present);
 r.IOPENR&=~RCC_IOPENR_GPIOCEN;assert(!sample(11).infrared_gpio_available);assert(!sample(11).guard_inputs.infrared_hand_present);cases++;
 seed(15);sample(0);sample(5);assert(sample(10).guard_inputs.infrared_hand_present);r.BDCR|=RCC_BDCR_LSEON;assert(!sample(11).guard_inputs.infrared_hand_present);cases++;
 seed(15);a.LCKR=b.LCKR=0x10001;assert(sample(0).hardware_interlock_closed && sample(0).user_key_pressed);cases++;
 for(unsigned fault=0;fault<4;fault++){
  seed(15);sample(0);sample(5);assert(sample(10).guard_inputs.infrared_hand_present);
  lg_guard g;lg_frame frame;
  const lg_config cfg={.channels=1,.max_run_ms=100,.ticket_lifetime_ms=20,.max_poll_gap_ms=10,.channel_input_mw={1},.input_budget_mw=1,.calibration_id=1};
  const lg_inputs good={true,true,true,true,true};
  assert(lg_init(&g,&cfg,1,10));lg_poll(&g,10,good);assert(lg_arm(&g,LG_AI,50,10));
  assert(lg_issue_ticket(&g,10,&frame));frame.source_frame_seq=1;frame.hand_present=true;frame.pwm[0]=1;
  assert(lg_accept_ai(&g,&frame,10) && g.output_enable);
  if(fault==1)a.MODER|=1u; /* incorrectly configured interlock output */
  if(fault==2)a.PUPDR&=~3u;
  if(fault==3)r.IOPENR&=~RCC_IOPENR_GPIOAEN;
  lamp_board_input_snapshot v=sample(11);lg_inputs isolated=v.guard_inputs;
  /* Synthetic, separately approved other inputs isolate the interlock route.
   * Actual sampler still reports unqualified ADC/driver inputs false. */
  isolated.temperature_ok=isolated.supply_ok=isolated.led_driver_ok=true;
  lg_poll(&g,11,isolated);
  assert(g.output_enable==(fault==0));
  if(fault)assert(g.mode==LG_FAULT && !g.armed && !g.pending_ticket);
  cases++;
 }
 printf("PASS %u board-input cases: raw combinations, mode/pull matrices, key aliases, clocks/remapping, read-only register snapshots and IR withdrawal; model only\n",cases);
}
