#include "soft_i2c.h"
static bool check(si_bus*b){
 if(b->polls++>=20000u){b->status=SI_TIMER_STALLED;return false;}
 if((uint16_t)(b->ticks(b->ctx)-b->started)>=4000u){b->status=SI_DEADLINE;return false;}
 return true;
}
static bool delay_us(si_bus*b,uint16_t amount){
 uint16_t start=b->ticks(b->ctx);
 do {if(!check(b))return false;} while((uint16_t)(b->ticks(b->ctx)-start)<(uint16_t)(amount+1u));
 return true;
}
static bool delay(si_bus*b){return delay_us(b,5u);}
static bool low(si_bus*b){
 b->scl(b->ctx,false);uint16_t start=b->ticks(b->ctx);
 while(b->read_scl(b->ctx)){
  if(!check(b))return false;
  if((uint16_t)(b->ticks(b->ctx)-start)>=100u){b->status=SI_DATA_HELD;return false;}
 }
 /* Observe a real low then cover max300ns fall allowance with nominal>=1us.
    +1 timer tick compensates quantization, assuming1MHz timer. */
 return delay_us(b,1u);
}
static bool high(si_bus*b){
 b->scl(b->ctx,true);uint16_t start=b->ticks(b->ctx);
 while(!b->read_scl(b->ctx)){
  if(!check(b))return false;
  if((uint16_t)(b->ticks(b->ctx)-start)>=100u){b->status=SI_STRETCH_TIMEOUT;return false;}
 }
 return delay(b);
}
static bool byte(si_bus*b,uint8_t v){
 for(unsigned bit=0;bit<8;bit++){
  bool one=(v&0x80u)!=0;v<<=1;
  b->sda(b->ctx,one);
  if(!delay(b)||!high(b))return false;
  if(one&&!b->read_sda(b->ctx)){b->status=SI_DATA_HELD;return false;}
  if(!low(b))return false;
 }
 b->sda(b->ctx,true);
 if(!delay(b)||!high(b))return false;
 bool ack=!b->read_sda(b->ctx);if(!low(b))return false;
 if(!ack){b->status=SI_NACK;return false;}return true;
}
static bool stop(si_bus*b){
 b->sda(b->ctx,false);
 if(!delay(b)||!high(b))return false;
 b->sda(b->ctx,true);
 if(!delay(b))return false;
 if(!b->read_sda(b->ctx)){b->status=SI_DATA_HELD;return false;}
 return true;
}
bool si_write(si_bus*b,uint8_t addr,const uint8_t*data,size_t n){
 if(!b)return false;
 b->status=SI_ARGUMENT;
 if(!data||!n||n>17||addr>127||!b->scl||!b->sda||!b->read_scl||!b->read_sda||!b->ticks)return false;
 b->status=SI_OK;b->polls=0;b->started=b->ticks(b->ctx);
 b->sda(b->ctx,true);b->scl(b->ctx,true);
 if(!delay(b))goto failed;
 if(!b->read_scl(b->ctx)||!b->read_sda(b->ctx)){b->status=SI_BUSY;goto failed;}
 b->sda(b->ctx,false);if(!delay(b)||!low(b))goto failed;
 if(!byte(b,(uint8_t)(addr<<1)))goto failed;
 for(size_t i=0;i<n;i++)if(!byte(b,data[i]))goto failed;
 if(!stop(b))goto failed;
 return true;
failed:
 if(b->status==SI_NACK){si_status cause=b->status;(void)stop(b);b->status=cause;}
 b->scl(b->ctx,true);b->sda(b->ctx,true);return false;
}
