#include "lamp_i2c_read.h"
#include "lamp_i2c_pins.h"
#include "stm32g0xx.h"
#include <stddef.h>
#define ERRORS (I2C_ISR_NACKF|I2C_ISR_BERR|I2C_ISR_ARLO|I2C_ISR_OVR|I2C_ISR_TIMEOUT)
#define CLEAR_ERRORS (I2C_ICR_NACKCF|I2C_ICR_BERRCF|I2C_ICR_ARLOCF|I2C_ICR_OVRCF|I2C_ICR_TIMOUTCF|I2C_ICR_STOPCF)
static bool ready(void){
    /* Owner has not yet been implemented. Do not invent TIMINGR here. */
    return lamp_i2c_pins_available() && I2C1->CR1==I2C_CR1_PE &&
           I2C1->TIMINGR!=0u;
}
static void fail(lamp_i2c_read *r,uint32_t flags){
    r->state=LIR_FAILED;r->valid=false;r->error_flags=flags;
    /* One transaction owner. Never request STOP after arbitration loss.
     * Disable own engine after abort; no claim that external BUSY clears. */
    if((flags&I2C_ISR_ARLO)==0u && (I2C1->ISR&I2C_ISR_BUSY)!=0u)
        I2C1->CR2|=I2C_CR2_STOP;
    I2C1->CR1&=~I2C_CR1_PE;
    I2C1->ICR=CLEAR_ERRORS;
}
void lamp_i2c_read_reset(lamp_i2c_read *r){if(r!=NULL)*r=(lamp_i2c_read){0};}
bool lamp_i2c_read_start(lamp_i2c_read *r,uint8_t addr,uint8_t reg,uint32_t now){
    if(r==NULL)return false;
    if(r->state!=LIR_IDLE && r->state!=LIR_DONE)return false;
    r->valid=false;
    if((addr!=0x15u && addr!=0x19u && addr!=0x65u) ||
       (reg!=1u && (reg<0x41u || reg>0x46u)) || !ready())return false;
    uint32_t isr=I2C1->ISR;
    /* Do not start while an earlier/unowned bus transaction is active. */
    if((isr&(I2C_ISR_BUSY|I2C_ISR_RXNE|I2C_ISR_TC|I2C_ISR_TCR|ERRORS))!=0u)return false;
    r->address=addr;r->reg=reg;r->started=now;r->last_poll=now;
    r->error_flags=0;r->bound=true;r->state=LIR_TX_POINTER;
    I2C1->ICR=CLEAR_ERRORS;
    I2C1->CR2=((uint32_t)addr<<1)|(1u<<I2C_CR2_NBYTES_Pos)|I2C_CR2_START;
    return true;
}
void lamp_i2c_read_cancel(lamp_i2c_read *r){
    if(r==NULL || !r->bound)return;
    if(r->state==LIR_TX_POINTER || r->state==LIR_WAIT_TC ||
       r->state==LIR_RX_BYTE || r->state==LIR_WAIT_STOP)fail(r,I2C1->ISR);
    else r->valid=false; /* Do not abort twice after cleared arbitration loss. */
}
void lamp_i2c_read_poll(lamp_i2c_read *r,uint32_t now){
    if(r==NULL || !r->bound || r->state==LIR_IDLE || r->state==LIR_FAILED || r->state==LIR_DONE)return;
    uint32_t flags=I2C1->ISR;
    if(now-r->started>5u || now-r->last_poll>5u || !ready() || (flags&ERRORS)!=0u){fail(r,flags);return;}
    r->last_poll=now;
    switch(r->state){
    case LIR_TX_POINTER:
        if((flags&(I2C_ISR_STOPF|I2C_ISR_RXNE|I2C_ISR_TC|I2C_ISR_TCR))!=0u){fail(r,flags);return;}
        if((flags&I2C_ISR_TXIS)!=0u){I2C1->TXDR=r->reg;r->state=LIR_WAIT_TC;}
        break;
    case LIR_WAIT_TC:
        if((flags&(I2C_ISR_STOPF|I2C_ISR_RXNE|I2C_ISR_TCR))!=0u){fail(r,flags);return;}
        if((flags&I2C_ISR_TC)!=0u){
            I2C1->CR2=((uint32_t)r->address<<1)|(1u<<I2C_CR2_NBYTES_Pos)|
                I2C_CR2_RD_WRN|I2C_CR2_AUTOEND|I2C_CR2_START;
            r->state=LIR_RX_BYTE;
        }
        break;
    case LIR_RX_BYTE:
        if((flags&I2C_ISR_RXNE)!=0u){r->value=(uint8_t)I2C1->RXDR;r->state=LIR_DONE;r->valid=true;}
        else if((flags&(I2C_ISR_STOPF|I2C_ISR_TC|I2C_ISR_TCR))!=0u){fail(r,flags);return;}
        break;
    case LIR_WAIT_STOP:
        if((flags&I2C_ISR_RXNE)!=0u){fail(r,flags);return;}
        if((flags&I2C_ISR_STOPF)!=0u && (flags&I2C_ISR_BUSY)==0u){
            I2C1->ICR=I2C_ICR_STOPCF;I2C1->CR2=0u;
            r->completed=now;r->state=LIR_DONE;r->valid=true;
        }
        break;
    default:fail(r,flags);break;
    }
}
bool lamp_i2c_read_get(const lamp_i2c_read *r,uint32_t now,uint8_t *value){
    if(r==NULL || value==NULL || !r->bound || !r->valid || r->state!=LIR_DONE ||
       now-r->started>20u || now-r->last_poll>5u || !ready())return false;
    *value=r->value;return true;
}
