#include "lamp_frame_stage.h"
#include <stddef.h>
#include <string.h>
static const uint8_t addr[3]={0x15u,0x19u,0x65u};
static bool allowed(const lg_guard *g,const lamp_current_profile *p,uint32_t now){
 return g!=NULL && p!=NULL && p->physically_qualified && p->calibration_id!=0u &&
 g->configured && g->armed && g->session_id!=0u && g->cfg.channels==61u &&
 g->cfg.calibration_id==p->calibration_id && (g->mode==LG_AI || g->mode==LG_MANUAL) &&
 g->output_enable && g->hand_present && g->vision_status_seen && g->frame_active &&
 g->inputs.infrared_hand_present && g->inputs.temperature_ok && g->inputs.supply_ok &&
 g->inputs.led_driver_ok && g->inputs.hardware_interlock_ok &&
 g->cfg.max_poll_gap_ms!=0u && now-g->last_poll_ms<=g->cfg.max_poll_gap_ms &&
 g->cfg.ticket_lifetime_ms!=0u && now-g->vision_status_ms<g->cfg.ticket_lifetime_ms &&
 now-g->frame_issued_ms<g->cfg.ticket_lifetime_ms &&
 g->run_duration_ms!=0u && now-g->run_started_ms<g->run_duration_ms;
}
static bool same(const lamp_frame_stage *s,const lg_guard *g,const lamp_current_profile *p,uint32_t now){
 return allowed(g,p,now) && s->session_id==g->session_id &&
 s->calibration_id==p->calibration_id && memcmp(s->pwm,g->pwm,61)==0 &&
 memcmp(s->iref,p->iref,61)==0;
}
static uint8_t expected(const lamp_frame_stage *s,unsigned chip,unsigned reg){
 if(reg==0u)return 0u; /* Single-register transactions, no auto-increment. */
 if(reg==1u)return 0u;
 if(reg>=2u && reg<=7u){
  uint8_t value=0;
  for(unsigned j=0;j<4;j++)if(chip*24u+(reg-2u)*4u+j<61u)value|=(uint8_t)(2u<<(j*2u));
  return value;
 }
 if(reg>=10u && reg<=33u){unsigned ch=chip*24u+reg-10u;return ch<61u?s->pwm[ch]:0u;}
 if(reg>=34u && reg<=57u){unsigned ch=chip*24u+reg-34u;return ch<61u?s->iref[ch]:0u;}
 return 0u; /* Group controls disabled by individual PWM LEDOUT mode. */
}
void lamp_frame_stage_abort(lamp_frame_stage *s){
 if(s==NULL)return;
 lamp_i2c_write_cancel(&s->writer);lamp_i2c_read_cancel(&s->reader);s->state=LFS_FAILED;
}
bool lamp_frame_stage_begin(lamp_frame_stage *s,const lg_guard *g,const lamp_current_profile *p,uint32_t now){
 if(s==NULL || (s->state!=LFS_IDLE && s->state!=LFS_DONE) || !allowed(g,p,now))return false;
 for(unsigned j=0;j<61;j++)if(g->pwm[j]!=0u && p->iref[j]==0u)return false;
 lamp_i2c_write_reset(&s->writer);lamp_i2c_read_reset(&s->reader);
 memcpy(s->pwm,g->pwm,61);memcpy(s->iref,p->iref,61);
 s->session_id=g->session_id;s->calibration_id=p->calibration_id;
 s->started=s->last_poll=now;s->index=0;s->state=LFS_WRITE_BEGIN;return true;
}
void lamp_frame_stage_poll(lamp_frame_stage *s,const lg_guard *g,const lamp_current_profile *p,uint32_t now){
 if(s==NULL || s->state==LFS_IDLE || s->state==LFS_FAILED)return;
 if(!same(s,g,p,now) || now-s->last_poll>5u || now-s->started>500u){lamp_frame_stage_abort(s);return;}
 s->last_poll=now;
 if(s->state==LFS_DONE)return;
 unsigned chip=s->index/58u,reg=s->index%58u;uint8_t value=expected(s,chip,reg);
 switch(s->state){
 case LFS_WRITE_BEGIN:{
  const uint8_t frame[2]={(uint8_t)reg,value};
  if(!lamp_i2c_write_start(&s->writer,addr[chip],frame,2u,now)){lamp_frame_stage_abort(s);return;}
  s->state=LFS_WRITE_WAIT;break;
 }
 case LFS_WRITE_WAIT:
  lamp_i2c_write_poll(&s->writer,now);
  if(s->writer.state==LIW_FAILED){lamp_frame_stage_abort(s);return;}
  if(lamp_i2c_write_done(&s->writer,now))s->state=LFS_READ_BEGIN;
  break;
 case LFS_READ_BEGIN:
  if(!lamp_i2c_read_start(&s->reader,addr[chip],(uint8_t)reg,now)){lamp_frame_stage_abort(s);return;}
  s->state=LFS_READ_WAIT;break;
 case LFS_READ_WAIT:{
  lamp_i2c_read_poll(&s->reader,now);
  if(s->reader.state==LIR_FAILED){lamp_frame_stage_abort(s);return;}
  uint8_t actual;
  if(lamp_i2c_read_get(&s->reader,now,&actual)){
   if(actual!=(reg==1u?0x05u:value)){lamp_frame_stage_abort(s);return;}
   ++s->index;
   if(reg==0u){s->wake_started=now;s->state=LFS_WAKE;}
   else s->state=s->index==174u?LFS_DONE:LFS_WRITE_BEGIN;
  }
  break;
 }
 case LFS_WAKE:if(now-s->wake_started>=2u)s->state=LFS_WRITE_BEGIN;break;
 default:lamp_frame_stage_abort(s);break;
 }
}
bool lamp_frame_stage_done(const lamp_frame_stage *s,const lg_guard *g,const lamp_current_profile *p,uint32_t now){
 return s!=NULL && s->state==LFS_DONE && same(s,g,p,now) && now-s->last_poll<=5u && now-s->started<=500u;
}
