#include "stm32g4xx.h"
#include "drill_adc_capture.h"
#include "drill_spi_transport.h"
#include "drill_config_binding.h"
#include <stddef.h>
#define DA_SMPR (ADC_SMPR1_SMP1|ADC_SMPR1_SMP2|ADC_SMPR1_SMP3|ADC_SMPR1_SMP4)
#define DA_FAULT_FLAGS (ADC_ISR_OVR|ADC_ISR_JQOVF|ADC_ISR_AWD1|ADC_ISR_AWD2|ADC_ISR_AWD3|ADC_ISR_JEOC|ADC_ISR_JEOS)
volatile da_state drill_adc_state;
volatile da_error drill_adc_error;
static uint32_t da_last,da_phase,da_origin,da_sequence;
static unsigned da_channel;
static da_sample da_pending;
static bool da_config_ok(void){
 if(!drill_spi_available()||drill_config.state!=DC_READY)return false;
 if(!(GPIOF->ODR&2u)||!(GPIOF->IDR&2u)||((GPIOA->MODER>>12)&3u)!=0u||!(GPIOA->IDR&(1u<<6)))return false;
 if(((GPIOA->MODER>>24)&3u)!=1u||(GPIOA->OTYPER&(1u<<12))||!(GPIOA->ODR&(1u<<12))||!(GPIOA->IDR&(1u<<12)))return false;
 if(!(RCC->AHB2ENR&RCC_AHB2ENR_ADC12EN)||(RCC->AHB2RSTR&RCC_AHB2RSTR_ADC12RST))return false;
 if((GPIOA->MODER&255u)!=255u || (GPIOA->PUPDR&255u))return false;
 if(ADC12_COMMON->CCR!=ADC_CCR_CKMODE||ADC2->CR!=ADC_CR_DEEPPWD)return false;
 if(ADC1->CFGR||ADC1->CFGR2||ADC1->IER||ADC1->DIFSEL||ADC1->JSQR||ADC1->SMPR1!=DA_SMPR||ADC1->SMPR2)return false;
 if(ADC1->SQR1!=((da_channel+1u)<<ADC_SQR1_SQ1_Pos)||ADC1->SQR2||ADC1->SQR3||ADC1->SQR4)return false;
 if(ADC1->OFR1||ADC1->OFR2||ADC1->OFR3||ADC1->OFR4||ADC1->GCOMP)return false;
 uint32_t expected=ADC_CR_ADVREGEN;
 if(drill_adc_state==DA_CALIBRATION){if((ADC1->CR&~ADC_CR_ADCAL)!=expected)return false;}
 else if(drill_adc_state==DA_ENABLE||drill_adc_state==DA_READY||drill_adc_state==DA_DONE)expected|=ADC_CR_ADEN;
 else if(drill_adc_state==DA_CONVERT){if((ADC1->CR&~ADC_CR_ADSTART)!=(expected|ADC_CR_ADEN))return false;return true;}
 else if(ADC1->CR!=expected)return false;
 if(drill_adc_state==DA_CALIBRATION)return true;
 return ADC1->CR==expected;
}
static void da_fail(da_error e){
 drill_spi_abort();drill_config.state=DC_FAILED;drill_config.error=DC_INTERLOCK;
 /* Exclusive ownership of ADC12 was acquired only from clock-disabled reset.
  * Reset cancels conversion without an unbounded ADDIS/ADSTP wait. */
 if(RCC->AHB2ENR&RCC_AHB2ENR_ADC12EN){RCC->AHB2RSTR|=RCC_AHB2RSTR_ADC12RST;__DSB();RCC->AHB2RSTR&=~RCC_AHB2RSTR_ADC12RST;__DSB();RCC->AHB2ENR&=~RCC_AHB2ENR_ADC12EN;}
 da_pending=(da_sample){0};drill_adc_error=e;drill_adc_state=DA_FAILED;
}
bool drill_adc_bench_begin(uint32_t now,bool qualified){
 if(!qualified||drill_adc_state!=DA_IDLE||drill_config.state!=DC_READY||!drill_spi_available()||drill_spi_active())return false;
 if((RCC->AHB2ENR&RCC_AHB2ENR_ADC12EN)||(RCC->AHB2RSTR&RCC_AHB2RSTR_ADC12RST))return false;
 GPIOA->MODER|=255u;GPIOA->PUPDR&=~255u;__DSB();
 RCC->AHB2ENR|=RCC_AHB2ENR_ADC12EN;(void)RCC->AHB2ENR;__DSB();
 RCC->AHB2RSTR|=RCC_AHB2RSTR_ADC12RST;__DSB();RCC->AHB2RSTR&=~RCC_AHB2RSTR_ADC12RST;__DSB();
 ADC12_COMMON->CCR=ADC_CCR_CKMODE;
 ADC1->CFGR=0;ADC1->CFGR2=0;ADC1->IER=0;ADC1->DIFSEL=0;ADC1->JSQR=0;
 ADC1->SMPR1=DA_SMPR;ADC1->SMPR2=0;ADC1->SQR1=1u<<ADC_SQR1_SQ1_Pos;ADC1->SQR2=ADC1->SQR3=ADC1->SQR4=0;
 ADC1->OFR1=ADC1->OFR2=ADC1->OFR3=ADC1->OFR4=ADC1->GCOMP=0;
 ADC1->CR&=~ADC_CR_DEEPPWD;ADC1->CR|=ADC_CR_ADVREGEN;__DSB();
 da_last=da_phase=da_origin=now;da_channel=0;da_sequence=0;da_pending=(da_sample){0};drill_adc_error=DA_NONE;drill_adc_state=DA_REGULATOR;
 if(!da_config_ok()){da_fail(DA_CONFIG);return false;}return true;
}
void drill_adc_poll(uint32_t now){
 if(drill_adc_state==DA_IDLE||drill_adc_state==DA_FAILED)return;
 if(now-da_last>0x7fffffffu){da_fail(DA_TIME);return;}da_last=now;
 if(!da_config_ok()){da_fail(DA_CONFIG);return;}
 uint32_t flags=ADC1->ISR;if(flags&DA_FAULT_FLAGS){da_fail(DA_FLAGS);return;}
 if(drill_adc_state<DA_READY && now-da_origin>5000u){da_fail(DA_TIME);return;}
 if(drill_adc_state==DA_REGULATOR){if(now-da_phase>=40u){ADC1->CR|=ADC_CR_ADCAL;drill_adc_state=DA_CALIBRATION;da_phase=now;}return;}
 if(drill_adc_state==DA_CALIBRATION){if(!(ADC1->CR&ADC_CR_ADCAL)){drill_adc_state=DA_CAL_GAP;da_phase=now;}return;}
 if(drill_adc_state==DA_CAL_GAP){if(now-da_phase>=2u){ADC1->ISR=ADC_ISR_ADRDY;__DSB();ADC1->CR|=ADC_CR_ADEN;drill_adc_state=DA_ENABLE;da_phase=now;}return;}
 if(drill_adc_state==DA_ENABLE){if(flags&ADC_ISR_ADRDY){drill_adc_state=DA_READY;}return;}
 if(drill_adc_state!=DA_CONVERT)return;
 if(now-da_phase>1000u){da_fail(DA_TIME);return;}
 if(!(flags&ADC_ISR_EOC)){if(flags&ADC_ISR_EOS)da_fail(DA_FLAGS);return;}
 if(!(flags&ADC_ISR_EOS)||(ADC1->CR&ADC_CR_ADSTART)){da_fail(DA_FLAGS);return;}
 uint32_t raw=ADC1->DR;if(raw>4095u){da_fail(DA_RANGE);return;}
 da_pending.raw[da_channel]=(uint16_t)raw;da_pending.observed_us[da_channel]=now;
 ADC1->ISR=ADC_ISR_EOC|ADC_ISR_EOS|ADC_ISR_EOSMP;__DSB();
 if(++da_channel==4u){--da_channel;da_pending.completed_us=now;da_pending.sequence=++da_sequence;drill_adc_state=DA_DONE;return;}
 ADC1->SQR1=(da_channel+1u)<<ADC_SQR1_SQ1_Pos;ADC1->CR|=ADC_CR_ADSTART;da_phase=now;__DSB();
}
bool drill_adc_capture_start(uint32_t now){
 if(drill_adc_state!=DA_READY)return false;
 drill_adc_poll(now);if(drill_adc_state!=DA_READY)return false;
 if(drill_spi_active())return false;
 da_channel=0;da_pending=(da_sample){0};ADC1->SQR1=1u<<ADC_SQR1_SQ1_Pos;
 ADC1->ISR=ADC_ISR_EOC|ADC_ISR_EOS|ADC_ISR_EOSMP;__DSB();ADC1->CR|=ADC_CR_ADSTART;da_phase=now;drill_adc_state=DA_CONVERT;__DSB();return true;
}
bool drill_adc_take(da_sample *out,uint32_t now){
 if(!out||drill_adc_state!=DA_DONE)return false;
 drill_adc_poll(now);if(drill_adc_state!=DA_DONE)return false;
 if(now-da_pending.completed_us>1000u){da_fail(DA_TIME);return false;}
 *out=da_pending;da_pending=(da_sample){0};drill_adc_state=DA_READY;return true;
}
bool drill_adc_busy(void){return drill_adc_state==DA_REGULATOR||drill_adc_state==DA_CALIBRATION||drill_adc_state==DA_CAL_GAP||drill_adc_state==DA_ENABLE||drill_adc_state==DA_CONVERT;}
