#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;
}

/* Combined register-pointer write and repeated-START read, single master. */
bool si_read_register(si_bus*b,uint8_t addr,uint8_t reg,uint8_t*out,size_t n){
 if(!b)return false;
 b->status=SI_ARGUMENT;
 if(!out||!n||n>17||addr>127||!b->scl||!b->sda||!b->read_scl||!b->read_sda||!b->ticks)return false;
 for(size_t i=0;i<n;i++)out[i]=0;
 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))||!byte(b,reg))goto failed;
 /* Repeated START: release SDA while SCL is low, then assert START high. */
 b->sda(b->ctx,true);if(!delay(b)||!high(b))goto failed;
 if(!b->read_sda(b->ctx)){b->status=SI_DATA_HELD;goto failed;}
 b->sda(b->ctx,false);if(!delay(b)||!low(b))goto failed;
 if(!byte(b,(uint8_t)((addr<<1)|1u)))goto failed;
 for(size_t i=0;i<n;i++){
  uint8_t v=0;b->sda(b->ctx,true);
  for(unsigned j=0;j<8;j++){
   if(!delay(b)||!high(b))goto failed;
   v=(uint8_t)((v<<1)|(b->read_sda(b->ctx)?1u:0u));
   if(!low(b))goto failed;
  }
  bool last=i+1u==n;
  b->sda(b->ctx,last); /* ACK every intermediate byte, final NACK */
  if(!delay(b)||!high(b))goto failed;
  if(last&&!b->read_sda(b->ctx)){b->status=SI_DATA_HELD;goto failed;}
  if(!low(b))goto failed;
  out[i]=v;
 }
 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);
 for(size_t i=0;i<n;i++)out[i]=0;
 return false;
}
