#include <Key.h>
#include <Keypad.h>

#define NOTE_C2  65
#define NOTE_CS2 69
#define NOTE_D2  73
#define NOTE_DS2 78
#define NOTE_E2  82
#define NOTE_F2  87
#define NOTE_FS2 93
#define NOTE_G2  98
#define NOTE_GS2 104
#define NOTE_A2  110
#define NOTE_AS2 117
#define NOTE_B2  123
#define NOTE_C3  131
#define NOTE_CS3 139
#define NOTE_D3  147
#define NOTE_DS3 156
#define NOTE_E3  165
#define NOTE_F3  175
#define NOTE_FS3 185
#define NOTE_G3  196
#define NOTE_GS3 208
#define NOTE_A3  220
#define NOTE_AS3 233
#define NOTE_B3  247
#define NOTE_C4  262
#define NOTE_CS4 277
#define NOTE_D4  294
#define NOTE_DS4 311
#define NOTE_E4  330
#define NOTE_F4  349
#define NOTE_FS4 370
#define NOTE_G4  392
#define NOTE_GS4 415
#define NOTE_A4  440
#define NOTE_AS4 466
#define NOTE_B4  494
#define NOTE_C5  523
#define NOTE_CS5 554
#define NOTE_D5  587
#define NOTE_DS5 622
#define NOTE_E5  659
#define NOTE_F5  698
#define NOTE_FS5 740
#define NOTE_G5  784
#define NOTE_GS5 831
#define NOTE_A5  880
#define NOTE_AS5 932
#define NOTE_B5  988
#define NOTE_C6  1047
#define NOTE_CS6 1109
#define NOTE_D6  1175
#define NOTE_DS6 1245
#define NOTE_E6  1319
#define NOTE_F6  1397
#define NOTE_FS6 1480
#define NOTE_G6  1568
#define NOTE_GS6 1661
#define NOTE_A6  1760
#define NOTE_AS6 1865
#define NOTE_B6  1976
#define NOTE_C7  2093
#define NOTE_CS7 2217
#define NOTE_D7  2349
#define NOTE_DS7 2489
#define NOTE_E7  2637
#define NOTE_F7  2794
#define NOTE_FS7 2960
#define NOTE_G7  3136
#define NOTE_GS7 3322
#define NOTE_A7  3520
#define NOTE_AS7 3729
#define NOTE_B7  3951
#define NOTE_C8  4186
#define NOTE_CS8 4435
#define NOTE_D8  4699
#define NOTE_DS8 4978
#define REST      0

int buzzer = 8;

const int startingTempo = 50;
int currentTempo = startingTempo;

// const int availableNotes[] = { 
//   NOTE_C3, NOTE_D3, NOTE_E3, NOTE_F3, NOTE_G3, NOTE_A3, NOTE_B3,
//   NOTE_C4, NOTE_D4, NOTE_E4, NOTE_F4, NOTE_G4, NOTE_A4, NOTE_B4,
//   NOTE_C5, NOTE_D5
// };

// const int AvailableNotesMax = 15;

const int maxMelodySize = 8;

const byte ROWS = 4;
const byte COLS = 4;

// this will be converted into a keymap
const char buttons[ROWS][COLS] = {
  {'0', '1', '2', '3'},
  {'4', '5', '6', '7'},
  {'8', '9', 'a', 'b'},
  {'c', 'd', 'e', 'f'},
};

const int tones[ROWS][COLS] = {
  {NOTE_C4, NOTE_D4, NOTE_E4, NOTE_F4},
  {NOTE_G4, NOTE_A4, NOTE_B4, NOTE_C5}, 
  {NOTE_D5, NOTE_E5, NOTE_F5, NOTE_G5}, 
  {NOTE_A5, NOTE_B5, NOTE_C6, NOTE_D6}
};


// indicates the hardware pins connected to the keypad
// these correspond to the digital pins the keypad is connected to
const byte rowPins[ROWS] = {5, 4, 3, 2};
const byte colPins[COLS] = {6, 7, 12, 13};

const int redPin = 11;
const int greenPin = 10;
const int bluePin = 9;

