#include "lamp_i2c_write.h"
#include "lamp_i2c_clock.h"
#include "lamp_i2c_owner.h"
#include "stm32g0xx.h"
#include <string.h>
#define ERRORS (I2C_ISR_NACKF|I2C_ISR_BERR|I2C_ISR_ARLO|I2C_ISR_OVR|I2C_ISR_TIMEOUT)
#define CLEAR (I2C_ICR_NACKCF|I2C_ICR_BERRCF|I2C_ICR_ARLOCF|I2C_ICR_OVRCF|I2C_ICR_TIMOUTCF|I2C_ICR_STOPCF)
static void fail(lamp_i2c_write *w,uint32_t flags){
 w->state=LIW_FAILED;w->valid=false;w->error_flags=flags;
 if(!lamp_i2c_owner_is(w))return; /* Never touch another transaction. */
 if(!(flags&I2C_ISR_ARLO) && (I2C1->ISR&I2C_ISR_BUSY))I2C1->CR2|=I2C_CR2_STOP;
 I2C1->CR1&=~I2C_CR1_PE;I2C1->ICR=CLEAR;lamp_i2c_owner_release(w);
}
void lamp_i2c_write_cancel(lamp_i2c_write *w){
 if(w==NULL)return;
 if(w->state==LIW_TX || w->state==LIW_STOP)fail(w,I2C1->ISR);
 else w->valid=false;
}
void lamp_i2c_write_reset(lamp_i2c_write *w){
 if(w==NULL)return;
 if(lamp_i2c_owner_is(w))lamp_i2c_write_cancel(w);
 *w=(lamp_i2c_write){0};
}
bool lamp_i2c_write_start(lamp_i2c_write *w,uint8_t addr,const uint8_t *bytes,size_t n,uint32_t now){
 if(w==NULL || bytes==NULL || (w->state!=LIW_IDLE && w->state!=LIW_DONE))return false;
 bool single=n==2u && (bytes[0]<=0x39u || bytes[0]==0x3fu || bytes[0]==0x40u);
 bool burst=n==25u && (bytes[0]==0x8au || bytes[0]==0xa2u);
 if((addr!=0x15u && addr!=0x19u && addr!=0x65u) || (!single && !burst) || !lamp_i2c_clock_available())return false;
 uint32_t flags=I2C1->ISR;
 if(flags&(I2C_ISR_BUSY|I2C_ISR_RXNE|I2C_ISR_TC|I2C_ISR_TCR|I2C_ISR_STOPF|ERRORS))return false;
 if(!lamp_i2c_owner_acquire(w))return false;
 memcpy(w->bytes,bytes,n);w->length=(uint8_t)n;w->sent=0;w->started=w->last_poll=now;
 w->valid=false;w->error_flags=0;w->state=LIW_TX;
 I2C1->ICR=CLEAR;
 I2C1->CR2=((uint32_t)addr<<1)|((uint32_t)n<<I2C_CR2_NBYTES_Pos)|I2C_CR2_AUTOEND|I2C_CR2_START;
 return true;
}
void lamp_i2c_write_poll(lamp_i2c_write *w,uint32_t now){
 if(w==NULL || (w->state!=LIW_TX && w->state!=LIW_STOP))return;
 uint32_t flags=I2C1->ISR;
 if(now-w->started>5u || now-w->last_poll>5u || !lamp_i2c_clock_available() ||
    !lamp_i2c_owner_is(w) || (flags&ERRORS)){fail(w,flags);return;}
 w->last_poll=now;
 if(flags&(I2C_ISR_RXNE|I2C_ISR_TC|I2C_ISR_TCR)){fail(w,flags);return;}
 if(w->state==LIW_TX){
  if(flags&I2C_ISR_STOPF){fail(w,flags);return;}
  if(flags&I2C_ISR_TXIS){
   I2C1->TXDR=w->bytes[w->sent++];
   if(w->sent==w->length)w->state=LIW_STOP;
  }
 }else{
  if(flags&I2C_ISR_TXIS){fail(w,flags);return;}
  if((flags&I2C_ISR_STOPF) && !(flags&I2C_ISR_BUSY)){
   I2C1->ICR=I2C_ICR_STOPCF;I2C1->CR2=0u;w->completed=now;
   w->valid=true;w->state=LIW_DONE;lamp_i2c_owner_release(w);
  }
 }
}
bool lamp_i2c_write_done(const lamp_i2c_write *w,uint32_t now){
 return w!=NULL && w->state==LIW_DONE && w->valid &&
        now-w->started<=20u && now-w->last_poll<=5u && lamp_i2c_clock_available();
}
