#include "drill_ui.h"
#include <assert.h>
#include <stdio.h>
static ds_config cfg={50000,2000,100,1000,100000,100,50,65536,0};
static ds_inputs input(uint32_t t,int32_t rpm) {
 ds_inputs i={0}; i.power_ok=i.driver_ready=i.observer_locked=i.current_calibrated=i.handpiece_sensor_ok=true;
 i.measured_at_ms=t;i.measured_rpm=rpm;return i;
}
static dui_report tick(dui_control *u,uint32_t t,uint8_t edge,uint32_t knob,int32_t rpm,bool ready) {
 ds_inputs i=input(t,rpm);return dui_tick(u,t,edge,knob,&i,ready);
}
static void init(dui_control *u) { assert(dui_init(u,&cfg,450,0)); }
int main(void) {
 dui_control u;dui_report r;ds_inputs i;
 init(&u);assert(u.speed.beginner_mode);
 tick(&u,0,1,50000,0,false);r=tick(&u,450,0,50000,0,false);
 assert(r.command_rejected && r.speed.state==DS_DISABLED);
 r=tick(&u,451,0,50000,0,true);assert(r.speed.state==DS_DISABLED); /* no deferred restart */
 tick(&u,500,1,50000,0,true);r=tick(&u,950,0,50000,0,true);
 assert(r.command_accepted && r.speed.state==DS_RUNNING && r.speed.active_speed_limit_rpm==30000);
 r=tick(&u,960,0,50000,1000,true);assert(r.speed.ramped_target_rpm<=30000);
 /* Running mode request rejected, display uses accepted supervisor state. */
 tick(&u,1000,1,50000,1000,true);tick(&u,1100,1,50000,1000,true);tick(&u,1200,1,50000,1000,true);
 r=tick(&u,1650,0,50000,1000,true);assert(r.command_rejected && u.speed.beginner_mode);
 /* Handpiece double reverses only through zero-speed dwell. */
 tick(&u,1700,2,50000,1000,true);tick(&u,1800,2,50000,1000,true);
 r=tick(&u,2250,0,50000,1000,true);assert(r.command_accepted && u.requested_direction==-1);
 r=tick(&u,2251,0,50000,1000,true);assert(r.speed.state==DS_REVERSAL_WAIT && r.speed.q_current_ref_ma==0);
 r=tick(&u,2260,0,50000,0,true);assert(r.speed.state==DS_REVERSAL_WAIT);
 r=tick(&u,2309,0,50000,0,true);assert(r.speed.state==DS_REVERSAL_WAIT);
 r=tick(&u,2310,0,50000,0,true);assert(r.speed.state==DS_RUNNING && r.speed.ramped_target_rpm<0);
 /* Fault on SAME millisecond must stop, before queued gesture dispatch. */
 i=input(2310,0);i.hardware_fault=true;r=dui_tick(&u,2310,1,50000,&i,true);
 assert(r.speed.state==DS_FAULT && !r.speed.run_enable && r.speed.q_current_ref_ma==0);
 r=tick(&u,2760,0,50000,0,true);assert(r.speed.state==DS_FAULT);
 /* Fault can only clear through separate supervisory path. */
 i=input(2760,0);assert(ds_clear_fault(&u.speed,2760,&i));
 r=tick(&u,2761,0,50000,0,true);assert(r.speed.state==DS_DISABLED);
 /* Triple click while stopped toggles mode. */
 tick(&u,2800,1,50000,0,true);tick(&u,2900,1,50000,0,true);tick(&u,3000,1,50000,0,true);
 r=tick(&u,3450,0,50000,0,true);assert(r.command_accepted && !r.speed.beginner_mode && r.speed.active_speed_limit_rpm==50000);
 /* Two simultaneous singles must not start or toggle twice. */
 tick(&u,3500,3,50000,0,true);r=tick(&u,3950,0,50000,0,true);
 assert(r.command_rejected && r.speed.state==DS_DISABLED);
 /* Parking cancels pending start. Pickup never starts. */
 tick(&u,4000,1,50000,0,true);i=input(4450,0);i.handpiece_parked=true;
 r=dui_tick(&u,4450,0,50000,&i,true);assert(r.speed.state==DS_DISABLED);
 r=tick(&u,4451,0,50000,0,true);assert(r.speed.state==DS_DISABLED && r.main_command==DC_NONE);
 /* Unlocked observer denies explicit start even with startup flag. */
 tick(&u,4500,1,50000,0,true);i=input(4950,0);i.observer_locked=false;
 r=dui_tick(&u,4950,0,50000,&i,true);assert(r.command_rejected && !r.speed.run_enable);
 /* Stop dominates a concurrent reverse request. */
 tick(&u,5000,1,1000,0,true);r=tick(&u,5450,0,1000,0,true);assert(r.speed.state==DS_RUNNING);
 tick(&u,5500,2,1000,0,true);tick(&u,5600,3,1000,0,true);
 r=tick(&u,6050,0,1000,0,true);assert(r.command_accepted && r.speed.state==DS_DISABLED);
 /* Direct stop cancels gestures and zero speed cannot start. */
 tick(&u,6100,1,1000,0,true);dui_stop(&u);r=tick(&u,6550,0,1000,0,true);assert(!r.speed.run_enable);
 tick(&u,6600,1,0,0,true);r=tick(&u,7050,0,0,0,true);assert(r.command_rejected && !r.speed.run_enable);
 puts("PASS: startup handoff, mode caps, guarded reversal, same-ms fault, explicit recovery, conflict arbitration, park cancellation, zero-speed rejection");
}
