#include "pca9956b_bank_monitor.h"
#include <stddef.h>
static const uint8_t addresses[3]={0x15u,0x19u,0x65u};
static const uint32_t populated[3]={0xffffffu,0xffffffu,0x1fffu};
static void fail(pca_bank_monitor *b) {
    b->failed=true;b->complete=false;
    for(unsigned i=0;i<3;i++){b->chip[i].valid=false;b->chip[i].failed=true;}
}
static bool timed_read(void *ctx,uint8_t addr,uint8_t reg,uint8_t *out) {
    pca_bank_monitor *b=ctx;
    bool ok=b->read(b->ctx,addr,reg,out);
    uint32_t end=b->clock(b->ctx);
    /* Includes the final register-read duration in whole-bank freshness. */
    if(!ok || end-b->read_started>5u || end-b->first_read>30u) {
        fail(b);return false;
    }
    return true;
}
bool pca_bank_init(pca_bank_monitor *b,void *ctx,pca_fault_read_fn read,
                   pca_bank_clock_fn clock,const uint32_t eligible[3]) {
    if(b==NULL)return false;
    *b=(pca_bank_monitor){0};
    if(read==NULL || clock==NULL || eligible==NULL){fail(b);return false;}
    b->ctx=ctx;b->read=read;b->clock=clock;
    uint32_t now=clock(ctx);b->last_poll=now;
    for(unsigned i=0;i<3;i++) {
        if(!pca_fault_init(&b->chip[i],b,timed_read,addresses[i],
                           populated[i],eligible[i],now)){fail(b);return false;}
    }
    b->bound=true;return true;
}
void pca_bank_poll(pca_bank_monitor *b) {
    if(b==NULL || !b->bound || b->failed)return;
    uint32_t now=b->clock(b->ctx);
    if(now-b->last_poll>5u){fail(b);return;}
    b->last_poll=now;
    if(b->complete){
        b->complete=false;b->reads=0;
        for(unsigned i=0;i<3;i++)b->chip[i].valid=false;
    }
    if(b->reads==0)b->first_read=now;
    if(now-b->first_read>30u){fail(b);return;}
    unsigned i=b->reads%3u;b->read_started=now;
    pca_fault_poll(&b->chip[i],now);
    if(b->failed || b->chip[i].failed){fail(b);return;}
    ++b->reads;
    if(b->reads==24u){
        pca_fault_snapshot s;
        uint32_t end=b->clock(b->ctx);
        if(end-b->first_read>30u){fail(b);return;}
        for(unsigned j=0;j<3;j++)if(!pca_fault_get(&b->chip[j],end,&s)){
            fail(b);return;
        }
        b->completed=end;b->complete=true;
    }
}
bool pca_bank_get(const pca_bank_monitor *b,pca_bank_snapshot *out) {
    if(b==NULL || out==NULL || !b->bound || b->failed || !b->complete)return false;
    uint32_t now=b->clock(b->ctx);
    if(now-b->first_read>30u || now-b->last_poll>5u)return false;
    pca_bank_snapshot s={0};s.first_read_ms=b->first_read;
    for(unsigned i=0;i<3;i++) {
        if(!pca_fault_get(&b->chip[i],now,&s.chip[i]))return false;
    }
    s.completed_ms=b->completed;
    *out=s;return true;
}
