#include <SPI.h>
// https://programmersqtcpp.blogspot.com/

SPISettings spisettings;
const byte PIN_SS       = 4;
const byte PIN_START_A  = 7;
const byte PIN_START_B  = 6;

enum State {
  RE_START, SEMAFORO, MONITORING, END_RESET
};
byte      g_state;

uint16_t  g_rndNumber; // range 1000÷3000 ms
bool      g_btnAState;
bool      g_btnBState;

struct timer_t {
  uint32_t oneSec;
  uint16_t oneSecInt;
  uint32_t t0;    // tempo di riferimento
  uint32_t t1;    // tempo giocatore A
  uint32_t t2;    // tempo giocatore B
} timer;  // istanza di tipo timer_t

struct Semaforo_t {
  byte cnt = 8;
  byte data;
} semaforo;  // istanza di tipo Semaforo_t


byte g_testResult;
bool g_wasPushedA;
bool g_wasPushedB;

// define RANDOM_RANGE
#define RANDOM_RANGE (1000, 3001)
//   intervallo    min-^  max-^


void setup() {

  Serial.begin(115200);
  pinMode(PIN_START_A, INPUT_PULLUP);
  pinMode(PIN_START_B, INPUT_PULLUP);
  SPI.begin();

  // PIN_SS è lo slave select o chip select
  pinMode(PIN_SS, OUTPUT);
  digitalWrite(PIN_SS, HIGH);

  writeShiftReg(0);
}

// writeShiftReg usa la SPI hardware
// la chiamata writeShiftReg(byte data) consuma 148us
void writeShiftReg(byte data) {
  SPI.beginTransaction(spisettings);
  digitalWrite(PIN_SS, LOW);
  SPI.transfer(data);  // spi hardware

  digitalWrite(PIN_SS, HIGH);
  SPI.endTransaction();
}

byte semaforoRun() {
  if ((millis() - timer.oneSec) >= timer.oneSecInt) {
    timer.oneSec = millis();
    timer.oneSecInt = 1000;

    semaforo.data |= (1 << (semaforo.cnt - 1))
                     | (1 << (semaforo.cnt - 2)); // end command


    writeShiftReg(semaforo.data);

    semaforo.cnt -= 2;

    if (semaforo.cnt == 0) {
      semaforo.cnt = 8;
      // *** GHANGE STATE
      g_state = MONITORING;
      timer.t0 = millis();

      return g_state;
    }
  }
  return g_state;
}

void initRandom() {
  static bool lstate;
  if (!lstate) {
    Serial.println("Premi A o B per iniziare il test.");

    lstate = true;
  } else {
    if (digitalRead(PIN_START_A) == LOW
        || digitalRead(PIN_START_B) == LOW) {
      // carica il timer oneSec
      timer.oneSec = millis();
      // azzera intervallo
      timer.oneSecInt = 0;
      // Inizializza il generatore rnd
      randomSeed(micros());
      // genera un numero nel range 1000÷3000
      g_rndNumber = random(RANDOM_RANGE);
    }
  }
}

void printResult() {
    if (g_testResult == 1) {
        Serial.println("Tempo di reazione: ");

        if (timer.t1) {
            uint32_t trA =  timer.t1 - timer.t0;
            Serial.print("Giocatore A: ");
            Serial.print(trA);
            Serial.print(" ms");
            if ((timer.t1 < timer.t2) || (timer.t2 == 0))
                Serial.println(" Vince.");
            else
                Serial.println(" Perde.");
      } else {
            Serial.println("Giocatore A non ha premuto.");
      }
      if (timer.t2) {
          uint32_t trB =  timer.t2 - timer.t0;
          Serial.print("Giocatore B: ");
          Serial.print(trB);
          Serial.print(" ms");
          if ((timer.t2 < timer.t1) || (timer.t1 == 0))
              Serial.println(" Vince.");
          else
              Serial.println(" Perde.");
      } else {
          Serial.println("Giocatore B non ha premuto.");
      }

      //Serial.println("ms");
    } else if (g_testResult == 2) {
        writeShiftReg(0);
        Serial.println("Falsa partenza");
        uint32_t totalTime = timer.t0 + g_rndNumber;
        if (timer.t1) {
            Serial.print("Il giocatore A, ha premuto: ");
            Serial.print(totalTime - timer.t1);
            Serial.println(" ms in anticipo.");
        }
        if (timer.t2) {
            Serial.print("Il giocatore B, ha premuto: ");
            Serial.print(totalTime - timer.t2);
            Serial.println(" ms in anticipo.");
        }
    }
}

