#include "registers.h"
#include <stdbool.h>
#include <stddef.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#undef ADC1
ADC_TypeDef adc;
#define ADC1 (&adc)
#define ADC_POLL_LIMIT 100000u
static bool g_adc_ready;
static unsigned ready_delay,convert_delay,selected,starts,channel_writes;
static bool converting,overrun;
static void host_w1c(uint32_t mask) {adc.ISR &= ~mask;}
static void host_channel(uint32_t mask) {
 adc.CHSELR=mask;selected=0;while((mask>>selected)!=1u)++selected;
 channel_writes++;adc.ISR &= ~ADC_ISR_CCRDY;
 if(!ready_delay)adc.ISR |= ADC_ISR_CCRDY;
}
static void complete(void) {
 adc.DR=100u+selected;adc.ISR |= ADC_ISR_EOC|ADC_ISR_EOS;
 if(overrun)adc.ISR |= ADC_ISR_OVR;else adc.CR &= ~ADC_CR_ADSTART;
 converting=false;
}
static void host_start(void) {
 starts++;
 /* Hardware ignores ADSTART before channel configuration acknowledgement. */
 if(!(adc.ISR&ADC_ISR_CCRDY)) return;
 adc.CR |= ADC_CR_ADSTART;converting=true;if(!convert_delay)complete();
}
static void host_cycle(void) {
 if(ready_delay && --ready_delay==0)adc.ISR |= ADC_ISR_CCRDY;
 if(converting && convert_delay && --convert_delay==0)complete();
}
static bool read_adc(uint32_t channel, uint16_t *raw)
{
    uint32_t timeout;

    if (!g_adc_ready || raw == NULL || channel > 18u ||
        (ADC1->CR & ADC_CR_ADSTART) != 0u) {
        return false;
    }

    host_w1c(ADC_ISR_EOC | ADC_ISR_EOS | ADC_ISR_OVR | ADC_ISR_CCRDY);
    host_channel(1u << channel);
    /* RM0454: ADSTART is ignored until the new channel configuration is
       applied. Never convert with an unacknowledged channel selection. */
    timeout = ADC_POLL_LIMIT;
    while (((ADC1->ISR & ADC_ISR_CCRDY) == 0u) && timeout != 0u) {
        --timeout;host_cycle();
    }
    if (timeout == 0u) {
        g_adc_ready = false; /* Do not reuse an uncertain ADC state. */
        return false;
    }
    host_start();
    timeout = ADC_POLL_LIMIT;
    while (((ADC1->ISR & ADC_ISR_EOC) == 0u) && timeout != 0u) {
        --timeout;host_cycle();
    }
    if (timeout == 0u || (ADC1->ISR & ADC_ISR_OVR) != 0u) {
        g_adc_ready = false;
        if ((ADC1->CR & ADC_CR_ADSTART) != 0u) {
            ADC1->CR |= ADC_CR_ADSTP;
        }
        return false;
    }

    *raw = (uint16_t)(ADC1->DR & 0x0FFFu);
    return true;
}


static void seed(unsigned rd,unsigned cd,bool ov) {
 memset(&adc,0,sizeof adc);g_adc_ready=true;ready_delay=rd;convert_delay=cd;
 selected=starts=channel_writes=0;converting=false;overrun=ov;
 adc.ISR=ADC_ISR_EOC|ADC_ISR_EOS|ADC_ISR_OVR|ADC_ISR_CCRDY;adc.DR=4095;adc.CR=ADC_CR_ADEN;
}
int main(void) {
 unsigned cases=0;const unsigned channels[]={0,1,3,4,5,6,13,18};
 const unsigned rd[]={0,1,7},cd[]={0,1,9};
 for(unsigned ch=0;ch<8;ch++) for(unsigned i=0;i<3;i++) for(unsigned j=0;j<3;j++) {
  seed(rd[i],cd[j],false);uint16_t raw=555;
  assert(read_adc(channels[ch],&raw));assert(raw==100u+channels[ch]);
  assert(starts==1 && channel_writes==1 && g_adc_ready);cases++;
 }
 seed(ADC_POLL_LIMIT+1,0,false);uint16_t raw=555;assert(!read_adc(1,&raw));assert(raw==555 && starts==0 && !g_adc_ready);
 assert(!read_adc(5,&raw));assert(starts==0 && raw==555);
 seed(0,ADC_POLL_LIMIT+1,false);assert(!read_adc(1,&raw));assert(raw==555 && !g_adc_ready && (adc.CR&ADC_CR_ADSTP));
 seed(0,3,true);assert(!read_adc(1,&raw));assert(raw==555 && !g_adc_ready && (adc.CR&ADC_CR_ADSTP));
 seed(0,0,false);assert(!read_adc(19,&raw));assert(!read_adc(1,NULL));assert(channel_writes==0 && raw==555);
 adc.CR|=ADC_CR_ADSTART;assert(!read_adc(1,&raw));assert(channel_writes==0);
 printf("PASS: %u actual read_adc success cases; delayed CCRDY/EOC, stale flags/raw data, channel identity, timeout/overrun latch, invalid/null/busy rejection. MMIO event model only.\n",cases);
}
