// https://wokwi.com/projects/386139924464331777
// https://forum.arduino.cc/t/fader-pickup-catch-mode/1206227

// temporarily 0..31 disaplyed on 16 LEDs.
# define MAXX 32    // 128
# define OSCALE 2   // 8 so 16 LEDs are 0..127

# define ENCODER_CLK 2
# define ENCODER_DT  3

# define slideFaderPin A0

bool rotaryCCW;
bool rotaryCW;

# define testSnapZero A1
# define testSnapMAXX A2

# include <Adafruit_NeoPixel.h>

# define N_REAL   16
# define PIN      8

Adafruit_NeoPixel led(N_REAL, PIN, NEO_GRB + NEO_KHZ800);

# define faderPrime   6
# define rotaryPrime  5
# define catchMode    4

int rotaryValue = 16;
int slideFaderValue;

void setup()
{
  Serial.begin(115200); Serial.println("first things first");

  pinMode(ENCODER_CLK, INPUT);
  pinMode(ENCODER_DT, INPUT);
  attachInterrupt(digitalPinToInterrupt(ENCODER_CLK), readEncoder, FALLING);

  pinMode(faderPrime, OUTPUT);
  pinMode(rotaryPrime, OUTPUT);
  pinMode(catchMode, OUTPUT);

// test buttons for very rapid slide fading LOW equals rapid movement to value
  pinMode(testSnapZero, INPUT_PULLUP);
  pinMode(testSnapMAXX, INPUT_PULLUP); 

  led.begin();
  led.setPixelColor(1,  0xff0000);
  led.show();
  delay(777);
}

bool trackA = true;

enum {ROT = 0, FADE, FADE_LOW, FADE_HIGH, FADE_EQUAL};

char *stateTags[] = {"ROT", "FADE", "FADE_LOW", "FADE_HIGH", "FADE_EQUAL"};

unsigned char state = FADE;

void loop()
{
  static int lastSlideFaderValue;
  static int output;

// INPUT

  bool slideFaderInput = false;
  int faderReading;
  faderReading = analogRead(slideFaderPin);

// these are so we can easily yank the fader to an extreme. still goes through mapping &c.
  if (!digitalRead(testSnapZero)) faderReading = 0;
  if (!digitalRead(testSnapMAXX)) faderReading = 1023;

  int slideFaderValue = map(faderReading, 0, 1023, 0, MAXX);
  if (slideFaderValue >= MAXX) slideFaderValue = MAXX - 1;   // argh! map!

  if (slideFaderValue != lastSlideFaderValue) {
    slideFaderInput = true;
//    lastSlideFaderValue = slideFaderValue; don't update this until it has informed any change of state
  }

  bool rotaryInput = rotaryCW || rotaryCCW;

  /*
    Serial.print(" slideFader at ");
    Serial.print(valA);
    Serial.print(" rotary accumulated value ");
    Serial.print(rotaryValue);
    Serial.println("");
  */

// PROCESS

  if (rotaryInput) {
    if (state == FADE) {
      rotaryValue = slideFaderValue;  // coming from fader, we grab the current value
    }
    state = ROT;    // always

    if (rotaryCW) {
      rotaryValue++;
      rotaryCW = false;
      rotaryInput = true;
    }
    if (rotaryCCW) {
      rotaryValue--;
      rotaryCCW = false;
      rotaryInput = true;
    }

    if (rotaryValue  < 0) rotaryValue = 0;
    if (rotaryValue >= MAXX) rotaryValue = MAXX - 1;
  }

// just to report new state
  static unsigned lastState = 0xff;   // no state
  if (state != lastState) {
    Serial.print("state = "); Serial.println(stateTags[state]);
    lastState = state;
  }

  switch (state) {
  case ROT : // the rotary encoder is (still) in charge
    output = rotaryValue;
    if (slideFaderInput) {
      if (lastSlideFaderValue < rotaryValue) state = FADE_LOW;
      else if (lastSlideFaderValue > rotaryValue) state = FADE_HIGH;
      else { // slideFaderValue == rotaryValue... it could happen

Serial.println("does this hapen? How do we deal with it?");
        output = slideFaderValue;
        state = FADE;
      }
    }
    break;

  case FADE_LOW : // rotary constant, slide fader activity, wait it may exceed and capture
    if (slideFaderValue >= rotaryValue) {
      Serial.println("                   pickup catch from below!");
      state = FADE;
      output = slideFaderValue;
    }
    break;

  case FADE_HIGH : // rotary constant, slide fader activity, it may subceed and capture
    if (slideFaderValue <= rotaryValue) {
      Serial.println("                   pickup catch from above!");
      state = FADE;
      output = slideFaderValue;
    }
    break;

  //  case FADE_EQUAL : // silde fader good to just take over - it already did, nevermind
  //    break;

  case FADE :
    //    rotaryValue = slideFaderValue;
    output = slideFaderValue;
    break;

  default :
    Serial.println("impossible"); while (1);
  }

// now
  lastSlideFaderValue = slideFaderValue;

// OUTPUT

  //  Serial.println(output);

  setLAMPS();   // status indicators

  led.clear();
  led.setPixelColor(slideFaderValue / OSCALE, 0xd00000);
  led.setPixelColor(output / OSCALE, 0xff40c0);
  led.show();
}

void readEncoder() {
  int dtValue = digitalRead(ENCODER_DT);
  if (dtValue == HIGH) {
    //    Serial.println("Rotated clockwise >>");
    rotaryCW = true;
  }
  if (dtValue == LOW) {
    //    Serial.println("Rotated counterclockwise <<");
    rotaryCCW = true;
  }
}

void setLAMPS()
{
  switch (state) {
  case ROT :
    digitalWrite(rotaryPrime, HIGH);
    digitalWrite(faderPrime, LOW);
    digitalWrite(catchMode, LOW);

    break;

  case FADE_LOW :
  case FADE_HIGH :
    digitalWrite(rotaryPrime, HIGH);
    digitalWrite(faderPrime, LOW);
    digitalWrite(catchMode, HIGH);

    break;

  case FADE :
    digitalWrite(faderPrime, HIGH);
    digitalWrite(rotaryPrime, LOW);
    digitalWrite(catchMode, LOW);

    break;

  default :
    Serial.println("impossible"); while (1);
  }
}


zero
maxx
instant MAXX
instant ZERO