#include "pca9956b_bank_monitor.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
typedef struct {uint32_t now;unsigned calls;int fail_at,slow_at,change_bank;
    unsigned duration;uint8_t mode,flags[3][6];} bus;
static uint32_t clock_ms(void *v){return ((bus *)v)->now;}
static bool read_reg(void *v,uint8_t addr,uint8_t reg,uint8_t *out){
    bus *s=v;unsigned k=s->calls++,i=k%3u,step=(k/3u)%8u;
    const uint8_t a[3]={0x15,0x19,0x65};
    assert(addr==a[i]);assert(reg==((step==0 || step==7)?1:0x40+step));
    if((int)k==s->slow_at)s->now+=s->duration;
    if((int)k==s->fail_at)return false;
    *out=(reg==1)?s->mode:s->flags[i][reg-0x41];
    if(step==7 && (int)i==s->change_bank)*out^=0x40;
    return true;
}
static void start(pca_bank_monitor *m,bus *s,uint32_t base,bool eligible){
    *s=(bus){.now=base,.fail_at=-1,.slow_at=-1,.change_bank=-1};
    uint32_t e[3]={0,0,0};if(eligible){e[0]=e[1]=0xffffff;e[2]=0x1fff;}
    assert(pca_bank_init(m,s,read_reg,clock_ms,e));
}
static bool get(pca_bank_monitor *m,bool wanted){
    pca_bank_snapshot out;memset(&out,0xa5,sizeof(out));pca_bank_snapshot old=out;
    bool ok=pca_bank_get(m,&out);assert(ok==wanted);
    if(!ok)assert(memcmp(&old,&out,sizeof(out))==0);return ok;
}
static void collect(pca_bank_monitor *m,bus *s,uint32_t base,unsigned span){
    for(unsigned k=0;k<24;k++){
        s->now=base+(k*span)/23u;pca_bank_poll(m);
        if(k<23)get(m,false);
    }
}
int main(void){
    pca_bank_monitor m;bus s;pca_bank_snapshot out;
    const uint32_t bases[]={0,100,0xfffffff8u};
    for(unsigned b=0;b<3;b++)for(unsigned span=0;span<=20;span++){
        start(&m,&s,bases[b],false);collect(&m,&s,bases[b],span);get(&m,true);
        assert(pca_bank_get(&m,&out));assert(out.first_read_ms==bases[b]);
        assert(out.completed_ms==bases[b]+span);
        for(unsigned i=0;i<3;i++)assert(!out.chip[i].coverage_complete);
        pca_bank_poll(&m);get(&m,false); /* old complete batch invalid on restart */
    }
    start(&m,&s,0,false);collect(&m,&s,0,23);get(&m,false);assert(m.failed);
    for(int f=0;f<24;f++){
        start(&m,&s,0,true);s.fail_at=f;collect(&m,&s,0,11);
        get(&m,false);assert(m.failed);assert(s.calls==(unsigned)f+1);
    }
    for(int f=0;f<24;f++){
        start(&m,&s,0,false);s.slow_at=f;s.duration=6;
        collect(&m,&s,0,11);get(&m,false);assert(m.failed);
    }
    for(int i=0;i<3;i++){
        start(&m,&s,0,true);s.change_bank=i;collect(&m,&s,0,11);
        get(&m,false);assert(m.failed);
    }
    for(unsigned bank=0;bank<3;bank++)for(unsigned ch=0;ch<24;ch++)for(unsigned code=1;code<=3;code++){
        start(&m,&s,0,true);s.mode=0x40;s.flags[bank][ch/4]=code<<(2*(ch%4));
        collect(&m,&s,0,11);assert(pca_bank_get(&m,&out));
        const pca_fault_snapshot *c=&out.chip[bank];
        assert((code==1?c->short_mask:code==2?c->open_mask:c->invalid_code_mask)==(1u<<ch));
        /* Includes unused U3 channels; unexpected fault cannot be hidden. */
        assert(!c->summary_mismatch);
    }
    start(&m,&s,0,false);collect(&m,&s,0,11);s.now=21;get(&m,false);
    start(&m,&s,100,false);s.now=99;pca_bank_poll(&m);assert(m.failed);get(&m,false);
    start(&m,&s,0,false);s.now=6;pca_bank_poll(&m);assert(m.failed);assert(s.calls==0);
    start(&m,&s,0,false);s.slow_at=23;s.duration=1;collect(&m,&s,0,20);
    assert(m.failed);get(&m,false); /* final callback crosses whole-bank 20ms */
    start(&m,&s,0,false);s.slow_at=23;s.duration=1;collect(&m,&s,0,19);
    assert(pca_bank_get(&m,&out));assert(out.completed_ms==20);
    uint32_t bad[3]={0,0,0x2000};assert(!pca_bank_init(&m,&s,read_reg,clock_ms,bad));
    get(&m,false);
    puts("PASS bank cadence spans0..20ms/wrap, 24 read failures, 24 slow returns, 3 changed summaries, 216 channel faults, stale/backward/gap/final-completion/eligibility gates");
    return 0;
}
