/* Light game for Christmas tree v1
*  made by Bob Clagett at I Like To Make Stuff
*  
*  This is PROTOTYPE code, not meant to be fully optimized or even correct.
*  I tried to leave comments for a while, then ran out of time.
*  
*  We cannot provide any support for this code, but feel free to use it as a 
*/

#include <EasyButton.h>
#include <dmtimer.h>
#include <FastLED.h>

#define BUTTON_ONE_PIN 10 //left
#define BUTTON_ONE_LED_PIN 9//right
#define BUTTON_TWO_LED_PIN 11 //left
#define BUTTON_TWO_PIN 12//right
EasyButton button1(BUTTON_ONE_PIN);
EasyButton button2(BUTTON_TWO_PIN);

CRGBPalette16 currentPalette;
TBlendType    currentBlending;

extern CRGBPalette16 myRedWhiteBluePalette;
extern const TProgmemPalette16 myRedWhiteBluePalette_p PROGMEM;

//setup constants and variables for gameplay
const int initGameSpeed = 500;
const bool renderGameAsText = false;

#define NUM_STRIPS 7
#define NUM_LEDS_PER_STRIP 50

bool screenSaverToggle = false;
int screenSaverTimeOut = 0;

//define gameboard matrix
const int rows = NUM_LEDS_PER_STRIP;
const int columns = NUM_STRIPS;
int gameArray[columns][rows];

CRGB leds[NUM_STRIPS][NUM_LEDS_PER_STRIP];

bool queueNewGame = false;
bool gameOn = false;
int screenSaverSelector = 1;
int playerX = 3;
int gameLevel = 0;
int levelStepper = 30;  
int levelIncrease = 3; //how many clicks to condense next drop
int numToDrop = 3; //initial # to drop at once (random position, potential overlap)
int countUntilDrop = 0;
int waitForDrop = 0;
int waitForDropIncrease = 0;
int waitForDropThreshold = 5; 
int increaseDropThreshold = 35;
DMTimer myTimer(initGameSpeed);

void setup() {
  Serial.begin(115200);
  // put your setup code here, to run once:

currentPalette = CloudColors_p;
    currentBlending = LINEARBLEND;
    
pinMode(BUTTON_ONE_LED_PIN, OUTPUT);
pinMode(BUTTON_TWO_LED_PIN, OUTPUT);
digitalWrite(BUTTON_ONE_LED_PIN, HIGH);
digitalWrite(BUTTON_TWO_LED_PIN, HIGH);
  //setup LEDs
   // tell FastLED there's XX NEOPIXEL leds on pin YY
  FastLED.addLeds<WS2811, 2>(leds[0], NUM_LEDS_PER_STRIP);
  FastLED.addLeds<WS2811, 3>(leds[1], NUM_LEDS_PER_STRIP);
  FastLED.addLeds<WS2811, 4>(leds[2], NUM_LEDS_PER_STRIP);
  FastLED.addLeds<WS2811, 5>(leds[3], NUM_LEDS_PER_STRIP);
  FastLED.addLeds<WS2811, 6>(leds[4], NUM_LEDS_PER_STRIP);
  FastLED.addLeds<WS2811, 7>(leds[5], NUM_LEDS_PER_STRIP);
  FastLED.addLeds<WS2811, 8>(leds[6], NUM_LEDS_PER_STRIP);

 // Initialize the button1
  button1.begin();
  // Initialize the button2
  button2.begin();
  // Add the callback function to be called when the button1 is pressed.
  button1.onPressed(onButton1Pressed);
  // Add the callback function to be called when the button2 is pressed.
  button2.onPressed(onButton2Pressed);
  Serial.println("Ready......");
}

void loop() {
  if(myTimer.isTimeReached()){ //check if execution time has been reached
    tickTock();
  }
}

void onButton1Pressed() {
  if(queueNewGame == false){
    if(gameOn == true){
      Serial.println("left");
      if(playerX>0){
        playerX--;
      }
   } else {
    //start a new game!!!!
      Serial.println("clicked, start a new one");
      queueNewGame = true;  
    }
  }
}

void onButton2Pressed() {
  if(queueNewGame == false){
    if(gameOn == true){
      Serial.println("right");
        if(playerX<columns-1){
          playerX++;
        }
    } else {
      //start a new game!!!!
        Serial.println("clicked, start a new one");
        queueNewGame = true;  
      }
  }
}

void tickTock(){
  button1.read();
  button2.read();
  if(queueNewGame == true){
    startNewGame(); 
  }

  switch(gameOn){
    case false:
      //screensaver mode  
      stepScreensaver();
      break;
    case true:
      stepGame();
      break;
   }
  renderGameBoard();
}
void startNewGame() {
  Serial.println("HERE WE GO!");
playerX = 3;
gameLevel = 0;
levelStepper = 30;  
levelIncrease = 3; //how many clicks to condense next drop
numToDrop = 3; //initial # to drop at once (random position, potential overlap)
countUntilDrop = 0;
waitForDrop = 0;
waitForDropIncrease = 0;
waitForDropThreshold = 5; 
increaseDropThreshold = 35;

  //clear gameboard
  for (int i = columns-1; i >=0; i--) {
        for (int j = rows-1; j >= 0; j--) {
          gameArray[i][j] = 0;
        }
   }
  //reset all light colors;
  // play startup sequence
  gameOn = true;
  queueNewGame = false;
}

