#pragma once
#include <stdint.h>
// Hardware-independent state machine, ticked even when Wi-Fi/TLS is blocked.
class MotionLight {
public:
  bool automatic=true, ready=false, motion=false;
  uint32_t holdMs=30000, fadeMs=2000;
  uint8_t maximum=255, value=0;
  explicit MotionLight(uint32_t now=0):boot(now),lastMotion(now),fadeStart(now) {}
  void configure(bool mode,uint32_t hold,uint32_t fade,uint8_t max,uint32_t now) {
    automatic=mode;holdMs=hold;fadeMs=fade;maximum=max;seen=false;
    target(0,now);
  }
  void manual(bool on,uint32_t now) {automatic=false;seen=false;target(on?maximum:0,now);}
  void networkLost(uint32_t now) {if(!automatic) {value=0;from=0;goal=0;fadeStart=now;}}
  uint8_t tick(uint32_t now,bool high) {
    if(!ready && uint32_t(now-boot)>=60000)ready=true;
    motion=ready && high;
    if(automatic) {
      if(motion){lastMotion=now;seen=true;}
      target(seen && uint32_t(now-lastMotion)<holdMs ? maximum:0,now);
    }
    advance(now);return value;
  }
private:
  uint32_t boot,lastMotion,fadeStart;
  uint8_t from=0,goal=0;
  bool seen=false;
  void advance(uint32_t now) {
    uint32_t elapsed=uint32_t(now-fadeStart);
    if(elapsed>=fadeMs || fadeMs==0)value=goal;
    else value=int(from)+(int(goal)-int(from))*int64_t(elapsed)/fadeMs;
  }
  void target(uint8_t next,uint32_t now) {
    if(goal==next)return;
    advance(now);from=value;goal=next;fadeStart=now;
  }
};
