#include "drill_config_async.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
typedef struct {bool off,fh,awake,failed,pending,stall;unsigned wakes,stops,transfers;int corrupt_step,corrupt_bit;uint16_t word;} fake;
static bool ok(void *v){return ((fake*)v)->off;}
static bool fh(void *v){return ((fake*)v)->fh;}
static bool wake(void *v,bool a){fake *f=v;f->awake=a;++f->wakes;return true;}
static bool submit(void *v,uint16_t w,uint32_t n){(void)n;fake *f=v;assert(f->awake&&!f->pending);unsigned parity=0;for(unsigned i=0;i<16;++i)parity^=(w>>i)&1;assert(!parity);static const uint16_t sequence[22]={0x8100,0x8200,0x8400,0x0603,0x8700,0x0941,0x8800,0x0b4f,0x8b00,0x0d10,0x8d00,0x0e01,0x8e00,0x1001,0x9000,0x1800,0x9900,0x0606,0x8700,0x8100,0x8200,0x8400};assert(f->transfers<22&&w==sequence[f->transfers]);f->word=w;f->pending=true;return true;}
static bool receive(void *v,uint16_t *w,uint32_t n){(void)n;fake *f=v;if(!f->pending||f->stall)return false;unsigned step=f->transfers++;unsigned addr=(f->word>>9)&63;bool read=(f->word&0x8000)!=0;uint8_t data=0;
 if(step<3||step>=19)data=addr==0?8:0;
 else if(read){static const unsigned val[8]={3,0x40,0x4f,0x10,1,1,0,6};data=val[(step-3)/2];}
 /* writes intentionally reply OLD data: current command is never echoed */
 *w=(uint16_t)(0x0800|data);if((int)step==f->corrupt_step)*w^=(uint16_t)(1u<<f->corrupt_bit);f->pending=false;return true;}
static bool bad(void *v){return ((fake*)v)->failed;}
static void stop(void *v){fake *f=v;f->awake=false;f->off=true;f->pending=false;++f->stops;}
static dc_hal hal(fake *f){return (dc_hal){f,ok,fh,wake,submit,receive,bad,stop};}
static void drive(dc_runtime *r,dc_hal *h,uint32_t start){for(unsigned n=0;n<25000&&r->state!=DC_READY&&r->state!=DC_FAILED;++n)dc_poll(r,h,start+n);}
int main(void){unsigned cases=0;
 for(unsigned wrap=0;wrap<2;++wrap){fake f={.off=true,.fh=true,.corrupt_step=-1};dc_hal h=hal(&f);dc_runtime r={0};uint32_t n=wrap?UINT_MAX-2000u:0;
 assert(!dc_begin(&r,&h,n,false)&&r.state==DC_IDLE&&f.wakes==0);assert(dc_begin(&r,&h,n,true));assert(!dc_begin(&r,&h,n,true));
 dc_poll(&r,&h,n+999);assert(!f.awake&&f.transfers==0);dc_poll(&r,&h,n+1000);assert(f.awake&&r.state==DC_WAKE);dc_poll(&r,&h,n+2999);assert(f.transfers==0);drive(&r,&h,n+2999);assert(r.state==DC_READY&&f.transfers==22&&f.awake&&f.stops==0&&r.npor_required&&dc_feedback_ready(&r));f.fh=false;dc_poll(&r,&h,r.last+1);assert(r.state==DC_FAILED&&!f.awake&&f.stops==1);++cases;}
 /* Every status fault bit at every one of 22 responses must stop. */
 for(unsigned s=0;s<22;++s)for(unsigned bit=8;bit<16;++bit){if(bit==11)continue;fake f={.off=true,.fh=true,.corrupt_step=(int)s,.corrupt_bit=(int)bit};dc_hal h=hal(&f);dc_runtime r={0};assert(dc_begin(&r,&h,0,true));drive(&r,&h,0);assert(r.state==DC_FAILED&&r.error==DC_STATUS&&!f.awake&&f.stops==1);++cases;}
 /* Every bit of all eight readbacks is checked. */
 for(unsigned i=0;i<8;++i)for(unsigned bit=0;bit<8;++bit){fake f={.off=true,.fh=true,.corrupt_step=(int)(4+2*i),.corrupt_bit=(int)bit};dc_hal h=hal(&f);dc_runtime r={0};assert(dc_begin(&r,&h,0,true));drive(&r,&h,0);assert(r.state==DC_FAILED&&r.error==DC_READBACK&&!f.awake);++cases;}
 for(unsigned s=7;s<22;++s){fake f={.off=true,.fh=true,.corrupt_step=(int)s,.corrupt_bit=11};dc_hal h=hal(&f);dc_runtime r={0};assert(dc_begin(&r,&h,0,true));drive(&r,&h,0);assert(r.state==DC_FAILED&&r.error==DC_STATUS&&!f.awake);++cases;}
 /* Monotonic reversal, stalled transaction, total deadline and off loss. */
 for(unsigned mode=0;mode<5;++mode){fake f={.off=true,.fh=true,.corrupt_step=-1};dc_hal h=hal(&f);dc_runtime r={0};assert(dc_begin(&r,&h,100,true));if(mode==0)dc_poll(&r,&h,99);if(mode==1){f.stall=true;drive(&r,&h,100);}if(mode==2)dc_poll(&r,&h,20101);if(mode==3){f.off=false;dc_poll(&r,&h,101);}if(mode==4){f.failed=true;dc_poll(&r,&h,101);}assert(r.state==DC_FAILED&&!f.awake&&f.stops==1);assert(!dc_begin(&r,&h,30000,true));++cases;}
 /* Low nFAULT is permitted only while waiting for wake, then rejected. */
 {fake f={.off=true,.fh=false,.corrupt_step=-1};dc_hal h=hal(&f);dc_runtime r={0};assert(dc_begin(&r,&h,0,true));dc_poll(&r,&h,1000);dc_poll(&r,&h,2999);assert(r.state==DC_WAKE);dc_poll(&r,&h,3000);assert(r.state==DC_FAILED&&f.transfers==0&&!f.awake);++cases;}
 printf("%u ASYNC CONFIG SCENARIOS PASS\n",cases);
}