void stepGame(){
  //Serial.println("stepGame");
    countUntilDrop--;

    if(countUntilDrop <= 0){
        dropPiece();
    }

    for (int i = columns-1; i >=0; i--) {
      for (int j = rows-1; j >=0; j--) {
        int locValue = gameArray[i][j];
        int above = j-1;
        if(above>=0){
          
          int ceilingValue = gameArray[i][above];
          if(ceilingValue==1){
            gameArray[i][j] = ceilingValue; //inherit value from location above
            gameArray[i][above] = 0; //clear location above
            locValue = gameArray[i][j];
          } else {
            locValue = gameArray[i][j] = 0; //empties bottom row when nothing is above it 
          }
          
        }
        if(j==rows-1 && i==playerX){
          locValue = 2;//show player position
        }
         switch (locValue) {
          case 2://player
            switch(gameLevel){
              case 0:
              case 1:
              case 2:
                leds[i][j] = CRGB::Blue;
                break;
              case 3:
              case 4:
              case 5:
                leds[i][j] = CRGB::Yellow;
                break;
              case 6:
              case 7:
              case 8:
                leds[i][j] = CRGB::Violet;
                break;
              case 9:
              case 10:
              case 11:
                leds[i][j] = CRGB::Green;  //I haven't seen anyone get past this point.  It's basically impossible
                break;
              case 12:
              case 13:
              case 14:
                leds[i][j] = CRGB::Navy;
                break;
               default:
                leds[i][j] = CRGB::Red;
            }
            
            break;
          case 1:
            leds[i][j] = CRGB::White;
            break;
          case 0:
            leds[i][j] = CRGB::Black;
            break;
        }

        //check for player collision
        if(j == rows-1){
          if(gameArray[i][j]==1){
            checkCollision(i);
          }
        }
      }
    }  
    //Serial.println("tick complete");
}

void renderGameBoard() {
  if(renderGameAsText == false){
    //Serial.println("renderGameBoard");
    FastLED.show();
    delay(30);
    
  } else {
    Serial.println("----------------------");
      // This outer loop will go over each strip, one at a time
    for (int i = columns-1; i >=0; i--) {
        for (int j = rows-1; j >= 0; j--) {
        int locValue = gameArray[i][j];
        if(j==0 && i==playerX){
           Serial.print("X");
        } else {
          if(locValue == 0){
            Serial.print(" ");
          } else{
            Serial.print(locValue);
          }
        }
      }
      Serial.println("|");
    }
  }
}
void stepScreensaver() {
  //Serial.println("stepScreensaver ");
  //TODO: make this more interesting
  static uint8_t colorIndex = 0;
  screenSaverTimeOut+=1000;
 screenSaverToggle = !screenSaverToggle;
  if(screenSaverTimeOut > 10000){
     screenSaverTimeOut = 0;
     uint16_t clr = CRGB::Black;
      for(int i = 0; i < columns; i++) { 
        // This inner loop will go over each led in the current strip, one at a time
        for(int j = 0; j < rows; j++) {
          switch(screenSaverSelector){
            case 0:
              if(screenSaverToggle){
                leds[i][j] = CRGB::Red;
              } else {
                leds[i][j] = CRGB::Green;  
              }
            break;
            case 1:
            colorIndex +=3;
              leds[i][j] = ColorFromPalette( currentPalette, colorIndex, 255, currentBlending);
            break;
            }
          
        }
        delay(30);
      }  
  }
}

void checkCollision(int k) {
    if(playerX == k){
      //Collision found
      gameOn = false;
      playLoseAnim();
    }
  }

void dropPiece() {
  // set random top row location
  countUntilDrop = levelStepper;
  waitForDrop++;
  if(waitForDrop==waitForDropThreshold && levelStepper>=0){
    waitForDrop = 0;
    levelStepper-=levelIncrease; 
    gameLevel++; 
  }
  waitForDropIncrease++;
  if(waitForDropIncrease == increaseDropThreshold){
    waitForDropIncrease = 0;
    numToDrop++; 
  }
  if( numToDrop >NUM_STRIPS-2){
    numToDrop = NUM_STRIPS-2; //Always leave at least 2 positions open ofr the player
  }
  for( int z=0;z<numToDrop;z++){
    int ranX = random(0, columns);
    gameArray[ranX][0] = 1;
  }
}

void playLoseAnim() {
  
  for(int k = 0; k < 3; k++) {
    Serial.println("YOU LOSE!");
    for(int i = 0; i < columns; i++) {
      for(int j = 0; j < rows; j++) {
        leds[i][j] = CRGB::Red; 
      }
    }
    FastLED.show();
    delay(250);
    for(int i = 0; i < columns; i++) {
      for(int j = 0; j < rows; j++) {
        leds[i][j] = CRGB::Black;
      }
    }
    FastLED.show();
    delay(250);
    //switch scrrensaver
    screenSaverSelector++;
    if (screenSaverSelector > 1){
      screenSaverSelector = 0;  
    }
  }
}