// https://forum.arduino.cc/t/resetting-data-for-addressable-leds/1245705/
// Rehash of code from https://forum.arduino.cc/t/resetting-data-for-addressable-leds/1245705/64?u=davex
// and https://wokwi.com/projects/394978368130357249

#include <FastLED.h>

byte RedV = 100; //variable for Red amount
byte GreenV = 100; //variable for green amount
byte BlueV = 100;  //variable for blue amount
byte Lights = 0; //variable for state of lights (on,off,auto)
byte Old_Lights;
byte Pattern = 0 ; //variable for pattern
byte Old_Pattern;
byte Old_RedV;
byte Old_GreenV;
byte Old_BlueV;
byte RandomRed;
byte RandomGreen;
byte RandomBlue;

// byte newRGB[] = {100, 100, 100};
// byte oldRGB[3];
// byte rndRGB[3];

// int NUM_LEDS[] = {150, 150, 220, 150};
// int DATA_PINS[] = {28, 29, 31, 31};

//---------------------LED STRIP 1-------------------------------
#define NUM_LEDS 150 //number of LEDs on garage
#define DATA_PIN 28  //data pin for lights
CRGB garage[NUM_LEDS]; //light array
//***************************************************************
//-------------------LED STRIP 2----------------------------------
#define NUM_LEDS2 150 // Lights on the house 
#define DATA_PIN2 29 // Data pin for house lights
CRGB house[NUM_LEDS2]; //House light array
//***************************************************************
//----------------------LED STRIP 3------------------------------
#define NUM_LEDS3 220 // Lights on the handrail 
#define DATA_PIN3 30 // Data pin for Handrail lights
CRGB handrail[NUM_LEDS3]; //handrail light array
//*************************************************************
//----------------------LED STRIP 4------------------------------
#define NUM_LEDS4 150 // EXTRA Light Strip
#define DATA_PIN4 31 // Data pin for Extra Light Strip
CRGB leds4[NUM_LEDS4]; //house light array
//*************************************************************

//---------------MOTION SENSOR STUFF---------------------------
byte GarageSensor = 22; //Garage motion sensor pin
byte pirValueG; //gets sets to 1 or 0 to set pin to high or low state
byte DoorSensor = 51; //Front Door Motion Sensor pin
byte pirValueD; //
//*************************************************************

enum Strands {GARAGE, HOUSE, HANDRAIL, EXTRA};
const int NumStrands = 4;

void setup() {
  Serial.begin(9600);
  pinMode(GarageSensor, INPUT);
  pinMode(DoorSensor, INPUT);

  // for (int i = 2; i < 14; i++) {
  //   pinMode(i, INPUT_PULLUP);
  // }
  pinMode(2, INPUT_PULLUP); //turns lights on
  pinMode(3, INPUT_PULLUP); // turns lights to auto

  pinMode(4, INPUT_PULLUP); // sets pattern1 variable to 1
  pinMode(5, INPUT_PULLUP); // sets pattern1 variable to 2
  pinMode(6, INPUT_PULLUP); // sets pattern2 varibale to 3
  pinMode(7, INPUT_PULLUP); // sets pattern2 variable to 4

  pinMode(8, INPUT_PULLUP); // adjusts red value up
  pinMode(9, INPUT_PULLUP); // adjust red value down

  pinMode(10, INPUT_PULLUP); // adjusts green value up
  pinMode(11, INPUT_PULLUP); // adjusts green value down
  pinMode(12, INPUT_PULLUP); // adjusts blue value up
  pinMode(13, INPUT_PULLUP); // adjusts blue value down
  FastLED.addLeds<WS2812B, DATA_PIN, GRB>(garage, NUM_LEDS); // LED strip 1 info
  FastLED.addLeds<WS2812B, DATA_PIN2, GRB>(house, NUM_LEDS2); //LED Strip 2info
  FastLED.addLeds<WS2812B, DATA_PIN3, GRB>(handrail, NUM_LEDS3); // LED Strip 3 info
  FastLED.addLeds<WS2812B, DATA_PIN4, GRB>(leds4, NUM_LEDS4); //LED strip 4 info
  // test(); while (1); // comment this line out to run the full sketch
  // test2(); while (1); // comment this line out to run the full sketch
}

