/* xawwnee ;) */
static int fxSpeed = 100;
static int fxDelay = 200;
unsigned int fxRepeat = 1;
bool bootEffect = true;
int xPin[16] = {
  2,
  3,
  4,
  5,
  12,
  13,
  14,
  15,
  16,
  17,
  18,
  19,
  21,
  22,
  23,
  25
};
const int pinCounts = sizeof(xPin) / sizeof(int);
unsigned int stack = 0;
unsigned int chaser = pinCounts - 1;
// DEFINE EFFECTS //
void fx_2();
void fx_3();
void fx_4();
void fx_5();
void fx_6();
void fx_7();
void fx_8();
void fx_9();
void fx_10();
void fx_11();
void fx_12();
void fx_13();
void fx_14();
void fx_15();
void fx_16();
void fx_17();
void fx_18();
void fx_19();
void fx_20();
void fx_21();
void fx_22();
void fx_23();
void( * randomEffects[])() = {
  fx_2,
  fx_3,
  fx_4,
  fx_5,
  fx_6,
  fx_7,
  fx_8,
  fx_9,
  fx_10,
  fx_11,
  fx_12,
  fx_13,
  fx_14,
  fx_15,
  fx_16,
  fx_17,
  fx_18,
  fx_19,
  fx_20,
  fx_21,
  fx_22,
  fx_23,
};
void setup() {
  // pinMode(34, INPUT); // Grab fxSpeed from Potentiometer
  for (int op = 0; op < pinCounts; op++) {
    pinMode(xPin[op], OUTPUT);
    // delay(10);
  }
  Serial.begin(115200);
}
void loop() {
  // fxSpeed = map(analogRead(34), 0, 1023, 0, 255); // Map fxSpeed from Potentiometer
  // Start of test debug

  for (int j = 0; j < pinCounts; j++) {
    digitalWrite(xPin[j], HIGH);
    delay(fxSpeed);
    j = j + 1;
  }
  for (int j = 0; j < pinCounts; j++) {  
    digitalWrite(xPin[j], LOW);
    delay(fxSpeed);  
    j = j + 1;
  }


  // End of test debug
  /*
  if (bootEffect == true)
  {
  fx_1();
  } 
  else
    {
    // Random Effects
    int i = random(0, sizeof(randomEffects) / sizeof(randomEffects[0]));
    if (randomEffects[i] != NULL)
    {
      for (int r = 0; r < fxRepeat; r++) {
        randomEffects[i]();
      }
      off();
      delay(fxDelay);
    }
      }
      */
}
// EFFECTS //
void fx_1() {
  Serial.println(__func__);  
  /* BOOT STANDBY AND FAVOURITE EFFECTS */
  // Standby Effect
      on();
      delay(1000);
      off();
      delay(fxDelay);
  for (int r = 0; r < fxRepeat; r++) {
  // Favourite Effects
      fx_10();
      fx_10();
      delay(fxDelay);
      fx_13();
      fx_14();
      delay(fxDelay);
      fx_17();
      delay(fxDelay);
      fx_20();
      fx_21();
      delay(fxDelay);
      fx_2();
      fx_3();
      delay(fxDelay);
      fx_18();
      fx_19();
      delay(fxDelay);
      fx_22();
      fx_22();
      fx_22();
      fx_22();
      off();
      delay(fxDelay);
  }
  bootEffect = false;
}
void fx_2() {
  Serial.println(__func__);
  /* CUSTOM FX 2: L - R, FILL SWIPE */
  for (int i = 0; i < pinCounts; i++) {
    digitalWrite(xPin[i], HIGH);
    delay(fxSpeed);
  }
  for (int i = 0; i < pinCounts; i++) {
    digitalWrite(xPin[i], LOW);
    delay(fxSpeed);
  }
}
void fx_3() {
  Serial.println(__func__);
  /* CUSTOM FX 3: R - L, FILL SWIPE  */
  for (int j = chaser; j >= 0; j--) {
    digitalWrite(xPin[j], HIGH);
    delay(fxSpeed);
  }
  for (int j = chaser; j >= 0; j--) {
    digitalWrite(xPin[j], LOW);
    delay(fxSpeed);
  }
}
void fx_4() {
  Serial.println(__func__);
  /* CUSTOM FX 4: L - R, CHASING, ONE BY ONE */
  for (int i = 0; i < pinCounts; i++) {
    digitalWrite(xPin[i], HIGH);
    delay(fxSpeed);
    digitalWrite(xPin[i], LOW);
    delay(fxSpeed);
  }
}
void fx_5() {
  Serial.println(__func__);
  /* CUSTOM FX 5: R - L, CHASING, ONE BY ONE */
  for (int i = chaser; i >= 0; i--) {
    digitalWrite(xPin[i], HIGH);
    delay(fxSpeed);
    digitalWrite(xPin[i], LOW);
    delay(fxSpeed);
  }
}
void fx_6() {
  Serial.println(__func__);
  /* CUSTOM FX 6: L - R, FILL MARQUEE */
  for (int i = 0; i < pinCounts; i++) {
    digitalWrite(xPin[i], HIGH);
    delay(fxSpeed);
  }
  for (int j = chaser; j >= 0; j--) {
    digitalWrite(xPin[j], LOW);
    delay(fxSpeed);
  }
}
void fx_7() {
  Serial.println(__func__);
  /* CUSTOM FX 7: R - L, FILL MARQUEE */
  for (int j = chaser; j >= 0; j--) {
    digitalWrite(xPin[j], HIGH);
    delay(fxSpeed);
  }
  for (int i = 0; i < pinCounts; i++) {
    digitalWrite(xPin[i], LOW);
    delay(fxSpeed);
  }
}
void fx_8() {
  Serial.println(__func__);
  /* CUSTOM FX 8: L - R, SWIPE STOP */
  delay(fxSpeed);
  for (int i = 0; i < pinCounts; i++) {
    digitalWrite(xPin[i], HIGH);
    delay(fxSpeed);
  }
  for (int i = 0; i < pinCounts; i++) {
    digitalWrite(xPin[i], LOW);
  }
  delay(fxSpeed);
}
void fx_9() {
  Serial.println(__func__);
  /* CUSTOM FX 9: R - L, SWIPE STOP */
  delay(fxSpeed);
  for (int j = chaser; j >= 0; j--) {
    digitalWrite(xPin[j], HIGH);
    delay(fxSpeed);
  }
  for (int j = chaser; j >= 0; j--) {
    digitalWrite(xPin[j], LOW);
  }
  delay(fxSpeed);
}
void fx_10() {
  Serial.println(__func__);
  /* CUSTOM FX 10: ODD EVEN BLINK */
  for (int i = 0; i < pinCounts; i++) {
    digitalWrite(xPin[i], HIGH);
    i = i + 1;
  }
  delay(fxSpeed);
  for (int i = 0; i < pinCounts; i++) {
    digitalWrite(xPin[i], LOW);
    i = i + 1;
  }
  delay(fxSpeed);
  for (int i = 1; i < pinCounts; i++) {
    digitalWrite(xPin[i], HIGH);
    i = i + 1;
  }
  delay(fxSpeed);
  for (int i = 1; i < pinCounts; i++) {
    digitalWrite(xPin[i], LOW);
    i = i + 1;
  }
  delay(fxSpeed);
}
void fx_11() {
  Serial.println(__func__);
  /* CUSTOM FX 11: RANDOM BLINKS */
  for (int i = 0; i < pinCounts; i++) {
    digitalWrite(xPin[random(0, pinCounts)], HIGH);
    delay(random(fxSpeed));
    digitalWrite(xPin[random(0, pinCounts)], LOW);
    delay(random(fxSpeed * 2));
  }
}
void fx_12() {
  Serial.println(__func__);
  /* CUSTOM FX 12: RANDOM DOUBLE BLINKS */
  for (int i = 0; i < pinCounts; i++) {
    int r1 = random(0, pinCounts);
    digitalWrite(xPin[r1], HIGH);
    delay(fxSpeed / 2);
    digitalWrite(xPin[r1], LOW);
    delay(fxSpeed / 2);
    digitalWrite(xPin[r1], HIGH);
  }
}
void fx_13() {
  Serial.println(__func__);
  /* CUSTOM FX 13: L - R, 4 COLS MARQUEE */
  for (int i = 0; i < pinCounts; i++) {
    digitalWrite(xPin[i], HIGH);
    digitalWrite(xPin[i] - 1, HIGH);
    digitalWrite(xPin[i] + 1, HIGH);
    digitalWrite(xPin[i] + 2, HIGH);
    delay(fxSpeed);
    digitalWrite(xPin[i], LOW);
    digitalWrite(xPin[i] - 1, LOW);
    digitalWrite(xPin[i] + 1, LOW);
    digitalWrite(xPin[i] + 2, LOW);
  }
}
void fx_14() {
  Serial.println(__func__);
  /* CUSTOM FX 14: R - L, 4 COLS MARQUEE */
  for (int i = chaser; i >= 0; i--) {
    digitalWrite(xPin[i], HIGH);
    digitalWrite(xPin[i] - 1, HIGH);
    digitalWrite(xPin[i] + 1, HIGH);
    digitalWrite(xPin[i] + 2, HIGH);
    delay(fxSpeed);
    digitalWrite(xPin[i], LOW);
    digitalWrite(xPin[i] - 1, LOW);
    digitalWrite(xPin[i] + 1, LOW);
    digitalWrite(xPin[i] + 2, LOW);
  }
}
void fx_15() {
  Serial.println(__func__);
  /* CUSTOM FX 15: L - R, 4 COLS MARQUEE - BLINKING HEAD */
  for (int i = 0; i < pinCounts; i++) {
    digitalWrite(xPin[i], HIGH);
    digitalWrite(xPin[i] + 1, HIGH);
    digitalWrite(xPin[i] + 2, HIGH);
    digitalWrite(xPin[i] + 3, HIGH);
    digitalWrite(xPin[i] + 3, LOW);
    delay(fxSpeed);
    digitalWrite(xPin[i] + 3, HIGH);
    delay(fxSpeed);
    digitalWrite(xPin[i], LOW);
    delay(fxSpeed);
  }
}
void fx_16() {
  Serial.println(__func__);
  /* CUSTOM FX 16: R - L, 4 COLS MARQUEE - BLINKING HEAD */
  for (int i = chaser; i >= 0; i--) {
    digitalWrite(xPin[i], HIGH);
    digitalWrite(xPin[i] - 1, HIGH);
    digitalWrite(xPin[i] - 2, HIGH);
    digitalWrite(xPin[i] - 3, HIGH);
    digitalWrite(xPin[i] - 3, LOW);
    delay(fxSpeed);
    digitalWrite(xPin[i] - 3, HIGH);
    delay(fxSpeed);
    digitalWrite(xPin[i], LOW);
    delay(fxSpeed);
  }
}
void fx_17() {
  Serial.println(__func__);
  /* CUSTOM FX 17: GATE OPEN CLOSE STYLE*/
  for (int i = (pinCounts / 2); i >= 0; i--) {
    digitalWrite(xPin[i], HIGH);
    digitalWrite(xPin[pinCounts - 1 - i], HIGH);
    delay(fxSpeed);
  }
  on();
  for (int i = (pinCounts / 2); i >= 0; i--) {
  digitalWrite(xPin[i], LOW);
  digitalWrite(xPin[pinCounts - 1 - i], LOW);
  delay(fxSpeed);
  }
  off();
}
void fx_18() {
  Serial.println(__func__);
  /* CUSTOM FX 18: L - R, BOUNCING MARQUEE BY FIRST COLOR, ONE BY ONE */
  for (int j = 0; j < pinCounts; j++) {
    digitalWrite(xPin[j], HIGH);
    delay(fxSpeed);
    j = j + 1;
  }
}
void fx_19() {
  Serial.println(__func__);
  /* CUSTOM FX 19: L - R, BOUNCING MARQUEE BY SECOND COLOR, ONE BY ONE */
  for (int j = 1; j < pinCounts; j++) {
    digitalWrite(xPin[j], HIGH);
    delay(fxSpeed);
    j = j + 1;
  }
  for (int j = 1; j < pinCounts; j++) {
    digitalWrite(xPin[j], LOW);
    delay(fxSpeed);
    j = j + 1;
  }
    for (int j = 0; j < pinCounts; j++) {
    digitalWrite(xPin[j], LOW);
    delay(fxSpeed);
    //j = j + 1;
  }
}
void fx_20() {
  Serial.println(__func__);
  /* CUSTOM FX 20: SPLIT INSIDE, ONE BY ONE */
  for (int i = 0; i < pinCounts / 2; i++) {
    digitalWrite(xPin[i], HIGH);
    digitalWrite(xPin[pinCounts - 1 - i], HIGH);
    delay(fxSpeed);
    digitalWrite(xPin[i], LOW);
    digitalWrite(xPin[pinCounts - 1 - i], LOW);
  }
}
void fx_21() {
  Serial.println(__func__);
  /* CUSTOM FX 21: SPLIT OUTSIDE, ONE BY ONE */
  for (int i = (pinCounts / 2); i >= 0; i--) {
    //delay(fxSpeed);
    digitalWrite(xPin[i], HIGH);
    digitalWrite(xPin[pinCounts - 1 - i], HIGH);
    delay(fxSpeed);
    digitalWrite(xPin[i], LOW);
    digitalWrite(xPin[pinCounts - 1 - i], LOW);
  }
}
void fx_22() {
  Serial.println(__func__);
  /* CUSTOM FX 22: L - R, POLICE LIGHT EFFECT */
    for (int i = 0; i < pinCounts/2; i++) {
    digitalWrite(xPin[i], HIGH);
      }
    for (int i = pinCounts/2; i < pinCounts; i++) {
      digitalWrite(xPin[i], LOW);
      }
    delay(fxSpeed * 2);
    for (int i = 0; i < pinCounts/2; i++) {
    digitalWrite(xPin[i], LOW);
      }
    for (int i = pinCounts/2; i < pinCounts; i++) {
      digitalWrite(xPin[i], HIGH);
      }
    delay(fxSpeed * 2);
}
void fx_23(void) {
  Serial.println(__func__);
  /* CUSTOM FX 23: L - R, STACK EFFECT */
  // Go through the LEDs once per loop and Turn on the chaser LED, turn off all LEDs not already "stacked"
    for (int unsigned i=stack; i < pinCounts; i++) {
    if (i == chaser) {
      digitalWrite(xPin[i], HIGH);
    }
    else if (i != stack) {
      digitalWrite(xPin[i], LOW);
    }
  }
  // the delay
  delay(fxSpeed);
  // Move the chaser LED down the line
  chaser--;
  // Reset the chaser when hitting bottom
  if (chaser >= pinCounts) {
    chaser = pinCounts-1;
    // Increase the stack
    stack++;
    // Reset the stack when reaching top
    stack %= pinCounts;
  }
  if (stack < pinCounts) {
      fx_23();
    }

    exit(0);

}
void on(void) {
  /* Turn on all channels */
  for (int i = 0; i < pinCounts; i++) {
    digitalWrite(xPin[i], HIGH);
  }
  delay(fxSpeed);
}
void off(void) {
  /* Turn off all channels */
  for (int i = 0; i < pinCounts; i++) {
    digitalWrite(xPin[i], LOW);
  }
  delay(fxSpeed);
}