#include "drill_clock.h"
#include "stm32g4xx.h"
#include "drill_spi_transport.h"
#include "drill_pwm_stage.h"
#include <stddef.h>
#define CR1_CONFIG (SPI_CR1_MSTR|SPI_CR1_SSM|SPI_CR1_SSI|SPI_CR1_CPHA|SPI_CR1_BR_2|SPI_CR1_BR_1|SPI_CR1_BR_0|SPI_CR1_SPE)
#define ERROR_BITS (SPI_SR_MODF|SPI_SR_OVR|SPI_SR_CRCERR|SPI_SR_FRE)
enum state {UNINITIALIZED,IDLE,SETUP,RECEIVE,DRAIN,HOLD,DONE,FAILED};
static enum state state;
static uint32_t begun,last,cs_high,held;
static uint16_t tx,rx;
static bool pin(GPIO_TypeDef *p,unsigned n,unsigned mode,bool high) {
 return ((p->MODER>>(2*n))&3u)==mode && !(p->OTYPER&(1u<<n)) &&
  ((p->ODR>>n)&1u)==(unsigned)high && ((p->IDR>>n)&1u)==(unsigned)high;
}
static bool blank(void) {
 return pin(GPIOA,5,1,false)&&pin(GPIOA,7,1,false)&&pin(GPIOA,8,1,false)&&pin(GPIOA,9,1,false)&&pin(GPIOA,10,1,false)&&pin(GPIOB,0,1,false)&&pin(GPIOB,1,1,false)&&
 ((GPIOF->MODER>>2)&3u)==1u && !(GPIOF->OTYPER&2u) &&
 (RCC->APB2ENR&RCC_APB2ENR_TIM1EN) && (!(TIM1->CR1&TIM_CR1_CEN)||drill_pwm_sampling_available()) && TIM1->CCER==0u && !(TIM1->BDTR&TIM_BDTR_MOE);
}
static bool spi_clock_ok(void) {return drill_clock_available() && (RCC->AHB2ENR&(RCC_AHB2ENR_GPIOAEN|RCC_AHB2ENR_GPIOBEN|RCC_AHB2ENR_GPIOFEN))==(RCC_AHB2ENR_GPIOAEN|RCC_AHB2ENR_GPIOBEN|RCC_AHB2ENR_GPIOFEN);}
static void cs(bool high) {GPIOB->BSRR=high?(1u<<12):(1u<<28);__DSB();}
static void fault(void) {
 drill_pwm_sampling_abort();
 RCC->AHB2ENR|=RCC_AHB2ENR_GPIOAEN|RCC_AHB2ENR_GPIOBEN|RCC_AHB2ENR_GPIOFEN;(void)RCC->AHB2ENR;
 GPIOA->BSRR=((1u<<5)|(1u<<7)|(7u<<8))<<16;GPIOB->BSRR=3u<<16;GPIOF->BSRR=2u<<16;__DSB();
 if(RCC->APB2ENR&RCC_APB2ENR_TIM1EN){TIM1->BDTR&=~TIM_BDTR_MOE;TIM1->CCER=0;TIM1->CR1&=~TIM_CR1_CEN;}
 if(RCC->APB1ENR1&RCC_APB1ENR1_SPI2EN)SPI2->CR1&=~SPI_CR1_SPE;
 cs(true);state=FAILED;rx=0;
}
bool drill_spi_available(void) {
 if(state==UNINITIALIZED||state==FAILED||!spi_clock_ok()||!blank())return false;
 if(!(RCC->APB1ENR1&RCC_APB1ENR1_SPI2EN)||(RCC->APB1RSTR1&RCC_APB1RSTR1_SPI2RST))return false;
 if(!pin(GPIOB,12,1,state==IDLE||state==DONE))return false;
 if(GPIOB->PUPDR&(255u<<24))return false;
 for(unsigned p=13;p<=15;++p)if(((GPIOB->MODER>>(2*p))&3u)!=2u || ((GPIOB->AFR[1]>>(4*(p-8)))&15u)!=5u || (GPIOB->OTYPER&(1u<<p)))return false;
 return SPI2->CR1==CR1_CONFIG && SPI2->CR2==SPI_CR2_DS && !(SPI2->SR&ERROR_BITS);
}
bool drill_spi_init(uint32_t now) {
 if(state!=UNINITIALIZED)return drill_spi_available();
 if(!spi_clock_ok()||!blank()||(RCC->APB1ENR1&RCC_APB1ENR1_SPI2EN))return false;
 cs(true);GPIOB->OTYPER&=~(15u<<12);GPIOB->PUPDR&=~(255u<<24);
 GPIOB->OSPEEDR=(GPIOB->OSPEEDR&~(255u<<24))|(0xaau<<24);
 GPIOB->MODER=(GPIOB->MODER&~(255u<<24))|(0xa9u<<24);
 GPIOB->AFR[1]=(GPIOB->AFR[1]&~(0xfffu<<20))|(0x555u<<20);__DSB();
 RCC->APB1ENR1|=RCC_APB1ENR1_SPI2EN;(void)RCC->APB1ENR1;__DSB();
 if(RCC->APB1RSTR1&RCC_APB1RSTR1_SPI2RST){fault();return false;}
 RCC->APB1RSTR1|=RCC_APB1RSTR1_SPI2RST;__DSB();RCC->APB1RSTR1&=~RCC_APB1RSTR1_SPI2RST;__DSB();
 SPI2->CR1=0;SPI2->CR2=SPI_CR2_DS;SPI2->CR1=CR1_CONFIG;__DSB();
 state=IDLE;last=cs_high=now;
 if(!drill_spi_available()){fault();return false;}return true;
}
bool drill_spi_start(uint16_t frame,uint32_t now) {
 if(state!=IDLE&&state!=DONE)return false;
 if(!drill_spi_available()){fault();return false;}
 if(now-last>0x7fffffffu||now-cs_high<2u||now-cs_high>0x7fffffffu)return false;
 unsigned parity=0;for(unsigned bit=0;bit<16;++bit)parity^=(frame>>bit)&1u;
 if(parity)return false;
 /* Driver sleep is controlled by future configuration runtime, not transport.
  * Do not clock the sleeping device or grant startup readiness. */
 if(!pin(GPIOF,1,1,true))return false;
 if(SPI2->SR&(SPI_SR_BSY|SPI_SR_RXNE)){fault();return false;}
 tx=frame;rx=0;begun=last=now;state=SETUP;cs(false);return true;
}
void drill_spi_poll(uint32_t now) {
 if(state==UNINITIALIZED||state==IDLE||state==FAILED)return;
 if(!drill_spi_available()||!pin(GPIOF,1,1,true)||now-last>0x7fffffffu||now-last>10u||now-begun>100u){fault();return;}
 last=now;
 if(state==SETUP){if(now-begun>=2u&&(SPI2->SR&SPI_SR_TXE)){*(volatile uint16_t*)&SPI2->DR=tx;state=RECEIVE;}return;}
 if(state==RECEIVE){if(SPI2->SR&SPI_SR_RXNE){rx=*(volatile uint16_t*)&SPI2->DR;state=DRAIN;}return;}
 if(state==DRAIN){if(!(SPI2->SR&SPI_SR_BSY)){held=now;state=HOLD;}return;}
 if(state==HOLD&&now-held>=2u){cs(true);cs_high=now;state=DONE;}
}
bool drill_spi_get(uint16_t *out,uint32_t now) {
 if(!out||state!=DONE)return false;
 if(!drill_spi_available()||now-last>10u||now-begun>100u||now-last>0x7fffffffu){fault();return false;}
 *out=rx;rx=0;state=IDLE;return true;
}
bool drill_spi_failed(void){return state==FAILED;}

bool drill_spi_active(void){return state==SETUP||state==RECEIVE||state==DRAIN||state==HOLD||state==DONE;}
void drill_spi_abort(void){if(state!=UNINITIALIZED)fault();}