void test2() {
  CRGB  strandPallette[] = {
    CRGB(255, 0, 0),
    CRGB(0, 255, 0),
    CRGB(255, 0, 255),
    CRGB(255, 255, 0)
  };
  int NPallette = sizeof(strandPallette) / sizeof(strandPallette[0]);

  for (int strand = 0; strand < FastLED.count(); ++strand) {
    int palletteIndex = strand % NPallette;
    for (int ii = 0; ii < FastLED[strand].size(); ++ii) {
      FastLED[strand].leds()[ii] = strandPallette[palletteIndex];
      // FastLED[strand].leds()[ii] = ii & 0b1 ? strandPallette[strand%NPallette]: strandPallette[(strand+1)%NPallette] ;
      // FastLED[strand].leds()[ii] = ii & 0b1 ? strandPallette[(strand + ii) % NPallette] : strandPallette[(strand + ii + 1) % NPallette] ;
    }
    FastLED.show();
    delay(1000);
  }
}

void test() {
  for (int i = 0; i < NUM_LEDS; i++) {
    garage[i] = CRGB(255, 0, 0);
  }
  for (int i = 0; i < NUM_LEDS2; i++) {
    house[i] = CRGB(0, 255, 0);
  }
  for (int i = 0; i < NUM_LEDS3; i++) {
    handrail[i] = CRGB(255, 0, 255);
  }
  for (int i = 0; i < NUM_LEDS4; i++) {
    leds4[i] = CRGB(255, 255, 0);
  }
  FastLED.show();
}

