#include <Adafruit_NeoPixel.h>
#include "pitches.h"

// Define ENDOFSONG to indicate the end of a song sequence
#define ENDOFSONG -1

// NeoPixel setup
#define PIXEL_PIN 6
#define NUMPIXELS 40
Adafruit_NeoPixel pixels(NUMPIXELS, PIXEL_PIN, NEO_GRB + NEO_KHZ800);

// Button setup (connect to GND and use INPUT_PULLUP)
const int BUTTON_PINS[7] = {2, 3, 4, 5, 7, 8, 9};  // 7 buttons
const int BUZZER_PIN = 10;

const int FADEINDURATION = 200;
const int FADEOUTDURATION = 150;
const int PAUSEB4SEQ = 500;
const int SEQDELAY = 50;
const int MINLEVEL = 2;
const int MAXLEVEL = 16;

int gameLevel;
int simonSez[MAXLEVEL];
const int NOTES[7] = {NOTE_C4, NOTE_D4, NOTE_E4, NOTE_F4, NOTE_G4, NOTE_A4, NOTE_B4};

// Group pixel ranges
int groupStart[7] = {0, 8, 15, 21, 26, 30, 34};
int groupEnd[7] = {7, 14, 20, 25, 29, 33, 36};

void setup() {
  Serial.begin(9600);
  pixels.begin();
  pixels.show();  // Initialize all pixels to 'off'
  pinMode(BUZZER_PIN, OUTPUT);

  // Set button pins as input with pull-up resistors
  for (int i = 0; i < 7; i++) {
    pinMode(BUTTON_PINS[i], INPUT_PULLUP);
  }

  randomSeed(analogRead(A0));  // Seed for randomization
  gameLevel = MINLEVEL;
  setGroupColors();  // Set initial colors for each group
  playWinSequence();  // Initial feedback to confirm startup
}

void loop() {
  initGameSequence(gameLevel);
  while (true) {
    waitForButtonPress();  // Wait for the first button press to start
    delay(PAUSEB4SEQ);
    playGameSequence(gameLevel);

    if (playerGuess(gameLevel)) {
      playCorrectSequence();
      if (++gameLevel > MAXLEVEL) {
        playWinSequence();
        while (true);  // Game over, wait for manual reset
      }
    } else {
      playLoseSequence();
      gameLevel = MINLEVEL;
    }
  }
}

void setGroupColors() {
  setColorRange(0, 7, pixels.Color(255, 0, 0));     // Red
  setColorRange(8, 14, pixels.Color(255, 165, 0));  // Orange
  setColorRange(15, 20, pixels.Color(255, 255, 0)); // Yellow
  setColorRange(21, 25, pixels.Color(173, 216, 230)); // Light Blue
  setColorRange(26, 29, pixels.Color(0, 0, 139));   // Dark Blue
  setColorRange(30, 33, pixels.Color(128, 0, 128)); // Purple
  pixels.show();
}

void setColorRange(int start, int end, uint32_t color) {
  for (int i = start; i <= end; i++) {
    pixels.setPixelColor(i, color);
  }
}

void waitForButtonPress() {
  while (true) {
    if (anyButtonPressed()) break;
  }
}

bool anyButtonPressed() {
  for (int i = 0; i < 7; i++) {
    if (digitalRead(BUTTON_PINS[i]) == LOW) {
      Serial.print("Button ");
      Serial.print(i);
      Serial.println(" pressed!");
      return true;
    }
  }
  return false;
}

void initGameSequence(int level) {
  for (int i = 0; i < level; i++) {
    simonSez[i] = random(7);
  }
}

void playGameSequence(int level) {
  Serial.print("Sequence: ");
  for (int i = 0; i < level; i++) {
    playGroup(simonSez[i]);
    Serial.print(simonSez[i]);
    if (i < level - 1) Serial.print(", ");
  }
  Serial.println();
}

void playGroup(int group) {
  int start = groupStart[group];
  int end = groupEnd[group];

  for (int i = start; i <= end; i++) {
    pixels.setPixelColor(i, pixels.Color(255, 255, 255));  // Flash white
  }
  pixels.show();
  tone(BUZZER_PIN, NOTES[group], 100);
  delay(FADEINDURATION);

  setGroupColors();  // Restore original group colors
  pixels.show();
  delay(FADEOUTDURATION);
}

bool playerGuess(int level) {
  for (int i = 0; i < level; i++) {
    int guess = getButtonPress();
    if (guess != simonSez[i]) return false;
    playGroup(guess);
  }
  return true;
}

int getButtonPress() {
  while (true) {
    for (int i = 0; i < 7; i++) {
      if (digitalRead(BUTTON_PINS[i]) == LOW) {
        Serial.print("Button ");
        Serial.print(i);
        Serial.println(" released!");
        while (digitalRead(BUTTON_PINS[i]) == LOW);  // Wait for release
        return i;
      }
    }
  }
}

void playSong(int song[][3]) {
  for (int i = 0; song[i][0] != ENDOFSONG; i++) {
    int note = song[i][0];
    int duration = song[i][1] * 125;
    int group = song[i][2];

    if (note > 0) tone(BUZZER_PIN, note, duration);
    playGroup(group);
    delay(duration);
    noTone(BUZZER_PIN);
  }
}

void playWinSequence() {
  int WINSONG[][3] = {{NOTE_C4, 1, 0}, {NOTE_D4, 1, 1}, {NOTE_E4, 1, 2}, {ENDOFSONG, ENDOFSONG, ENDOFSONG}};
  playSong(WINSONG);
  Serial.println("You won!");
}

void playLoseSequence() {
  int LOSESONG[][3] = {{NOTE_A4, 2, 6}, {NOTE_G4, 2, 5}, {NOTE_F4, 2, 4}, {ENDOFSONG, ENDOFSONG, ENDOFSONG}};
  playSong(LOSESONG);
  Serial.println("You lost!");
}

void playCorrectSequence() {
  Serial.println("Correct!");
}