#include "pca9956b_async_bank.h"
#include <assert.h>
#include <string.h>
#include <stdio.h>
typedef struct {unsigned calls,cancelled,hold;int fail_begin,fail_read,change;bool busy;uint8_t v,raw[3][8];} bus;
static bool begin(void *ctx,uint8_t a,uint8_t reg,uint32_t now){
 (void)now;bus *s=ctx;unsigned k=s->calls++,chip=k%3u,step=(k/3u)%8u;const uint8_t addr[3]={0x15,0x19,0x65};
 assert(a==addr[chip] && reg==((step==0||step==7)?1:0x40+step));assert(!s->busy);
 if((int)k==s->fail_begin)return false;
 s->busy=true;s->v=s->raw[chip][step];if(step==7&&(int)chip==s->change)s->v^=0x40;return true;
}
static void poll(void *ctx,uint32_t now){(void)ctx;(void)now;}
static bool take(void *ctx,uint32_t now,uint8_t *out){(void)now;bus *s=ctx;if(s->hold){s->hold--;return false;}*out=s->v;s->busy=false;return true;}
static bool failed(void *ctx){bus *s=ctx;return (int)s->calls-1==s->fail_read;}
static void cancel(void *ctx){bus *s=ctx;s->busy=false;s->cancelled++;}
static pca_async_ops ops={begin,poll,take,failed,cancel};
static void start(pca_async_bank *b,bus *s,uint32_t base){
 *s=(bus){.fail_begin=-1,.fail_read=-1,.change=-1};uint32_t e[3]={0,0,0};assert(pca_async_init(b,s,ops,e,base));
}
static void get(pca_async_bank *b,uint32_t now,bool wanted){pca_async_snapshot out,old;memset(&out,0xa5,sizeof out);old=out;assert(pca_async_get(b,now,&out)==wanted);if(!wanted)assert(memcmp(&out,&old,sizeof out)==0);}
static void collect(pca_async_bank *b,uint32_t base,unsigned span){
 for(unsigned k=0;k<48;k++){uint32_t now=base+k*span/47u;pca_async_poll(b,now);if(k<47)get(b,now,false);}
}
int main(void){pca_async_bank b;bus s;pca_async_snapshot out;unsigned cases=0;uint32_t bases[]={0,50,0xfffffff8u};
 for(unsigned q=0;q<3;q++)for(unsigned span=0;span<=20;span++){
 start(&b,&s,bases[q]);collect(&b,bases[q],span);assert(pca_async_get(&b,bases[q]+span,&out));assert(out.first_read_ms==bases[q] && out.completed_ms==bases[q]+span);
 assert(out.chip[0].completed_ms==bases[q]+43u*span/47u);assert(out.chip[1].completed_ms==bases[q]+45u*span/47u);
 for(unsigned j=0;j<3;j++)assert(!out.chip[j].coverage_complete);
 pca_async_poll(&b,bases[q]+span);get(&b,bases[q]+span,false);cases++;
 }
 for(unsigned chip=0;chip<3;chip++)for(unsigned ch=0;ch<24;ch++)for(unsigned code=1;code<4;code++){
 start(&b,&s,0);s.raw[chip][0]=s.raw[chip][7]=0x40;s.raw[chip][1+ch/4]=code<<(2*(ch%4));collect(&b,0,11);
 assert(pca_async_get(&b,11,&out));pca_fault_snapshot *c=&out.chip[chip];assert((code==1?c->short_mask:code==2?c->open_mask:c->invalid_code_mask)==(1u<<ch));cases++;
 }
 for(int k=0;k<24;k++){
 start(&b,&s,0);s.fail_read=k;collect(&b,0,11);assert(b.failed && s.cancelled==1 && !s.busy);get(&b,11,false);cases++;
 start(&b,&s,0);s.fail_begin=k;collect(&b,0,11);assert(b.failed && s.cancelled==0);get(&b,11,false);cases++;
 }
 for(int j=0;j<3;j++){start(&b,&s,0);s.change=j;collect(&b,0,11);assert(b.failed);get(&b,11,false);cases++;}
 start(&b,&s,0);collect(&b,0,47);assert(b.failed);get(&b,47,false); /*1ms each poll not acceptable*/
 start(&b,&s,0);pca_async_poll(&b,0);pca_async_poll(&b,6);assert(b.failed&&s.cancelled==1);
 start(&b,&s,100);pca_async_poll(&b,100);pca_async_poll(&b,99);assert(b.failed&&s.cancelled==1);
 start(&b,&s,0);collect(&b,0,11);get(&b,21,false);
 start(&b,&s,0);s.hold=99;pca_async_poll(&b,0);
 for(uint32_t now=1;now<=5;now++){pca_async_poll(&b,now);get(&b,now,false);assert(!b.failed && s.busy);}
 pca_async_poll(&b,6);assert(b.failed && s.cancelled==1); /* transaction timeout with timely polls */
 uint32_t invalid[3]={0,0,0x2000};assert(!pca_async_init(&b,&s,ops,invalid,0));
 printf("PASS %u async bank paths plus gap/backward/stale/eligibility/cancel/output-preservation tests. Host model only.\n",cases);
}