void loop()
{
  //-----------SETS "Lights" variable----------------------------
  if (digitalRead (2) == LOW) {
    Lights = 1;
  }
  if (digitalRead (3) == LOW) {
    Lights = 2;
  }
  if (digitalRead (2) == HIGH && digitalRead (3) == HIGH) {
    Lights = 0;
  }
  //---------------------displays the lights, pattern, and color variables on the serial monitor when there is a change in thier value-----------------------

  reportStatus();
  adjustColors();

  //----------------------------------------------------------------------------------------------------------------------------------------------------
  //*****************************************************************

  int patternInput = (digitalRead(4) == LOW) << 0
                     | (digitalRead(5) == LOW) << 1
                     | (digitalRead(6) == LOW) << 2
                     | (digitalRead(7) == LOW) << 3
                     | (digitalRead(8) == LOW) << 4
                     | (digitalRead(9) == LOW) << 5 ;
  static int lastPatternInput = -1;
  if (patternInput != lastPatternInput) {
    lastPatternInput = patternInput;

    switch (patternInput) {
      case 0b10000:
      case 0b00000: Pattern = 0; break; // garage

      case 0b00001: Pattern = 1; break; // RGB solid
      case 0b10001: Pattern = 2; break; // RGB twinkle

      case 0b00010: Pattern = 3; break; // Christmas
      case 0b10010: Pattern = 4; break; // Christmas twinkle


      case 0b00100: Pattern = 5; break; // Halloween
      case 0b10100: Pattern = 6; break; // Halloween twinkle

      case 0b00101: Pattern = 7; break; // Valentines
      case 0b10101: Pattern = 8; break; // Valentines twinkle

      case 0b00110: Pattern = 9; break; // St Patricks
      case 0b10110: Pattern = 10; break; // St Patricks twinkle


      case 0b01000: Pattern = 11; break; // Water
      case 0b11000: Pattern = 12; break; // Water twinkle

      case 0b01001: Pattern = 13; break; // Fire
      case 0b11001: Pattern = 14; break; // Fire twinkle

      case 0b01010: Pattern = 15; break; // Gold and Silver
      case 0b11010: Pattern = 16; break; // Gold and Silver twinkle

      // cant reach these cases with a 2x SPTT:  Need to use the Other effect
      case 0b100000: Pattern = 17; break; // Purple and Blue
      case 0b100001: Pattern = 18; break; // Gold and Silver twinkle

      default:
        Serial.print("Unknown switches:0b");
        Serial.print(patternInput, BIN); Serial.print(" ");
    }
    Serial.print("Switches:0b"); Serial.print(patternInput, BIN); Serial.print(" Pattern:"); Serial.print(Pattern); Serial.print(' ');
  }

  //*****************************************************************************************************************


  //------------------TURN THE GARAGE and HANDRAIL LIGHTS ON SOLID to white ----------------------
  if (Lights == 1 && Pattern == 0)
  {
    strandAlternate(GARAGE, CRGB::CRGB(255, 255, 255), CRGB::CRGB(255, 255, 255));
    strandAlternate(HANDRAIL, CRGB::CRGB(255, 255, 255), CRGB::CRGB(255, 255, 255));
    strandAlternate(HOUSE, CRGB::CRGB(0, 0, 0), CRGB::CRGB(0, 0, 0));
    strandAlternate(EXTRA, CRGB::CRGB(0, 0, 0), CRGB::CRGB(0, 0, 0));

  }

  //------------------MANUAL TURN ALL LIGHTS ON SOLID to user slected color----------------------
  else if (Lights == 1 && Pattern == 1)
  {
    for ( int ii = 0; ii < 4 ; ++ii) {
      strandFillSolid(ii, CRGB::CRGB(RedV, GreenV, BlueV));
    }
  }
  //------------------AUTO TURN ALL LIGHTS ON SOLID to user slected color----------------------
  else if (Lights == 2 && Pattern == 1 && pirValueG == 0 && pirValueD == 0)
  {
    fill_solid( &(garage[0]), NUM_LEDS, CRGB(RedV, GreenV, BlueV));
    fill_solid( &(house[0]), NUM_LEDS2, CRGB(RedV, GreenV, BlueV));
    fill_solid( &(handrail[0]), NUM_LEDS3, CRGB(RedV, GreenV, BlueV));
    fill_solid( &(leds4[0]), NUM_LEDS4, CRGB(RedV, GreenV, BlueV));
  }
  //---------------TURN ALL THE LIGHTS ON to TWINKLE using user slected color-------------------------
  else if (Lights == 1 && Pattern == 2)
  {
    int a = random(NUM_LEDS);
    if (a < NUM_LEDS) garage[a] = CRGB(RedV, GreenV, BlueV);
    for (int b = 0; b < NUM_LEDS; b++) garage[b].fadeToBlackBy(4);

    int c = random(NUM_LEDS2);
    if (c < NUM_LEDS2) house[c] = CRGB(RedV, GreenV, BlueV);
    for (int d = 0; d < NUM_LEDS2; d++) house[d].fadeToBlackBy(4);

    int e = random(NUM_LEDS3);
    if (e < NUM_LEDS3) handrail[e] = CRGB(RedV, GreenV, BlueV);
    for (int f = 0; f < NUM_LEDS3; f++) handrail[f].fadeToBlackBy(4);

    int g = random(NUM_LEDS4);
    if (g < NUM_LEDS4) leds4[g] = CRGB(RedV, GreenV, BlueV);
    for (int h = 0; h < NUM_LEDS4; h++) leds4[h].fadeToBlackBy(4);


    FastLED.show();
    FastLED.delay(200);               // FastLED delay
  }
  //***************************************************************



  //---------------TURN ALL THE LIGHTS ON SOLID to CHRISTMAS PATTERN (alternating red and green)-------------------------
  else if (Lights == 1 && Pattern == 3)
  {
    for (int strand = 0; strand < NumStrands; ++strand) {
      strandAlternate(strand, CRGB(0, 128, 0), CRGB(200, 0, 0));
    }
  }
  //***************************************************************

  //---------------TURN ALL THE LIGHTS ON to CHRISTMAS (red and green) TWINKLE-------------------------
  else if (Lights == 1 && Pattern == 4)
  {
    for (int strand = 0; strand < NumStrands; ++strand) {
      strandTwinkleNB(strand, CRGB(0, 128, 0), CRGB(200, 0, 0));
    }
  }
  //***************************************************************
  //---------------TURN ALL THE LIGHTS ON SOLID to HALLOWEEN (Purple and Orange)-------------------------
  else if (Lights == 1 && Pattern == 5)
  {
    for (int strand = 0; strand < NumStrands; ++strand) {
      strandAlternate(strand, CRGB(250, 0, 200), CRGB(200, 50, 0));
    }
  }
  //***************************************************************

  //---------------TURN ALL THE LIGHTS ON to MANUAL HALLOWEEN (purple, and orange) TWINKLE-------------------------
  else if (Lights == 1 && Pattern == 6)
  {
    for (int strand = 0; strand < NumStrands; ++strand) {
      strandTwinkleNB(strand, CRGB(250, 0, 200), CRGB(200, 50, 0));
    }
  }
  //***************************************************************

  //---------------TURN ALL THE LIGHTS ON SOLID to Valentines PATTERN -------------------------
  else if (Lights == 1 && Pattern == 7)
  {
    for (int strand = 0; strand < NumStrands; ++strand) {
      strandAlternate(strand, CRGB(247, 80, 80), CRGB(200, 0, 0));
    }
  }
  //***************************************************************
  //---------------TURN ALL THE LIGHTS ON to Valentines (red and pink) TWINKLE-------------------------
  else if (Lights == 1 && Pattern == 8)
  {
    for (int strand = 0; strand < NumStrands; ++strand) {
      strandTwinkleNB(strand, CRGB(247, 80, 80), CRGB(200, 0, 0));
    }
  }
  //***************************************************************
  //---------------TURN ALL THE LIGHTS ON SOLID to St Patricks PATTERN -------------------------
  else if (Lights == 1 && Pattern == 9)
  {
    for (int strand = 0; strand < NumStrands; ++strand) {
      strandAlternate(strand, CRGB(10, 255, 2), CRGB(100, 255, 100));
    }
  }
  //***************************************************************
  //---------------TURN ALL THE LIGHTS ON to St Patricks (Light green and green) TWINKLE-------------------------
  else if (Lights == 1 && Pattern == 10)
  {
    for (int strand = 0; strand < NumStrands; ++strand) {
      strandTwinkleNB(strand, CRGB(10, 166, 2), CRGB(100, 255, 100));
    }
  }
  //***************************************************************
  //---------------TURN ALL THE LIGHTS ON SOLID to WATER PATTERN (alternating Blues)-------------------------
  else if (Lights == 1 && Pattern == 11)
  {
    for (int strand = 0; strand < NumStrands; ++strand) {
      strandAlternate(strand, CRGB(15, 25, 255), CRGB(91, 240, 220));
    }
  }
  //***************************************************************
  //---------------TURN ALL THE LIGHTS ON to Water (Blues) TWINKLE-------------------------
  else if (Lights == 1 && Pattern == 12)
  {
    for (int strand = 0; strand < NumStrands; ++strand) {
      strandTwinkleNB(strand, CRGB(15, 25, 255), CRGB(91, 240, 220));
    }
  }
  //***************************************************************
  //---------------TURN ALL THE LIGHTS ON SOLID to Red and Orange-------------------------
  else if (Lights == 1 && Pattern == 13)
  {
    for (int strand = 0; strand < NumStrands; ++strand) {
      strandAlternate(strand, CRGB(250, 0, 0), CRGB(250, 50, 0));
    }
  }
  //***************************************************************
  //---------------TURN ALL THE LIGHTS ON to Red and Orange TWINKLE-------------------------
  else if (Lights == 1 && Pattern == 14)
  {
    for (int strand = 0; strand < NumStrands; ++strand) {
      strandTwinkleNB(strand, CRGB(250, 0, 0), CRGB(250, 50, 0));
    }
  }
  //***************************************************************
  //---------------TURN ALL THE LIGHTS ON SOLID to Silver and Gold-------------------------
  else if (Lights == 1 && Pattern == 15)
  {
    for (int strand = 0; strand < NumStrands; ++strand) {
      strandAlternate(strand, CRGB(250, 250, 250), CRGB(250, 107, 16));
    }
  }
  //***************************************************************
  //---------------TURN ALL THE LIGHTS ON to Silver and Gold TWINKLE-------------------------
  else if (Lights == 1 && Pattern == 16)
  {
    for (int strand = 0; strand < NumStrands; ++strand) {
      strandTwinkleNB(strand, CRGB(250, 250, 250), CRGB(250, 107, 16));
    }
  }
  //***************************************************************


  //---------------TURN ALL THE LIGHTS ON SOLID to Purple and Blue-------------------------
  else if (Lights == 1 && Pattern == 17)
  {
    strandAlternate(GARAGE, CRGB::CRGB(150, 8, 102), CRGB::CRGB(0, 56, 134));
    strandAlternate(HOUSE, CRGB::CRGB(150, 8, 102), CRGB::CRGB(0, 56, 134));
    strandAlternate(HANDRAIL, CRGB::CRGB(150, 8, 102), CRGB::CRGB(0, 56, 134));
    strandAlternate(EXTRA, CRGB::CRGB(150, 8, 102), CRGB::CRGB(0, 56, 134));
  }
  //***************************************************************
  //---------------TURN ALL THE LIGHTS ON to Purple and Blue TWINKLE-------------------------
  else if (Lights == 1 && Pattern == 18)
  {
    for (int strand = 0; strand < NumStrands; ++strand) {
      strandTwinkleNB(strand, CRGB(150, 8, 102), CRGB(0, 56, 134));
    }
  }
  //***************************************************************


  //---------------TURN THE LIGHTS OFF-------------------------
  else if (Lights == 0)
  {
    for (int ii = 0; ii < FastLED.count(); ++ii) {
      strandFillSolid(ii, CRGB::CRGB(0, 0, 0));
    }
  }
  //***************************************************************

  //---------PUTS THE LIGHTS IN AUTO MODE------------------------
  // This will turn the lights on in front of the garage when motion is detected with the motion detector(s), and then fade the lights out after a preset time has passed
  else if (Lights == 2)
  {
    pirValueG = digitalRead(GarageSensor);
    pirValueD = digitalRead(DoorSensor);

    if (pirValueG == 1 || pirValueD == 1)
    {
      strandFillSolid(GARAGE, CRGB::CRGB(255, 255, 255));
      strandFillSolid(HANDRAIL, CRGB::CRGB(255, 255, 255));
      Serial.println("Motion Detected");
    }

    else if (pirValueG == 0 && pirValueG == 0)
    {
      strandFadeToBlackBy(2, 30);
      strandFadeToBlackBy(GARAGE, 30 ); // 8 bit, low number = slow fade, high number = fast fade
      //fadeToBlackBy(handrail, NUM_LEDS, 30 ); // 8 bit,     5     = slow fade,     255     = fast fade
    }

  }
  FastLED.show(); // always show;
  FastLED.delay(200);               // always delay
  Serial.print('.');

}

