/*
Uno Link 4
created 10.19.2023 -q
*/


#include <Adafruit_NeoPixel.h>
#define NUMPIXELS 49
#define PIN 2
#define BTN_R 3
#define BTN_D 4
#define BTN_L 5
#define PLAYR_1 1
#define PLAYR_2 2

#define MAX_COL 6
#define MAX_ROW 5
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_RGB + NEO_KHZ800);

uint32_t empty = strip.Color(255, 255, 255);
uint32_t blu = strip.Color(0, 0, 255);
uint32_t red = strip.Color(0, 255, 0);
uint32_t off = strip.Color(0, 0, 0);
uint32_t currentPlayer = blu;

int16_t cur_bright = 255;

byte playerUp = PLAYR_1;
byte dropPosistion = 3;
byte gameMode = 0;

byte WinLeds[4];

byte GameGrid[7][6] = {
  {0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0}
};

byte LedGrid[7][6] = {
  {7, 14, 21, 28, 35, 42},
  {8, 15, 22, 29, 36, 43},
  {9, 16, 23, 30, 37, 44},
  {10, 17, 24, 31, 38, 45},
  {11, 18, 25, 32, 39, 46},
  {12, 19, 26, 33, 40, 47},
  {13, 20, 27, 34, 41, 48}
};

byte btnPins[3] = {5, 4, 3};
unsigned long debouncers[3];
byte lastButtons[3] = {1, 1, 1};
int  dropToo = 0;
byte dropStep = 0;
unsigned long lastStep;
int dropSpeed = 100;
int blinkSpeed = 250;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  Serial.println("ready..");
  pinMode(BTN_L, INPUT_PULLUP);
  pinMode(BTN_D, INPUT_PULLUP);
  pinMode(BTN_R, INPUT_PULLUP);
  strip.begin();
  for (int i = 0; i < NUMPIXELS; i++) {
    strip.setPixelColor(i, empty);
  }
  strip.setPixelColor(dropPosistion, blu);
  strip.show();

  for (int col = 0; col < 7; col++) {
    for (int row = 0; row < 6; row++) {
      GameGrid[col][row] = 0;
    }
  }
}

void loop() {

  unsigned long now = millis();

  if (gameMode == 0) {
    for (int i = 0; i < 3; i++) {
      if (now - debouncers[i] >= 50) {
        byte btn = digitalRead(btnPins[i]);
        if (btn != lastButtons[i]) {
          lastButtons[i] = btn;
          debouncers[i] = now;
          if (btn == LOW) {
            switch (i) {
              case 0: if (dropPosistion > 0) {
                  dropPosistion--;
                  strip.setPixelColor(dropPosistion, currentPlayer);
                  strip.setPixelColor(dropPosistion + 1, empty);
                  strip.show();
                }
                break;
              case 1: if (CanDrop(dropPosistion) > 0) {
                  dropToo = CanDrop(dropPosistion);
                  GameGrid[dropPosistion][dropToo - 2] = playerUp;
                  lastStep = now;
                  dropStep = 0;
                  gameMode = 1;
                } else {
                  dropToo = CanDrop(dropPosistion);
                } break;
              case 2: if (dropPosistion < 6) {
                  dropPosistion++;
                  strip.setPixelColor(dropPosistion, currentPlayer);
                  strip.setPixelColor(dropPosistion - 1, empty);
                  strip.show();
                }  break;
            }
          }
        }
      }
    }
  } else if (gameMode == 1) {
    //dropping..
    if (now - lastStep >= dropSpeed) {
      lastStep = now;
      strip.setPixelColor(dropPosistion + (dropStep * 7), currentPlayer);
      if (dropStep > 0)
        strip.setPixelColor(dropPosistion + ((dropStep - 1) * 7), empty);
      strip.show();
      dropStep++;
      if (dropStep == dropToo) {
        //check for a winning drop..
        if (DropWins(dropToo - 2)) {
          Serial.println("Winner");
          dropStep = 0;
          dropToo = 11;
          gameMode = 2;
          return;
        }

        gameMode = 0;
        if (currentPlayer == blu) currentPlayer = red; else currentPlayer = blu;
        if (playerUp == PLAYR_1) playerUp = PLAYR_2; else playerUp = PLAYR_1;
        strip.setPixelColor(dropPosistion, currentPlayer);
        strip.show();
      }
    }
  } else if (gameMode == 2) {
    if (now - lastStep >= blinkSpeed) {
      lastStep = now;
      for (int i = 0; i < 4; i++) {
        if (dropStep == 0) {
          strip.setPixelColor(WinLeds[i], currentPlayer);
        } else {
          strip.setPixelColor(WinLeds[i], empty);
        }
      }
      strip.show();
      dropStep++;
      if (dropStep > 1) dropStep = 0;
      dropToo--;
      if (dropToo == 0) {
        gameMode = 0;
        ResetGame();
      }
    }
  }
}

