#include "soft_i2c.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
typedef struct {
 bool scl,sda,high,active,slave_high,reading,pending_read,freeze,stuck_clock,stuck_data,wait_stop;
 unsigned bit,tx_n,rx_n,starts,stops,ack_n,nack_at,fall_delay,stretch,step,violations,fail_after_rx;
 uint16_t time,raised,lowered;
 uint8_t accum,tx[3],rx[17],acks[17];
} model;
static uint16_t tick(void*c){model*m=c;if(!m->freeze)m->time+=(uint16_t)m->step;return m->time;}
static void clock_set(void*c,bool r){model*m=c;if(m->scl!=r){if(r)m->raised=m->time;else m->lowered=m->time;}m->scl=r;}
static bool clock_get(void*c){model*m=c;
 if(!m->scl){
  if(m->high && (uint16_t)(m->time-m->lowered)<m->fall_delay)return true;
  if(m->high&&m->pending_read){m->reading=true;m->pending_read=false;}
  m->high=false;return false;
 }
 if((m->fail_after_rx&&m->rx_n>=m->fail_after_rx)||m->stuck_clock || (m->active&&(uint16_t)(m->time-m->raised)<m->stretch))return false;
 if(!m->high){
  m->high=true;m->slave_high=true;
  if(m->active&&!m->wait_stop){
   if(!m->reading){
    if(m->bit<8){m->accum=(uint8_t)((m->accum<<1)|m->sda);++m->bit;}
    else {
     assert(m->tx_n<3);m->tx[m->tx_n++]=m->accum;m->accum=0;m->bit=0;
     m->slave_high=m->tx_n==m->nack_at;
     if(m->tx_n==3&&!m->slave_high)m->pending_read=true;
    }
   }else{
    if(m->bit<8){assert(m->rx_n<17);m->slave_high=(m->rx[m->rx_n]&(0x80u>>m->bit))!=0;++m->bit;}
    else{assert(m->rx_n<17);m->acks[m->rx_n++]=m->sda;m->wait_stop=m->sda;m->bit=0;m->slave_high=true;}
   }
  }
 }
 return true;
}
static bool data_get(void*c){model*m=c;return m->sda&&m->slave_high&&!m->stuck_data;}
static void data_set(void*c,bool r){model*m=c;
 if(m->high&&!m->scl&&r!=m->sda)++m->violations;
 if(m->high&&m->scl&&r!=m->sda){
  if(!r){++m->starts;m->active=true;m->wait_stop=false;m->reading=false;m->pending_read=false;m->bit=0;m->accum=0;}
  else{++m->stops;m->active=false;m->slave_high=true;}
 }
 m->sda=r;
}
static si_bus bus(model*m){return(si_bus){m,clock_set,data_set,clock_get,data_get,tick,SI_OK,0,0};}
static model initial(void){model m={0};m.scl=m.sda=m.high=m.slave_high=true;m.step=1;return m;}
static void waveform(model*m,unsigned addr,unsigned reg,unsigned n,const uint8_t*out){
 assert(m->starts==2&&m->stops==1&&m->tx_n==3);
 assert(m->tx[0]==addr*2&&m->tx[1]==reg&&m->tx[2]==addr*2+1);
 assert(m->rx_n==n&&!memcmp(out,m->rx,n));
 for(unsigned i=0;i<n;i++)assert(m->acks[i]==(i+1==n));
 assert(m->scl&&m->sda&&m->violations==0);
}
int main(void){
 uint8_t out[17];unsigned count=0;
 for(unsigned a=0;a<128;a++)for(unsigned n=1;n<=17;n++){
  model m=initial();for(unsigned i=0;i<n;i++)m.rx[i]=(uint8_t)(a+i*39u);
  si_bus b=bus(&m);assert(si_read_register(&b,(uint8_t)a,0x04,out,n)&&b.status==SI_OK);waveform(&m,a,4,n,out);++count;
 }
 for(unsigned v=0;v<256;v++){
  model m=initial();memset(m.rx,(int)v,17);si_bus b=bus(&m);
  assert(si_read_register(&b,0x58,0x04,out,5));waveform(&m,0x58,4,5,out);
 }
 for(unsigned nack=1;nack<=3;nack++){
  model m=initial();m.nack_at=nack;si_bus b=bus(&m);memset(out,0xaa,17);
  assert(!si_read_register(&b,0x58,4,out,5)&&b.status==SI_NACK);
  assert(m.tx_n==nack&&m.stops==1&&m.scl&&m.sda);
  for(unsigned i=0;i<5;i++)assert(!out[i]);
 }
 for(unsigned f=0;f<5;f++){
  model m=initial();if(f==0)m.stuck_clock=true;if(f==1)m.stuck_data=true;if(f==2)m.freeze=true;if(f==3)m.stretch=101;if(f==4)m.step=30;
  si_bus b=bus(&m);memset(out,0xaa,17);assert(!si_read_register(&b,0x58,4,out,5));
  assert(m.scl&&m.sda&&b.polls<=20002);
  for(unsigned i=0;i<5;i++)assert(!out[i]);
  if(f==2)assert(b.status==SI_TIMER_STALLED);if(f==3)assert(b.status==SI_STRETCH_TIMEOUT);if(f==4)assert(b.status==SI_DEADLINE);
 }
 for(unsigned after=1;after<5;after++){
  model z=initial();z.fail_after_rx=after;si_bus q=bus(&z);memset(out,0xaa,17);
  assert(!si_read_register(&q,0x58,4,out,5)&&q.status==SI_STRETCH_TIMEOUT);
  for(unsigned i=0;i<5;i++)assert(!out[i]);
 }
 model m=initial();m.time=65500;m.fall_delay=1;m.stretch=10;si_bus b=bus(&m);
 assert(si_read_register(&b,0x58,4,out,5));waveform(&m,0x58,4,5,out);assert(m.time<65500);
 assert(!si_read_register(&b,128,4,out,5)&&b.status==SI_ARGUMENT);
 assert(!si_read_register(&b,0x58,4,out,0)&&b.status==SI_ARGUMENT);
 assert(!si_read_register(&b,0x58,4,out,18)&&b.status==SI_ARGUMENT);
 assert(!si_read_register(&b,0x58,4,NULL,5)&&b.status==SI_ARGUMENT);
 puts("PASS read wire model:2176 address/length cases,256 payload patterns,3 NACK positions,final NACK,stretch/fall/wrap/deadline/stall,failed buffer clear");
 (void)count;
}