#define ENCODER_BUTTON PIN_START_A
bool encBtnLastState;
bool encBtnState;
bool encoderBtnWasPressed() {
    encBtnLastState = encBtnState;
    encBtnState = digitalRead(ENCODER_BUTTON);
    
    if (!encBtnState && encBtnState != encBtnLastState) {
        
        return true;
    } 
    return false;
}

void loop() {
  while (1) {
    if (encoderBtnWasPressed()) {
        Serial.println("was pressed.");
        
    }
  }
  switch (g_state) {
    case RE_START:

      initRandom();
      if (g_rndNumber) {
        // *** CHAGE STATE ***
        g_state = SEMAFORO;
        Serial.println("Premi il pulsante allo \nspegnimento del semaforo.\n");
      }
      break;

    case SEMAFORO:
      // Quando tutte le luci sono accese
      // g_state assume il valore MONITORING
      g_state = semaforoRun();
      break;

    case MONITORING:
      g_btnAState = digitalRead(PIN_START_A);
      g_btnBState = digitalRead(PIN_START_B);

      if (semaforo.data != 0) {
        if ((millis() - timer.t0) >= g_rndNumber) {
          // spegne tutt i led
          semaforo.data = 0;
          writeShiftReg(0);
          // salva millis()
          timer.t0 = millis();
        } else {
          // Può sembrare misterioso 
          g_testResult = (g_wasPushedA | g_wasPushedB) << 1;
          if (g_testResult > 1) {
            g_state = END_RESET;
            break;
          }
        }
      } else {  // [else 2]
        bool toEnd = (millis() - timer.t0 > 2000) | g_wasPushedA & g_wasPushedB;
        //            tempo di esposizione ---^
        if (toEnd) {
          g_testResult = 1;
          g_state = END_RESET;
          break;
        }
      }

      if ((g_wasPushedA == false) && (g_btnAState == LOW)) {
        g_wasPushedA = true;
        timer.t1 = millis();
      }
      if ((g_wasPushedB == false) && (g_btnBState == LOW)) {
        g_wasPushedB = true;
        timer.t2 = millis();
      }

      break;

    case END_RESET:
      // azzera le variabil
      semaforo.data = 0;
      g_wasPushedA = false;
      g_wasPushedB = false;
      printResult();
      // azzera g_restResult qui perché è usata in printResult()
      g_testResult = 0;
      // azzera tutti i timer
      timer.t0 = timer.t1 = timer.t2 = 0;
      delay(200);
      // se un giocatore tiene ancora premuto il pulsante
      // il gioco non può ricominciare
      while (!digitalRead(PIN_START_A));
      while (!digitalRead(PIN_START_B));
      //delay(200);
      delay(g_rndNumber / 8);

      Serial.print("\nrnd number: ");
      Serial.println(g_rndNumber);
      Serial.println("Premi A o B per ricominciare.");
      
      // alla fine azzera g_rndNumber.
      g_rndNumber = 0;

      // ** CHAGE STATE **
      g_state = RE_START;
      break;

  }
}
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
74HC595
sr1:Q1
sr1:Q2
sr1:Q3
sr1:Q4
sr1:Q5
sr1:Q6
sr1:Q7
sr1:GND
sr1:Q7S
sr1:MR
sr1:SHCP
sr1:STCP
sr1:OE
sr1:DS
sr1:Q0
sr1:VCC
led1:A
led1:C
led2:A
led2:C
led3:A
led3:C
led4:A
led4:C
led5:A
led5:C
led6:A
led6:C
led7:A
led7:C
led8:A
led8:C
btn1:1.l
btn1:2.l
btn1:1.r
btn1:2.r
gnd1:GND
r1:1
r1:2
r2:1
r2:2
r3:1
r3:2
r4:1
r4:2
r5:1
r5:2
r6:1
r6:2
r7:1
r7:2
r8:1
r8:2
btn2:1.l
btn2:2.l
btn2:1.r
btn2:2.r