// not the buzzer, I removed it and yet.

/* nutshell ezButton

# include <ezButton.h>

ezButton button[2] = {3, 2};

  button[1].setDebounceTime(21);  

  button[1].loop();
  if (button[1].isPressed()) {
*/

/* nutshell jButton ARGH!

# include "jbutton.h"

jButton kButton[SIZE] = {2, 3, 4, 5};  // sorry mom.

    kButton[ii].begin();

    kButton[ii].loop();
    if (kButton[ii].isPress()) {

*/

# include "jbutton.h"

# define SIZE 4

# define k100 100
byte theSequence[k100];
byte currentLength = 0;
byte randomIndex;

unsigned long seeds[] = {
  1,
  374989, 378023, 381167, 384247, 387307, 390419, 393311, 396443,
  424549, 427781, 430847, 433729, 436739, 440023, 442973, 445943,
  474433, 477523, 480541, 483709, 487057, 490097, 493243, 496283,
};

const byte nGames = sizeof seeds / sizeof *seeds;
byte gameNumber;

byte userPress = 0;

unsigned long now;    // only one call to millis()
const int RATE = 277;

const byte lamps[] = {7, 8, 9, 10};
// no longer needed
//const byte buttons[] = {2, 3, 4, 5};

# define BEEP 6
bool noisy = true;

const int notes[SIZE] = {300, 500, 700, 900};

jButton kButton[SIZE] = {2, 3, 4, 5};  // sorry mom.

void setup()
{
  for (byte ii = 0; ii < SIZE; ii++) {
    kButton[ii].begin();  // ezButton cheats and has no begin method
    pinMode(lamps[ii], OUTPUT);

// jButton are already always timed, but    ezButton myEZButton[ii].setDebounceTime(20);
  }

  pinMode(BEEP, OUTPUT);

  Serial.begin(115200);
  Serial.println("\nSETUP, 'K?\n");

// this is deterministic with prejudice
}

void makeSequence()
{
  static bool fixed = true;
  static bool has;

  if (!has || !fixed) {
    randomSeed(seeds[gameNumber]);
    for (byte ii = 0; ii < k100; ii++) // theSequence[ii] = (ii & 2) ? 0 : 3;
      theSequence[ii] = random(0, SIZE);

    gameNumber++; if (gameNumber > nGames) gameNumber = 0;
  }
  has = true;
}

void heartBeat(const uint8_t theLED, uint32_t onTime = 333, uint32_t  offTime = 444)
{
  static bool setUp = false;
  static bool heart;
  static unsigned long lastTime;

  if (now - lastTime < (heart ? onTime : offTime)) return;

  heart = !heart;
  lastTime = now;
  if (setUp == false) pinMode (theLED, OUTPUT), setUp = true;

  digitalWrite(theLED, heart ? HIGH : LOW);
}

enum lampStates {IDLE, START, ON, OFF, NONE};
const char *lTags[] = {"Idle", "Start", "On", "Off", "NONE/WTF"};

bool lampFSM()
{
  return lampFSM(-1);
}

// returns true if busy
bool lampFSM(int theLamp)
{
  static unsigned long lastTime;
  static int lastCode = -1;
  static byte lampState;

  static byte theCode;
  static unsigned long dwell;

  static byte printedState = NONE;
  if (printedState != lampState) {
    Serial.print("             lamp : ");
    Serial.println(lTags[lampState]);

    printedState = lampState;
  }

  if (theLamp >= 0) {
    lampState = START;
    dwell = 0;
    theCode = theLamp;
  }

// unsigned long now = millis();

  if (now - lastTime < dwell) return lampState != IDLE;
  lastTime = now;

  switch (lampState) {
  case IDLE :
    break;

  case START :
    if (lastCode >= 0) {
      digitalWrite(lamps[lastCode], LOW);
      if (noisy) noTone(BEEP);
    }

    digitalWrite(lamps[theCode], HIGH);
    if (noisy) tone(BEEP, notes[theCode]);
    dwell = RATE;
    lampState = ON;
    lastCode = theCode;

    break;

  case ON :
    digitalWrite(lamps[theCode], LOW);
    if (noisy) noTone(BEEP);
    dwell = RATE;
    lampState = OFF;

    break;

  case OFF :
    lastCode = 0;
    dwell = 0;
    lampState = IDLE;

    break;
  }

  return lampState != IDLE;
}

