#include "stm32g0xx.h"
#include "lamp_i2c_clock.h"
#include "lamp_diagnostic_runtime.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
GPIO_TypeDef a,b,c;SYSCFG_TypeDef s;RCC_TypeDef r;I2C_TypeDef i;
static bool pins=true;
bool lamp_i2c_pins_available(void){return pins;}
static void gpio_effect(void){
 uint32_t v=b.BSRR;b.ODR=(b.ODR|(v&0xffffu))&~(v>>16);b.IDR=(b.IDR&~(1u<<9))|(b.ODR&(1u<<9));b.BSRR=0;
}
static void service(uint32_t now){lamp_diagnostic_runtime_poll(now);gpio_effect();assert(a.BSRR==0);}
static void fault_expected(uint32_t now){
 pca_async_snapshot out;memset(&out,0x5a,sizeof out);pca_async_snapshot old=out;
 assert(lamp_diagnostic_runtime_state()==LDR_FAULT);assert((b.ODR&(1u<<9))==0);
 assert(!lamp_diagnostic_runtime_needs_poll());assert(!lamp_diagnostic_runtime_get(now,&out));assert(memcmp(&out,&old,sizeof out)==0);
 uint32_t cr2=i.CR2;service(now+1);assert(i.CR2==cr2);assert(lamp_diagnostic_runtime_state()==LDR_FAULT);
}
static void corrupt(unsigned n){
 switch(n){
 case 0:a.ODR|=1u<<4;a.IDR|=1u<<4;break;
 case 1:a.ODR&=~(1u<<7);a.IDR&=~(1u<<7);break;
 case 2:a.ODR|=1u<<11;a.IDR|=1u<<11;break;
 case 3:a.MODER&=~(3u<<8);break;
 case 4:a.MODER&=~(3u<<14);break;
 case 5:a.MODER&=~(3u<<22);break;
 case 6:a.OTYPER|=1u<<4;break;
 case 7:a.OTYPER|=1u<<7;break;
 case 8:a.OTYPER|=1u<<11;break;
 case 9:r.IOPENR&=~RCC_IOPENR_GPIOAEN;break;
 case 10:r.IOPENR&=~RCC_IOPENR_GPIOBEN;break;
 case 11:b.MODER&=~(3u<<18);break;
 case 12:b.OTYPER|=1u<<9;break;
 case 13:b.IDR^=1u<<9;break;
 case 14:b.ODR^=1u<<9;break;
 case 15:r.CR&=~RCC_CR_HSIRDY;break;
 case 16:r.CCIPR=0;break;
 case 17:i.TIMINGR^=1;break;
 case 18:i.CR1|=I2C_CR1_ANFOFF;break;
 case 19:pins=false;break;
 case 20:a.IDR^=1u<<4;break;
 case 21:a.IDR^=1u<<7;break;
 case 22:a.IDR^=1u<<11;break;
 case 23:r.APBENR2&=~RCC_APBENR2_SYSCFGEN;break;
 case 24:s.CFGR1|=SYSCFG_CFGR1_PA11_RMP;break;
 case 25:s.CFGR1|=SYSCFG_CFGR1_PA12_RMP;break;
 case 26:r.BDCR|=RCC_BDCR_LSEON;break;
 case 27:c.MODER=1u<<28;break;
 case 28:r.IOPENR&=~RCC_IOPENR_GPIOCEN;break;
 case 29:c.PUPDR=1u<<28;break;
 default:assert(0);
 }
}
int main(int argc,char **argv){
 unsigned scenario=argc>1?(unsigned)atoi(argv[1]):0;
 a.MODER=(1u<<8)|(1u<<14)|(1u<<22);a.ODR=a.IDR=1u<<7;
 b.MODER=1u<<18;b.IDR=(1u<<6)|(1u<<7);
 r.APBENR2=RCC_APBENR2_SYSCFGEN;c.MODER=3u<<28;r.IOPENR=RCC_IOPENR_GPIOAEN|RCC_IOPENR_GPIOBEN|RCC_IOPENR_GPIOCEN;r.CR=RCC_CR_HSION|RCC_CR_HSIRDY;r.APBENR1=RCC_APBENR1_I2C1EN;
 assert(lamp_i2c_clock_init());
 if(scenario>=100 && scenario<130)corrupt(scenario-100);
 if(scenario==404){
  lamp_diagnostic_runtime_init(UINT32_MAX-2u);gpio_effect();
  service(UINT32_MAX-1u);service(UINT32_MAX);service(0);
  assert(lamp_diagnostic_runtime_state()==LDR_RESET_RECOVERY);
  service(1);service(2);service(3);assert(lamp_diagnostic_runtime_state()==LDR_READING);
  assert(i.CR2&I2C_CR2_START);return 0;
 }
 lamp_diagnostic_runtime_init(0);gpio_effect();
 if(scenario>=100 && scenario<130){fault_expected(0);return 0;}
 assert(lamp_diagnostic_runtime_state()==LDR_RESET_HOLD);
 service(0);service(1);service(2);assert(i.CR2==0 && !(b.ODR&(1u<<9)));
 service(3);assert(lamp_diagnostic_runtime_state()==LDR_RESET_RECOVERY && (b.ODR&(1u<<9)));
 service(4);service(5);assert(i.CR2==0);
 if(scenario>=200 && scenario<230){corrupt(scenario-200);service(6);fault_expected(6);return 0;}
 service(6);assert(lamp_diagnostic_runtime_state()==LDR_READING && (i.CR2&I2C_CR2_START));
 assert(lamp_diagnostic_runtime_needs_poll());
 if(scenario>=300 && scenario<330){corrupt(scenario-300);service(6);fault_expected(6);return 0;}
 if(scenario==400){service(12);fault_expected(12);return 0;}
 if(scenario==401){service(5);fault_expected(5);return 0;}
 if(scenario==402){lamp_diagnostic_runtime_init(6);gpio_effect();fault_expected(6);return 0;}
 if(scenario==403){i.ISR=I2C_ISR_BUSY;service(6);service(11);service(12);fault_expected(12);return 0;}
 unsigned tick=0;uint32_t now=6; pca_async_snapshot out;
 for(unsigned tx=0;tx<48;tx++){
  if(tx){i.ISR=0;now=6+tick/16u;service(now);tick++;}
  if(scenario>=500 && scenario<524 && tx==scenario-500){i.ISR=I2C_ISR_NACKF|I2C_ISR_BUSY;service(now);fault_expected(now);assert(!(i.CR1&I2C_CR1_PE));return 0;}
  i.ISR=I2C_ISR_TXIS|I2C_ISR_BUSY;service(6+tick++/16u);
  i.ISR=I2C_ISR_TC|I2C_ISR_BUSY;service(6+tick++/16u);
  i.RXDR=0;i.ISR=I2C_ISR_RXNE|I2C_ISR_BUSY;service(6+tick++/16u);
  assert(!lamp_diagnostic_runtime_get(6+tick/16u,&out));
  i.ISR=I2C_ISR_STOPF;now=6+tick++/16u;service(now);
  assert(lamp_diagnostic_runtime_state()==LDR_READING);
  bool valid=lamp_diagnostic_runtime_get(now,&out);assert(valid==((tx+1)%24==0));
  if(valid){assert(out.sequence==(tx+1)/24);for(unsigned j=0;j<3;j++)assert(out.chip[j].eligible_mask==0);}
 }
 assert(lamp_diagnostic_runtime_get(now,&out));
 assert(!lamp_diagnostic_runtime_get(now+21,&out));
 puts("PASS runtime reset/recovery, two diagnostic banks, zero eligible masks; MMIO simulation only");
}
