#include "drill_ui.h"
#include <string.h>
static int32_t target(const dui_control *u) {
 return u->requested_direction < 0 ? -(int32_t)u->knob_rpm : (int32_t)u->knob_rpm;
}
bool dui_init(dui_control *u, const ds_config *cfg, uint32_t click_ms, uint32_t now) {
 if (!u) return false;
 memset(u,0,sizeof(*u));
 if (!ds_init(&u->speed,cfg,now) || !dc_init(&u->clicks,click_ms)) return false;
 u->requested_direction=1;
 if (!ds_set_beginner_mode(&u->speed,true)) return false;
 u->configured=true;
 return true;
}
void dui_stop(dui_control *u) {
 if (!u) return;
 dc_cancel(&u->clicks);
 ds_stop(&u->speed);
}
dui_report dui_tick(dui_control *u, uint32_t now, uint8_t releases,
                    uint32_t knob_rpm, const ds_inputs *in, bool startup_ready) {
 dui_report r={0};
 r.speed.state=DS_FAULT; r.speed.fault=DS_FAULT_CONFIG;
 if (!u || !u->configured) return r;
 u->knob_rpm=knob_rpm>u->speed.cfg.max_rpm?u->speed.cfg.max_rpm:knob_rpm;
 r.speed=ds_update(&u->speed,target(u),now,in);
 /* Physical fault/park/power transitions cancel stale gestures, including
    ones that matured on this tick. No pickup or recovery auto-restart. */
 if (!in || !in->power_ok || !in->driver_ready || !in->current_calibrated ||
     !in->handpiece_sensor_ok || in->handpiece_parked || in->hardware_fault ||
     in->overcurrent_fault || r.speed.state==DS_FAULT) {
  dui_stop(u);
  r.speed=ds_update(&u->speed,target(u),now,in);
  return r;
 }
 r.main_command=dc_poll(&u->clicks,DC_MAIN_KEY,now);
 r.handpiece_command=dc_poll(&u->clicks,DC_HANDPIECE_KEY,now);
 bool running=r.speed.state==DS_RUNNING || r.speed.state==DS_REVERSAL_WAIT;
 bool two=r.main_command!=DC_NONE && r.handpiece_command!=DC_NONE;
 bool stop=running && (r.main_command==DC_TOGGLE_RUN_REQUEST ||
                      r.handpiece_command==DC_TOGGLE_RUN_REQUEST);
 if (stop) {
  dui_stop(u); r.command_accepted=true;
 } else if (two) {
  /* Candidate arbitration: simultaneous requests reject instead of toggling
     twice or picking an undocumented order. A stop above always dominates. */
  r.command_rejected=true;
 } else {
  dc_command c=r.main_command!=DC_NONE?r.main_command:r.handpiece_command;
  switch(c) {
   case DC_TOGGLE_RUN_REQUEST:
    r.command_accepted=startup_ready && ds_start(&u->speed,target(u),now,in);
    r.command_rejected=!r.command_accepted;
    break;
   case DC_REVERSE_REQUEST:
    u->requested_direction=(int8_t)-u->requested_direction;
    r.command_accepted=true;
    break;
   case DC_MODE_REQUEST:
    r.command_accepted=ds_set_beginner_mode(&u->speed,!u->speed.beginner_mode);
    r.command_rejected=!r.command_accepted;
    break;
   case DC_REJECTED: r.command_rejected=true; break;
   case DC_NONE: break;
  }
 }
 /* Repeated same-ms update now evaluates faults before returning; speed PI
    advances only once per millisecond. Reversal is applied on next dt>0. */
 r.speed=ds_update(&u->speed,target(u),now,in);
 if (!stop) {
  if ((releases & ~(DUI_MAIN_RELEASE|DUI_HANDPIECE_RELEASE)) != 0u)
   r.command_rejected=true;
  else {
   if (releases&DUI_MAIN_RELEASE) (void)dc_release(&u->clicks,DC_MAIN_KEY,now);
   if (releases&DUI_HANDPIECE_RELEASE) (void)dc_release(&u->clicks,DC_HANDPIECE_KEY,now);
  }
 }
 return r;
}