Keypad keyPad = Keypad(makeKeymap(buttons), rowPins, colPins, ROWS, COLS);

void setup() {
  Serial.begin(9600);

  pinMode(redPin, OUTPUT);  // designate pins for PWM LED output
  pinMode(greenPin, OUTPUT);
  pinMode(bluePin, OUTPUT);

  redLight();
  delay(500);
  greenLight();
  delay(500);
  blueLight();
  delay(500);
  noLight();

  randomSeed(analogRead(0));
}

int melody[maxMelodySize];
int melodyLength = 0;

void loop() {

  if (melodyLength > 0) {
    playMelody(melody, melodyLength);
    if (userAttempt(melody, melodyLength)) {
      melody[melodyLength] = generateNote();
      melodyLength++;
      if (melodyLength == maxMelodySize) {
        celebrate();
        melodyLength = 0;
      }
    }
    else {
      melodyLength = 0;
    }
  }
  else {
    Serial.println("Press Any Key to Start");
    keyPad.waitForKey();
    giveInitialPitch();
    melody[0] = generateNote();
    melodyLength++;
  }
}

void celebrate() {
  Serial.println("WINNER!!!");
  greenLight();
  playNote(NOTE_C5, 500);
  blueLight();
  playNote(NOTE_E5, 500);
  whiteLight();
  playNote(NOTE_G4, 500);
  greenLight();
  playNote(NOTE_E5, 1000);
}

void giveInitialPitch() {
  Serial.println("This is keypad note upper left");
  playNote(tones[0][0], 3000);
}

bool userAttempt(int melody[], int length) {
  char lastPress;
  int note;
  bool succeeded;

  logVar("userAttempt length", length);

  for (byte i = 0; i < length; i++) {
    whiteLight(); // shows waiting
    
    Serial.print("cheat "); 
    for(int r=0;r<ROWS;r++)
       for(int c=0;c<COLS;c++)
          if(tones[c][r]==melody[i])
          { Serial.print(r); Serial.print(',');Serial.println(c); }

    lastPress = keyPad.waitForKey();

    note = noteForKey(lastPress);
  
    if (melody[i] == note) {
      greenLight();
      playNote(note, 800);
      succeeded = true;
    }
    else {
      redLight();
      Serial.println("Wrong");
      playNote(NOTE_C2, 1000);
      succeeded = false;
      logVar("expected note:", melody[i]);
      logVar("entered note:", note);
      break;
    };
  }
  delay(1000);
  return succeeded;
}

void logVar(String pre, int val) {
  Serial.print(pre);
  Serial.print(" ");
  Serial.println(val);
}

int noteForKey(char key) {
  int answer = 0;
  for (byte row=0; row < ROWS; row++) {
    for (byte col=0; col < COLS; col++) {
      if (key == buttons[row][col]) {
        answer=tones[row][col];
      }
    }
  }
  return answer;
}

void playMelody(int melody[], int length) {
  const int noteDuration = 60000 / currentTempo; 
  blueLight();
  for (byte i = 0; i < length; i++) {
     playNote(melody[i], noteDuration);
  }
  noLight();
}

void playNote(int note, int duration) {
  tone(buzzer, note, duration * 0.9);
  delay(duration);
  noTone(buzzer);
}

void redLight() {
  analogWrite(redPin, 0);
  analogWrite(greenPin, 255);
  analogWrite(bluePin, 255);
}

void greenLight() {
  analogWrite(redPin, 255);
  analogWrite(greenPin, 0);
  analogWrite(bluePin, 255);
}

void blueLight() {
  analogWrite(redPin, 255);
  analogWrite(greenPin, 255);
  analogWrite(bluePin, 0);
}

void noLight() {
  analogWrite(redPin, 255);
  analogWrite(greenPin, 255);
  analogWrite(bluePin, 255);  
}

void whiteLight() {
  analogWrite(redPin, 0);
  analogWrite(greenPin, 0);
  analogWrite(bluePin, 0);    
}

int generateNote() {
  int note = tones[random(0, ROWS - 1)][random(0,COLS - 1)];
  return note;
}