#include "pca9956b_fault_monitor.h"
#include <stddef.h>
#define MASK24 0xffffffu
#define MAX_GAP 5u
#define MAX_AGE 30u
#define MODE2 0x01u
#define EFLAG0 0x41u
static void fail(pca_fault_monitor *m) {m->failed=true;m->valid=false;}
bool pca_fault_init(pca_fault_monitor *m,void *ctx,pca_fault_read_fn read,
                    uint8_t addr,uint32_t requested,uint32_t eligible,uint32_t now) {
    if(m==NULL) return false;
    *m=(pca_fault_monitor){0};
    if(read==NULL || addr<8u || addr>0x77u || (requested&~MASK24)!=0u ||
       (eligible&~MASK24)!=0u || (eligible&~requested)!=0u) {fail(m);return false;}
    m->ctx=ctx;m->read=read;m->address=addr;m->requested_mask=requested;
    m->eligible_mask=eligible;m->last_poll=now;m->bound=true;return true;
}
void pca_fault_poll(pca_fault_monitor *m,uint32_t now) {
    if(m==NULL || !m->bound || m->failed) return;
    if(now-m->last_poll>MAX_GAP){fail(m);return;}
    m->last_poll=now;
    uint8_t value=0u;
    const uint8_t reg=(m->step==0u || m->step==7u)?MODE2:(uint8_t)(EFLAG0+m->step-1u);
    if(!m->read(m->ctx,m->address,reg,&value)){fail(m);return;}
    if(m->step==0u){m->valid=false;m->mode2_before=value;m->first_read=now;}
    else if(m->step<7u)m->eflag[m->step-1u]=value;
    else {
        /* These registers are not an atomic hardware snapshot. Reject changed
         * status/control between bookend reads; stable summaries are still not
         * proof that every channel stayed constant during a read sequence. */
        if(value!=m->mode2_before || now-m->first_read>MAX_AGE){fail(m);return;}
        pca_fault_snapshot s={0};
        s.mode2_before=m->mode2_before;s.mode2_after=value;
        s.overtemperature=(value&0x80u)!=0u;s.global_error=(value&0x40u)!=0u;
        for(unsigned ch=0u;ch<24u;ch++) {
            unsigned code=(m->eflag[ch/4u]>>(2u*(ch%4u)))&3u;
            if(code==1u)s.short_mask|=1u<<ch;
            else if(code==2u)s.open_mask|=1u<<ch;
            else if(code==3u)s.invalid_code_mask|=1u<<ch;
        }
        const bool any=(s.short_mask|s.open_mask|s.invalid_code_mask)!=0u;
        s.summary_mismatch=s.global_error!=any;
        s.requested_mask=m->requested_mask;s.eligible_mask=m->eligible_mask;
        s.coverage_complete=m->requested_mask!=0u &&
                            (m->eligible_mask&m->requested_mask)==m->requested_mask;
        s.first_read_ms=m->first_read;s.completed_ms=now;s.sequence=++m->sequence;
        m->snapshot=s;m->valid=true;m->step=0u;return;
    }
    ++m->step;
}
bool pca_fault_get(const pca_fault_monitor *m,uint32_t now,pca_fault_snapshot *out) {
    if(m==NULL || out==NULL || !m->bound || m->failed || !m->valid ||
       now-m->snapshot.first_read_ms>MAX_AGE || now-m->last_poll>MAX_GAP) return false;
    *out=m->snapshot;return true;
}
