#include "stm32g0xx.h"
#include "lamp_i2c_clock.h"
#include "lamp_i2c_owner.h"
#include "lamp_i2c_read.h"
#include "lamp_i2c_write.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
GPIO_TypeDef b;RCC_TypeDef r;I2C_TypeDef i;
bool lamp_i2c_pins_available(void){return true;}
static lamp_i2c_write w;static lamp_i2c_read read1;
static void seed(void){
 lamp_i2c_write_reset(&w);lamp_i2c_read_reset(&read1);
 memset(&i,0,sizeof i);memset(&r,0,sizeof r);memset(&b,0,sizeof b);
 r.CR=RCC_CR_HSION|RCC_CR_HSIRDY;r.APBENR1=RCC_APBENR1_I2C1EN;b.IDR=(1u<<6)|(1u<<7);
 assert(lamp_i2c_clock_init());assert(!lamp_i2c_owner_is(&w));
}
static void start(uint8_t addr,uint8_t control,unsigned n,uint32_t now){
 uint8_t bytes[25];bytes[0]=control;for(unsigned j=1;j<n;j++)bytes[j]=(uint8_t)(j+20);
 assert(lamp_i2c_write_start(&w,addr,bytes,n,now));
 memset(bytes,0,sizeof bytes);assert(w.bytes[0]==control); /* caller may reuse buffer */
 assert(lamp_i2c_owner_is(&w));assert((i.CR2&I2C_CR2_NBYTES)==(n<<I2C_CR2_NBYTES_Pos));
 assert((i.CR2&I2C_CR2_AUTOEND)!=0 && !(i.CR2&I2C_CR2_RD_WRN));
}
static void byte(unsigned j,uint32_t now){
 i.ISR=I2C_ISR_TXIS|I2C_ISR_BUSY;lamp_i2c_write_poll(&w,now);
 assert(i.TXDR==w.bytes[j]);assert(w.sent==j+1);assert(!lamp_i2c_write_done(&w,now));
}
int main(void){
 unsigned successes=0,errors=0,validation=0;uint8_t addresses[]={0x15,0x19,0x65};
 unsigned sizes[]={2,25,25};uint8_t controls[]={0,0x8a,0xa2};uint32_t bases[]={0,UINT32_MAX-2u};
 for(unsigned a=0;a<3;a++)for(unsigned k=0;k<3;k++)for(unsigned z=0;z<2;z++){
  seed();uint32_t now=bases[z];start(addresses[a],controls[k],sizes[k],now);
  for(unsigned j=0;j<sizes[k];j++)byte(j,now);
  assert(w.state==LIW_STOP);i.ISR=I2C_ISR_STOPF|I2C_ISR_BUSY;lamp_i2c_write_poll(&w,now);assert(!lamp_i2c_write_done(&w,now));
  i.ISR=I2C_ISR_STOPF;lamp_i2c_write_poll(&w,now+1u);assert(lamp_i2c_write_done(&w,now+1u));assert(!lamp_i2c_owner_is(&w));assert(i.CR2==0);
  assert(!lamp_i2c_write_done(&w,now+7u));successes++;
 }
 uint32_t flags[]={I2C_ISR_NACKF,I2C_ISR_BERR,I2C_ISR_ARLO,I2C_ISR_OVR,I2C_ISR_TIMEOUT};
 for(unsigned k=0;k<3;k++)for(unsigned pos=0;pos<=sizes[k];pos++)for(unsigned e=0;e<5;e++){
  seed();start(0x15,controls[k],sizes[k],0);for(unsigned j=0;j<pos;j++)byte(j,0);
  i.ISR=flags[e]|I2C_ISR_BUSY;uint32_t cr2=i.CR2;lamp_i2c_write_poll(&w,1);
  assert(w.state==LIW_FAILED && !lamp_i2c_write_done(&w,1));assert(!(i.CR1&I2C_CR1_PE));assert(!lamp_i2c_owner_is(&w));
  assert((i.CR2&I2C_CR2_STOP)==(e==2?(cr2&I2C_CR2_STOP):I2C_CR2_STOP));
  uint32_t after=i.CR2;i.ISR=I2C_ISR_BUSY;lamp_i2c_write_cancel(&w);assert(i.CR2==after);errors++;
 }
 for(unsigned addr=0;addr<256;addr++)for(unsigned reg=0;reg<256;reg++){
  seed();uint8_t p[2]={(uint8_t)reg,123};bool expected=(addr==0x15||addr==0x19||addr==0x65)&&(reg<=0x39||reg==0x3f||reg==0x40);
  uint32_t before=i.CR2;assert(lamp_i2c_write_start(&w,addr,p,2,0)==expected);
  if(!expected){assert(i.CR2==before);assert(!lamp_i2c_owner_is(&w));}validation++;
 }
 for(unsigned n=0;n<27;n++){
  seed();uint8_t p[27]={0x8a};assert(lamp_i2c_write_start(&w,0x15,p,n,0)==(n==25));
 }
 seed();start(0x15,0,2,0);i.ISR=0;uint32_t before=i.CR2;
 assert(!lamp_i2c_read_start(&read1,0x15,1,0));assert(i.CR2==before);
 lamp_i2c_write_cancel(&w);assert(!lamp_i2c_owner_is(&w));
 seed();assert(lamp_i2c_read_start(&read1,0x15,1,0));i.ISR=0;before=i.CR2;uint8_t p[2]={0,0};
 assert(!lamp_i2c_write_start(&w,0x15,p,2,0));assert(i.CR2==before);
 lamp_i2c_write_cancel(&w);assert(i.CR2==before && lamp_i2c_owner_is(&read1));
 /* A different token cannot release the reader; software lock tested even with BUSY low. */
 lamp_i2c_owner_release(&w);assert(lamp_i2c_owner_is(&read1));
 lamp_i2c_read_cancel(&read1);assert(!lamp_i2c_owner_is(&read1));
 for(unsigned fault=0;fault<6;fault++){
  seed();start(0x15,0,2,100);
  if(fault==0)lamp_i2c_write_poll(&w,106);
  if(fault==1)lamp_i2c_write_poll(&w,99);
  if(fault==2){i.TIMINGR^=1;lamp_i2c_write_poll(&w,100);}
  if(fault==3){i.ISR=I2C_ISR_STOPF;lamp_i2c_write_poll(&w,100);}
  if(fault==4){i.ISR=I2C_ISR_RXNE;lamp_i2c_write_poll(&w,100);}
  if(fault==5){byte(0,100);byte(1,100);i.ISR=I2C_ISR_TXIS;lamp_i2c_write_poll(&w,100);}
  assert(w.state==LIW_FAILED && !lamp_i2c_write_done(&w,100));assert(!lamp_i2c_owner_is(&w));
 }
 seed();assert(!lamp_i2c_owner_acquire(NULL));assert(!lamp_i2c_write_start(NULL,0x15,p,2,0));assert(!lamp_i2c_write_start(&w,0x15,NULL,2,0));
 printf("PASS writer: %u complete frames, %u byte/STOP-position hardware errors, %u address/control combinations, 27 lengths, collision/cancel/time/config tests; MMIO only\n",successes,errors,validation);
}