int userInput()
{
  int theButton = -1;

  for (byte ii = 0; ii < SIZE; ii++) {
    kButton[ii].loop();

    if (kButton[ii].isPress())
      theButton = ii;
  }

  return theButton;   // -1 or button 0 .. SIZE - 1
}



void loop() {
  now = millis();

  heartBeat(A2); // after @paulpaulson
  bool busyQ = lampFSM();

  static bool lastLamping;
  if (0)  // you know what I mean.
  if (lastLamping != busyQ) {
    lastLamping = !lastLamping;
    Serial.println(lastLamping ? "      BUSY" : "      IDLE");
  }

  simonFSM();
}

// FSM for game control
enum states {SHOW = 0, SHOWING, TEST, TESTING, WON, LOST, AGAIN, NOSTATE};
const char *tags[] ={"Show", "Showing", "Test", "Testing", "Won!", "Lost.", "Again", "hi Mom!"};

void simonFSM()
{
  static unsigned long lastTime;
  static unsigned long dwell;
  static byte index;
  static byte simonState = AGAIN;

  static byte printedState = NOSTATE;
  if (printedState != simonState) {
    Serial.print("state is ");
    Serial.println(tags[simonState]);

    printedState = simonState;
  }

  if (now - lastTime < dwell) return;
  lastTime = now;
  dwell = 0;

  int theInput;

  switch (simonState) {
  case SHOW :
    index = 0;
    Serial.println("first");
    simonState = SHOWING;

    // break; // not.

  case SHOWING :
    if (!lampFSM()) {     // extra service just to see if it is busy yet
      lampFSM(theSequence[index]);
      Serial.print(now); Serial.print(" playing ");
      Serial.println(theSequence[index]);
      index++;
    }

    if (index >= currentLength) {
      simonState = TEST;
    }
  
    break;

  case TEST :
    index = 0;
    simonState = TESTING;

    // break;  // not.

  case TESTING :
    theInput = userInput();
    if (theInput == -1) break;
    lampFSM(theInput);  // right?

    if (theInput == theSequence[index])
      index++;
    else
      simonState = LOST;

    if (index == currentLength) simonState = WON;

    break;

  case WON :
    Serial.print("you did it! ");

// series advance logic here.
// now there is no global WIN of anything,
// just sequences out to 100 steps boom!

    currentLength++;
    simonState = SHOW;
    dwell = 1777;
    
    break;

  case LOST :
    Serial.print("you lose. ");
    simonState = AGAIN;   // hmmm this is odd
  case AGAIN :
    currentLength = 1;
    makeSequence();

    simonState = SHOW;
    dwell = 2000;

    break;
    
  default :
    Serial.println(".   ambitious   .");
    Serial.flush();
    for (; ; );
  }
}
uno:A5.2
uno:A4.2
uno:AREF
uno:GND.1
uno:13
uno:12
uno:11
uno:10
uno:9
uno:8
uno:7
uno:6
uno:5
uno:4
uno:3
uno:2
uno:1
uno:0
uno:IOREF
uno:RESET
uno:3.3V
uno:5V
uno:GND.2
uno:GND.3
uno:VIN
uno:A0
uno:A1
uno:A2
uno:A3
uno:A4
uno:A5
led1:A
led1:C
led2:A
led2:C
led3:A
led3:C
led4:A
led4:C
btn1:1.l
btn1:2.l
btn1:1.r
btn1:2.r
btn2:1.l
btn2:2.l
btn2:1.r
btn2:2.r
btn3:1.l
btn3:2.l
btn3:1.r
btn3:2.r
btn4:1.l
btn4:2.l
btn4:1.r
btn4:2.r
bz1:1
bz1:2
led5:A
led5:C
sw1:1
sw1:2
sw1:3