#include "wled.h"

/*
 * Usermods allow you to add own functionality to WLED more easily
 * See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
 * 
 * This is Stairway-Wipe as a v2 usermod.
 * 
 * Using this usermod:
 * 1. Copy the usermod into the sketch folder (same folder as wled00.ino)
 * 2. Register the usermod by adding #include "stairway-wipe-usermod-v2.h" in the top and registerUsermod(new StairwayWipeUsermod()) in the bottom of usermods_list.cpp
 */

class StairwayWipeUsermod : public Usermod {
  private:
    //Private class members. You can declare variables and functions only accessible to your usermod here
    unsigned long lastTime = 0;
    byte wipeState = 0; //0: inactive 1: wiping 2: solid
    unsigned long timeStaticStart = 0;
    uint16_t previousUserVar0 = 0;

//comment this out if you want the turn off effect to be just fading out instead of reverse wipe
#define STAIRCASE_WIPE_OFF
  public:

    void loop() {
  //userVar0 (U0 in HTTP API):
  //has to be set to 1 if movement is detected on the PIR that is the same side of the staircase as the ESP8266
  //has to be set to 2 if movement is detected on the PIR that is the opposite side
  //can be set to 0 if no movement is detected. Otherwise LEDs will turn off after a configurable timeout (userVar1 seconds)

  if (userVar0 > 0)
  {
    if ((previousUserVar0 == 1 && userVar0 == 2) || (previousUserVar0 == 2 && userVar0 == 1)) wipeState = 3; //turn off if other PIR triggered
    previousUserVar0 = userVar0;
    
    if (wipeState == 0) {
      startWipe();
      wipeState = 1;
    } else if (wipeState == 1) { //wiping
      uint32_t cycleTime = 360 + (255 - effectSpeed)*75; //this is how long one wipe takes (minus 25 ms to make sure we switch in time)
      if (millis() + strip.timebase > (cycleTime - 25)) { //wipe complete
        effectCurrent = FX_MODE_STATIC;
        timeStaticStart = millis();
        colorUpdated(NOTIFIER_CALL_MODE_NOTIFICATION);
        wipeState = 2;
      }
    } else if (wipeState == 2) { //static
      if (userVar1 > 0) //if U1 is not set, the light will stay on until second PIR or external command is triggered
      {
        if (millis() - timeStaticStart > userVar1*1000) wipeState = 3;
      }
    } else if (wipeState == 3) { //switch to wipe off
      #ifdef STAIRCASE_WIPE_OFF
      effectCurrent = FX_MODE_COLOR_WIPE;
      strip.timebase = 360 + (255 - effectSpeed)*75 - millis(); //make sure wipe starts fully lit
      colorUpdated(NOTIFIER_CALL_MODE_NOTIFICATION);
      wipeState = 4;
      #else
      turnOff();
      #endif
    } else { //wiping off
      if (millis() + strip.timebase > (725 + (255 - effectSpeed)*150)) turnOff(); //wipe complete
    }
  } else {
    wipeState = 0; //reset for next time
    if (previousUserVar0) {
      #ifdef STAIRCASE_WIPE_OFF
      userVar0 = previousUserVar0;
      wipeState = 3;
      #else
      turnOff();
      #endif
    }
    previousUserVar0 = 0;
  }
}

    void readFromJsonState(JsonObject& root)
    {
      userVar0 = root["user0"] | userVar0; //if "user0" key exists in JSON, update, else keep old value
      //if (root["bri"] == 255) Serial.println(F("Don't burn down your garage!"));
    }

    void addToConfig(JsonObject& root)
    {
      JsonObject top = root.createNestedObject("exampleUsermod");
      top["great"] = userVar0; //save this var persistently whenever settings are saved
    }

    void readFromConfig(JsonObject& root)
    {
      JsonObject top = root["top"];
      userVar0 = top["great"] | 42; //The value right of the pipe "|" is the default value in case your setting was not present in cfg.json (e.g. first boot)
    }

    uint16_t getId()
    {
      return USERMOD_ID_EXAMPLE;
    }


    void startWipe()
    {
    bri = briLast; //turn on
    transitionDelayTemp = 0; //no transition
    effectCurrent = FX_MODE_COLOR_WIPE;
    resetTimebase(); //make sure wipe starts from beginning

    //set wipe direction
    WS2812FX::Segment& seg = strip.getSegment(0);
    bool doReverse = (userVar0 == 2);
    seg.setOption(1, doReverse);

    colorUpdated(NOTIFIER_CALL_MODE_NOTIFICATION);
    }

    void turnOff()
    {
    #ifdef STAIRCASE_WIPE_OFF
    transitionDelayTemp = 0; //turn off immediately after wipe completed
    #else
    transitionDelayTemp = 4000; //fade out slowly
    #endif
    bri = 0;
    colorUpdated(NOTIFIER_CALL_MODE_NOTIFICATION);
    wipeState = 0;
    userVar0 = 0;
    previousUserVar0 = 0;
    }



   //More methods can be added in the future, this example will then be extended.
   //Your usermod will remain compatible as it does not need to implement all methods from the Usermod base class!
};