void strandFadeToBlackBy(int strand, int val) {
  fadeToBlackBy(FastLED[strand].leds(), FastLED[strand].size(), val);
}

void strandFillSolid(int strand, CRGB color) {
  fill_solid((FastLED[strand]).leds(), FastLED[strand].size(), color);
}

void strandAlternate(int strand, CRGB color1, CRGB color2) {
  const int NumLeds = FastLED[strand].size();
  CRGB * myStrand = FastLED[strand].leds();
  for (int idx = 0; idx < NumLeds; ++idx) {
    myStrand[idx] = idx & 0b1 ? color1 : color2 ;
  }
}

void strandTwinkleNB(int strand, CRGB color1, CRGB color2) {
  const int NumLeds = FastLED[strand].size();
  CRGB * myStrand = FastLED[strand].leds();
  fadeToBlackBy(myStrand, NumLeds, 4);
  int idx  = random(NumLeds);
  myStrand[idx] = idx & 0b1 ? color1 : color2 ;
}


void adjustColors(void) {
  const uint32_t interval = 200;
  static uint32_t last = 0;
  uint32_t now = millis();
  if (now - last > interval) {
    last = now;
    const int REDPIN = 12;
    const int BLUEPIN = 13;
    const int UPPIN = 10;
    const int DOWNPIN = 11;
    // 0 = green, 1=blue, 2=red
    int colorCode = (digitalRead(13) == LOW) << 0 | (digitalRead(12) == LOW) << 1;
    //Serial.print(" 0b"); Serial.println(colorCode, BIN);

    if (digitalRead(UPPIN) == LOW) {
      switch (colorCode) {
        case 0: if (GreenV < 255)++GreenV; break;
        case 1: if (BlueV < 255)++BlueV; break;
        case 2: if (RedV < 255)++RedV; break;
        default:
          Serial.print("Bad up adjustment for code ");
          Serial.println(colorCode);
      }
      //   Serial.println(colorCode, BIN);

    }
    if (digitalRead(DOWNPIN) == LOW) {
      switch (colorCode) {
        case 0: if (GreenV > 0)--GreenV; break;
        case 1: if (BlueV > 0)--BlueV; break;
        case 2: if (RedV > 0)--RedV; break;
        default:
          Serial.print("Bad up adjustment for code ");
          Serial.println(colorCode);
      }
    }
  }
}

void reportStatus(void) {
  static uint32_t last = 0;
  if (millis() - last < 200) return;
  last = millis();
  if  (Lights != Old_Lights) {
    Serial.print("Lights=");
    Serial.println(Lights, DEC);
    Old_Lights = Lights;
  }
  if  (Pattern != Old_Pattern) {
    Serial.print("Pattern=");
    Serial.println(Pattern, DEC);
    Old_Pattern = Pattern;
  }
  if (RedV != Old_RedV || GreenV != Old_GreenV || BlueV != Old_BlueV) {

    Serial.print("RGB=[");
    Serial.print(RedV, DEC);
    Serial.print(",");
    Serial.print(GreenV, DEC);
    Serial.print(",");
    Serial.print(BlueV, DEC);
    Serial.println("]");
    Old_RedV = RedV;
    Old_GreenV = GreenV;
    Old_BlueV = BlueV;
  }
}
ON
AUTO
P1V1
P1V2
P2V3
P2V4
TWINK
OTHER
UP
DN
RED
BLU
EFF
OFF
ADJ
N_ADJ
RED/BL
GRN
GARAGE PIR
ON
OFF
ON
OFF
ON
OFF
DOOR PIR
OFF
OFF