#include <LiquidCrystal_I2C.h>
#include <Keypad.h>

// LCD Settings
LiquidCrystal_I2C lcd(0x27, 20, 4); 

// Game Constants
const int BUTTON_ROWS = 3;
const int BUTTON_COLS = 3;

// Keypad constants
const byte ROWS = 4;
const byte COLS = 4;

// Array to represent keys on keypad
char hexaKeys[ROWS][COLS] = {
 {'1', '2', '3', 'A'},
 {'4', '5', '6', 'B'},
 {'7', '8', '9', 'C'},
 {'*', '0', '#', 'D'},
};

// Connections to the arduino
byte rowPins[ROWS] = {9, 8, 7, 6};
byte colPins[COLS] = {5, 4, 3, 2};

// Create keypad object
Keypad customKeypad = Keypad(makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);

// Player Constants
const int PLAYER_NONE = 0;
const int PLAYER_1 = 1;
const int PLAYER_YOU = 2;

// Game Variables
int board[BUTTON_ROWS][BUTTON_COLS];
int currentPlayer;
bool gameOver;

void setup() {
 // Initialize LCD Display
 lcd.begin(20, 4);
 lcd.backlight();
 lcd.print("Welcome To My Game, ICT Online");
 delay(2000);
 lcd.clear();

 // Initialize Game Variables
 currentPlayer = PLAYER_1;
 gameOver = false;
 resetBoard();
 updateDisplay();
}

void loop() {
 // Check for Button Press
 char keypress = customKeypad.getKey();
  
 if (keypress == '0') {
  setup();
 }
  
 if (!gameOver) {
  if (keypress <= 57 && keypress >= 49) {
   int number = keypress - 57 + 9;

   int i = (number - 1) / 3;
   int j = ((number - 1) % 3);

   if (board[i][j] == PLAYER_NONE) {
    board[i][j] = currentPlayer;
    updateDisplay();
    checkWinCondition();
    switchPlayers();
   }
   delay(200); // Button Rebound
  }
 }
}

void resetBoard() {
 for (int i = 0; i < BUTTON_ROWS; i++) {
  for (int j = 0; j < BUTTON_COLS; j++) {
   board[i][j] = PLAYER_NONE;
  }
 }
}

void updateDisplay() {
 lcd.clear();
 lcd.setCursor(0, 0);
 lcd.print("Player ");
 lcd.print(currentPlayer == PLAYER_1 ? "1" : "YOU");
 lcd.setCursor(0, 1);
 delay(1);
 for (int i = 0; i < BUTTON_ROWS; i++) {
  lcd.setCursor(0, i + 1);
  for (int j = 0; j < BUTTON_COLS; j++) {
   lcd.print("|");
   lcd.print(symbolForPlayer(board[i][j]));
  }
  lcd.print("|");
 }
// lcd.setCursor(0, 5);
// lcd.print("-------------");
}

char symbolForPlayer(int player) {
 if (player == PLAYER_1) {
  return 'X';
 } else if (player == PLAYER_YOU) {
  return 'O';
 }
 return ' ';
}

void switchPlayers() {
 currentPlayer = (currentPlayer == PLAYER_1) ? PLAYER_YOU : PLAYER_1;
}

void checkWinCondition() {
 // Check Rows
 for (int i = 0; i < BUTTON_ROWS; i++) {
  if (board[i][0] != PLAYER_NONE && board[i][0] == board[i][1] && board[i][1] == board[i][2]) {
   gameOver = true;
   lcd.setCursor(0, 4);
   lcd.print("Player ");
   lcd.print(symbolForPlayer(board[i][0]));
   lcd.print(" wins!");
   return;
  }
 }

 // Check Columns
 for (int j = 0; j < BUTTON_COLS; j++) {
  if (board[0][j] != PLAYER_NONE && board[0][j] == board[1][j] && board[1][j] == board[2][j]) {
   gameOver = true;
   lcd.setCursor(0, 4);
   lcd.print("Player ");
   lcd.print(symbolForPlayer(board[0][j]));
   lcd.print(" wins!");
   return;
  }
 }

 // Check Diagonals
 if (board[0][0] != PLAYER_NONE && board[0][0] == board[1][1] && board[1][1] == board[2][2]) {
  gameOver = true;
  lcd.setCursor(0, 4);
  lcd.print("Player ");
  lcd.print(symbolForPlayer(board[0][0]));
  lcd.print(" wins!");
  return;
 }

 if (board[0][2] != PLAYER_NONE && board[0][2] == board[1][1] && board[1][1] == board[2][0]) {
  gameOver = true;
  lcd.setCursor(0, 4);
  lcd.print("Player ");
  lcd.print(symbolForPlayer(board[0][2]));
  lcd.print(" wins!");
  return;
 }

 // Check Draw
 bool draw = true;
 for (int i = 0; i < BUTTON_ROWS; i++) {
  for (int j = 0; j < BUTTON_COLS; j++) {
   if (board[i][j] == PLAYER_NONE) {
    draw = false;
    break;
   }
  }
  if (!draw) {
   break;
  }
 }
 if (draw) {
  gameOver = true;
  lcd.setCursor(0, 4);
  lcd.print("It's a draw!");
 }
}