// Arduino "ToneQuest" Game with Training Mode and Directional LEDs

const int JOY_X = A1;
const int JOY_Y = A2;
const int JOY_BTN = 7;
const int buzzerPin = 8;

// LED pins for directions
const int LED_UP = 2;
const int LED_DOWN = 3;
const int LED_LEFT = 4;
const int LED_RIGHT = 5;

const int MAX_LEVEL = 100;
int pattern[MAX_LEVEL];
int currentLevel = 1;
int currentStep = 0;
bool trainingMode = false;

unsigned long lastButtonTime = 0;
const unsigned long debounceDelay = 300;

#define NOTE_C4  262
#define NOTE_D4  294
#define NOTE_E4  330
#define NOTE_F4  349
#define NOTE_G4  392
#define NOTE_A4  440
#define NOTE_B4  494

#define NOTE_C5  523
#define NOTE_D5  587
#define NOTE_E5  659
#define NOTE_F5  698
#define NOTE_G5  784
#define NOTE_A5  880
#define NOTE_B5  988

#define NOTE_SUSTAIN 100

const int successMelody[] = {
  NOTE_A5, NOTE_B5, NOTE_C5, NOTE_B5,
  NOTE_C5, NOTE_D5, NOTE_C5, NOTE_D5,
  NOTE_E5, NOTE_D5, NOTE_E5, NOTE_E5
};
const uint8_t successMelodyLength = sizeof(successMelody) / sizeof(successMelody[0]);

enum Direction { UP = 0, DOWN, LEFT, RIGHT };
int directionTones[4] = { NOTE_D4, NOTE_C4, NOTE_E4, NOTE_F4 };
const char* directionNames[4] = { "UP", "DOWN", "LEFT", "RIGHT" };
int directionLEDs[4] = { LED_UP, LED_DOWN, LED_LEFT, LED_RIGHT };

void setup() {
  Serial.begin(9600);
  pinMode(JOY_BTN, INPUT_PULLUP);
  pinMode(buzzerPin, OUTPUT);

  pinMode(LED_UP, OUTPUT);
  pinMode(LED_DOWN, OUTPUT);
  pinMode(LED_LEFT, OUTPUT);
  pinMode(LED_RIGHT, OUTPUT);

  randomSeed(analogRead(A0));
  generatePattern();
  delay(500);
  playPattern();
}

void loop() {
  if (buttonPressed()) {
    trainingMode = !trainingMode;
    currentStep = 0;
    if (!trainingMode) {
      Serial.println(">> Training ended. Restarting level.");
      delay(3000);
      playPattern();
    } else {
      Serial.println(">> Training mode ON. Play tones freely.");
    }
    return;
  }

  int move = getJoystickDirection();
  if (move != -1) {
    Serial.print("Joystick moved: ");
    Serial.print(directionNames[move]);
    playTone(move);

    if (trainingMode) return;

    int expected = pattern[currentStep];
    Serial.print(" | Expected move: ");
    Serial.println(directionNames[expected]);

    if (move == expected) {
      currentStep++;
      if (currentStep >= currentLevel + 3) {
        delay(250);
        playSuccessTune();
        Serial.print(">> Level ");
        Serial.print(currentLevel);
        Serial.println(" completed!");
        currentStep = 0;
        currentLevel++;
        delay(1000);
        playPattern();
      }
    } else {
      delay(250);
      playFailureTune();
      Serial.println("!! WRONG MOVE. Restarting game at level 1.");
      currentLevel = 1;
      currentStep = 0;
      generatePattern();
      delay(1000);
      playPattern();
    }
  }
}

void generatePattern() {
  for (int i = 0; i < MAX_LEVEL; i++) {
    pattern[i] = random(0, 4);
  }
  Serial.println(">> New pattern generated.");
}

void playPattern() {
  Serial.print(">> Playing pattern for Level ");
  Serial.println(currentLevel);
  for (int i = 0; i < currentLevel + 3; i++) {
    int dir = pattern[i];
    Serial.print("Play tone: ");
    Serial.println(directionNames[dir]);
    playTone(dir);
    noTone(buzzerPin);
    digitalWrite(directionLEDs[dir], LOW);
    delay(250);
  }
}

void playTone(int direction) {
  digitalWrite(directionLEDs[direction], HIGH);
  tone(buzzerPin, directionTones[direction], 500);
  delay(500);
  digitalWrite(directionLEDs[direction], LOW);
}

void notifyLevel(int level) {
  Serial.print(">> Notifying new level: ");
  Serial.println(level);
  for (int i = 0; i < level; i++) {
    tone(buzzerPin, 1000, 150);
    delay(300);
  }
}

void playSuccessTune() {
  for (uint8_t i = 0; i < successMelodyLength; i++) {
    tone(buzzerPin, successMelody[i]);
    delay(NOTE_SUSTAIN);
  }
  noTone(buzzerPin);
}

void playFailureTune() {
  tone(buzzerPin, NOTE_G4);
  delay(250);
  tone(buzzerPin, NOTE_C4);
  delay(500);
  noTone(buzzerPin);
}

int getJoystickDirection() {
  int x = analogRead(JOY_X);
  int y = analogRead(JOY_Y);

  // Adjust this based on your joystick orientation
  if (x > 800) return UP;
  if (x < 200) return DOWN;
  if (y < 200) return LEFT;
  if (y > 800) return RIGHT;

  return -1;
}

bool buttonPressed() {
  if (digitalRead(JOY_BTN) == LOW) {
    if (millis() - lastButtonTime > debounceDelay) {
      lastButtonTime = millis();
      return true;
    }
  }
  return false;
}
