// EncoderRing https://wokwi.com/projects/417712838205345793
// Control the a FastLED strip's NUM_LEDS, Hue, Saturation, and Value
// Based on
// https://wokwi.com/projects/304919215794553409 -- Wokwi Encoder Volume
// https://wokwi.com/projects/417720757017823233 -- Wokwi Encoder 6 variables
//
// https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#Basicframework
//
// See also https://forum.arduino.cc/t/wokwi-simulations-for-arduino-built-in-examples/1304754/9

#include "FastLED.h" // https://github.com/FastLED/FastLED
#include <Encoder.h> // https://github.com/PaulStoffregen/Encoder
#define NUM_LEDS 60
CRGB leds[NUM_LEDS];
#define PIN 6

const byte EncClockPin = 2;
const byte EncDataPin = 5;
const byte EncSwPin = 7;

Encoder enc(EncClockPin, EncDataPin);


uint8_t hue = 0;
uint8_t sat = 255;
uint8_t val = 255;
uint8_t num = NUM_LEDS - 5 ; // leave an initial gap
bool lastSwitchPressed;
uint32_t lastChangeMs = 0;
uint32_t lastChangedMs;
bool dirty = false;

typedef enum {
  SET_NUM,
  SET_HUE,
  SET_SAT,
  SET_VALUE,
} EncoderMode;

EncoderMode encoderMode = SET_NUM;

void report() {
  char* names[] = {"Num", "Hue", "Sat", "Val"};

  Serial.print("EncoderMode:");
  Serial.print(encoderMode);
  Serial.print(",");
  Serial.print(names[encoderMode]);
  Serial.print(" num:");
  Serial.print(num);
  Serial.print(" H:");
  Serial.print(hue);
  Serial.print(" S:");
  Serial.print(sat);
  Serial.print(" V:");
  Serial.print(val);

  Serial.println();
}

void refreshStrip() { // refresh if dirty
  const uint32_t interval = 100;
  static uint32_t lastUpdate = -interval;
  //  if (millis() - lastUpdate >= interval) {
  if (dirty) {
    lastUpdate = millis();
    for (int ii = 0; ii < NUM_LEDS; ++ii) {
      if (ii < num ) {
        leds[ii].setHSV( hue, sat, val);
      } else {
        leds[ii].setRGB( 0, 0, 0);
      }
    }
    showStrip();
  }
}

// Encoder State machine handling
void nextMode() { // advance through button-pressing
  switch (encoderMode) {
    case SET_NUM:
      encoderMode = SET_HUE;
      break;

    case SET_HUE:
      encoderMode = SET_SAT;
      break;

    case SET_SAT:
      encoderMode = SET_VALUE;
      break;

    case SET_VALUE:
      encoderMode = SET_NUM;
      break;
  }
}

void updateValue(int delta) { // tweak variables based on encoderMode
  switch (encoderMode) {
    case SET_NUM:
      num = constrain(num + delta, 0, NUM_LEDS);
      break;

    case SET_HUE:
      hue = (hue + delta) &0xFF;  // loop around 
      break;

    case SET_SAT:
      sat = constrain(sat + delta, 0, 255);
      break;

    case SET_VALUE:
      val = constrain(val + delta, 0, 255);
      break;
  }
}

// neopixel/fastled Generalizing bits
void showStrip() {
#ifdef ADAFRUIT_NEOPIXEL_H
  // NeoPixel
  strip.show();
#endif
#ifndef ADAFRUIT_NEOPIXEL_H
  // FastLED
  FastLED.show();
#endif
}

void setPixel(int Pixel, byte red, byte green, byte blue) {
#ifdef ADAFRUIT_NEOPIXEL_H
  // NeoPixel
  strip.setPixelColor(Pixel, strip.Color(red, green, blue));
#endif
#ifndef ADAFRUIT_NEOPIXEL_H
  // FastLED
  leds[Pixel].r = red;
  leds[Pixel].g = green;
  leds[Pixel].b = blue;
#endif
}

void setAll(byte red, byte green, byte blue) {
  for (int i = 0; i < NUM_LEDS; i++ ) {
    setPixel(i, red, green, blue);
  }
  showStrip();
}

// Arduino main()-called functions:

void setup()
{
  FastLED.addLeds<WS2811, PIN, GRB>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
  Serial.begin(115200);
  pinMode(EncSwPin, INPUT_PULLUP);
  lastSwitchPressed = digitalRead(EncSwPin) == LOW;
  dirty = true;
}

void loop() {
  static long lastEncVal = 0;

  long encVal = enc.read() >> 2;
  bool switchPressed = digitalRead(EncSwPin) == LOW;

  if (switchPressed != lastSwitchPressed && millis() - lastChangedMs > 300  ) {
    lastChangedMs = millis();
    nextMode();
    //Serial.print(encoderMode);
    dirty = true; // mark for refresh
  }

  if (encVal != lastEncVal) {
    int delta = encVal - lastEncVal > 0 ? -1 : 1;
    lastEncVal = encVal;
    updateValue(delta);
    dirty = true; // mark for refresh
  }

  if (dirty) {
    refreshStrip();
    report();
    dirty = false;
  }
}