void ResetGame() {
  //clear leds..
  for (int i = 0; i < NUMPIXELS; i++) {
    strip.setPixelColor(i, empty);
  }
  strip.show();
  //clear gamegrid
  for (int col = 0; col < 7; col++) {
    for (int row = 0; row < 6; row++) {
      GameGrid[col][row] = 0;
    }
  }
  //set next player up..
  dropPosistion = 3;
  if (currentPlayer == blu) currentPlayer = red; else currentPlayer = blu;
  if (playerUp == PLAYR_1) playerUp = PLAYR_2; else playerUp = PLAYR_1;
  strip.setPixelColor(dropPosistion, currentPlayer);
  strip.show();
}

bool DropWins(int drop) {
  bool result = false;
  byte col = dropPosistion;
  byte row = drop;
  result = CheckCol(col);
  if (!result) result = CheckRow(row);
  if (!result) result = CheckDiag(col, row);
  return (result);
}

bool CheckCol(int col) {
  bool result = false;
  byte cnt = 0;
  for (int i = 0; i < 6; i++)
  {
    if (GameGrid[col][i] == playerUp) {
      if (cnt < 4) WinLeds[cnt] = LedGrid[col][i];
      cnt++;
    } else if (cnt < 4) cnt = 0;
  }
  if (cnt > 3) result = true;
  return (result);
}

bool CheckRow(int row) {
  bool result = false;
  byte cnt = 0;
  for (int i = 0; i < 7; i++)
  {
    if (GameGrid[i][row] == playerUp) {
      if (cnt < 4) WinLeds[cnt] = LedGrid[i][row];
      cnt++;
    } else if (cnt < 4) cnt = 0;
  }
  if (cnt > 3) result = true;
  return (result);
}


bool CheckDiag(int col, int row) {
  bool result = false;
  int startCol, startRow, count;
  //top left..
  if (col > row) {
    startCol = col - row;
    startRow = 0;
  } else {
    startCol = 0;
    startRow = row - col;
  }
  int rowStep = 0;
  for (int c = startCol; c < MAX_COL + 1; c++) {
    if (GameGrid[c][startRow + rowStep] == playerUp) {
      if (count < 4) WinLeds[count] = LedGrid[c][startRow + rowStep];
      count++;
    } else if (count < 4) count = 0;
    rowStep++;
  }
  //top right
  if (count < 4) {
    count = 0;
    startCol = row + col;
    if (startCol > MAX_COL) {
      startCol = MAX_COL;
      startRow = row - ((col + row) - startCol);
    }
    else
      startRow = 0;
    int rowStep = 0;
    for (int c = startCol; c > -1; c--) {
      if (GameGrid[c][startRow + rowStep] == playerUp) {
        if (count < 4) WinLeds[count] = LedGrid[c][startRow + rowStep];
        count++;
      } else if (count < 4) count = 0;
      rowStep++;
    }
  }
  if (count > 3) result = true;
  return (result);
}


int CanDrop(byte col) {
  int result = -1;
  for (int i = 0; i < 6; i++) {
    if (GameGrid[col][i] != 0) {
      byte val = GameGrid[col][i];
      break;
    } else result = i;
  }
  if (result > -1) result += 2;
  return (result);
}