#include <FastLED.h>
//For Chess pieces
#define EMPTY 0
#define PAWN 1
#define KNIGHT 2
#define BISHOP 3
#define ROOK 4
#define QUEEN 5
#define KING 6
// Use negative values for black pieces
//Tracker variables for movement
int pickedUpRow = -1;
int pickedUpCol = -1;
bool isPiecePickedUp = false;

//switch tracker
byte prevSwitchStates[8][8] = {{0}}; // Initialize all to 0


#define NUM_LEDS 64 // Define the number of LEDs
#define LED_DATA_PIN 6 // The data pin for the LED strip
#define LED_TYPE WS2812B // Type of LED strip
#define LED_COLOR_ORDER GRB // Color order for the LED strip

// Pin definitions for the HC165 shift register
#define SHIFT_REGISTER_PIN_DATA 12 // Data pin
#define SHIFT_REGISTER_PIN_CLOCK 13 // Clock pin
#define SHIFT_REGISTER_PIN_LATCH 9 // Latch pin

CRGB leds[NUM_LEDS]; // Array to hold the LEDs

void setup() {
  // Set the pin modes for the shift register pins
  pinMode(SHIFT_REGISTER_PIN_DATA, INPUT);
  pinMode(SHIFT_REGISTER_PIN_CLOCK, OUTPUT);
  pinMode(SHIFT_REGISTER_PIN_LATCH, OUTPUT);

  // Initialize the LED strip
  FastLED.addLeds<LED_TYPE, LED_DATA_PIN, LED_COLOR_ORDER>(leds, NUM_LEDS);

  // Initialize serial communication at 9600 bits per second
  Serial.begin(9600);
  
}

// Define a new 8x8 matrix for LED control, initially filled with 1's
byte ledControlMatrix[8][8] = {0};

  
//Define a matrix for Chess board

byte chessBoard[8][8] = {
  {ROOK, KNIGHT, BISHOP, QUEEN, KING, BISHOP, KNIGHT, ROOK},
  {PAWN, PAWN, PAWN, PAWN, PAWN, PAWN, PAWN, PAWN},
  {EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY},
  {EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY},
  {EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY},
  {EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY},
  {-PAWN, -PAWN, -PAWN, -PAWN, -PAWN, -PAWN, -PAWN, -PAWN},
  {-ROOK, -KNIGHT, -BISHOP, -QUEEN, -KING, -BISHOP, -KNIGHT, -ROOK}
};
//Logic for movements updating
void updateChessBoard(int oldRow, int oldCol, int newRow, int newCol) {
    int piece = chessBoard[oldRow][oldCol];
    chessBoard[newRow][newCol] = piece; // Move the piece to the new position
    chessBoard[oldRow][oldCol] = EMPTY; // Set the old position to empty
    updateLEDsFromMatrix(); // Optional: Update LED display to reflect new board state
}

//logic for updating lights
void updateLEDsFromMatrix() {
    for (int row = 0; row < 8; row++) {
        for (int col = 0; col < 8; col++) {
            int ledIndex = (row % 2 == 0) ? (row * 8 + (7 - col)) : (row * 8 + col);
            leds[ledIndex] = ledControlMatrix[row][col] ? CRGB::Green : CRGB::Black;
        }
    }
    FastLED.show();
}

//Logic for pawn
void showPossibleMovesForPawn(int row, int col, int piece) {
    //Clear last highlight
    memset(ledControlMatrix, 0, sizeof(ledControlMatrix)); 
    
    // Determine pawn direction based on its color
    int direction = (piece > 0) ? -1 : 1; // White pawns move up (-1), Black pawns move down (+1)
  
    // Standard move (one square forward)
    int nextRow = row + direction;
    if (nextRow >= 0 && nextRow < 8 && chessBoard[nextRow][col] == EMPTY) {
        ledControlMatrix[nextRow][col] = 1; // Highlight this square
  
        // Check for the initial two-square move
        if ((piece > 0 && row == 6) || (piece < 0 && row == 1)) {
            int twoSquaresForward = row + 2 * direction;
            if (chessBoard[twoSquaresForward][col] == EMPTY) {
                ledControlMatrix[twoSquaresForward][col] = 1; // Highlight this square
            }
        }
        Serial.print("I am Working");
    }
    // This function assumes the existence of updateLEDsFromMatrix() which updates the actual LEDs from ledControlMatrix
    updateLEDsFromMatrix();
}


void loop() {
    digitalWrite(SHIFT_REGISTER_PIN_LATCH, LOW);
    delayMicroseconds(5);
    digitalWrite(SHIFT_REGISTER_PIN_LATCH, HIGH);
    delayMicroseconds(5);

    // Reading from the shift registers into an array
    byte switchStates[8];
    for (int i = 0; i < 8; ++i) {
        switchStates[i] = shiftIn(SHIFT_REGISTER_PIN_DATA, SHIFT_REGISTER_PIN_CLOCK, MSBFIRST);
    }

    // Check for changes in the switch matrix and identify the chess piece
    for (int i = 0; i < 8; ++i) {
        for (int j = 0; j < 8; ++j) {
            byte currentState = bitRead(switchStates[7-i], j);
            if (currentState != prevSwitchStates[i][j]) { // Detect change
                prevSwitchStates[i][j] = currentState; // Update previous state

                Serial.print(currentState == 1 ? "Piece picked up at: " : "Piece placed down at: ");
                Serial.print(i);
                Serial.print(", ");
                Serial.println(j);

                if (currentState == 1) { // Piece picked up
                    pickedUpRow = i;
                    pickedUpCol = j;
                    isPiecePickedUp = true;
                } else if (isPiecePickedUp && currentState == 0) { // Piece placed down
                    updateChessBoard(pickedUpRow, pickedUpCol, i, j);
                    isPiecePickedUp = false;
                }
                
                // Identifying the chess piece type
                int piece = chessBoard[i][j]; // Identify the chess piece
                
                Serial.print("Piece type: ");
                if (piece > 0) {
                    Serial.print("White ");
                } else if (piece < 0) {
                    Serial.print("Black ");
                }
                piece = abs(piece); // Make piece positive to check type
                switch (piece) {
                    case PAWN: Serial.println("Pawn"); break;
                    case KNIGHT: Serial.println("Knight"); break;
                    case BISHOP: Serial.println("Bishop"); break;
                    case ROOK: Serial.println("Rook"); break;
                    case QUEEN: Serial.println("Queen"); break;
                    case KING: Serial.println("King"); break;
                    default: Serial.println("Empty");
                }
                if (piece == PAWN) {
                    showPossibleMovesForPawn(i, j, piece);
                }
            }
        }
    }
    // Update and compare the matrix to detect changes and identify chess pieces
    // The provided code goes here, replacing the example change detection and identification logic

    // Create an 8x8 matrix and populate it with switch states
    byte matrix[8][8];
    for (int i = 0; i < 8; ++i) {
        for (int j = 0; j < 8; ++j) {
            matrix[i][j] = bitRead(switchStates[7-i], j); // Each row corresponds to a shift register
        }
    }

    // Print the matrix to Serial Monitor
    Serial.println("Switch Matrix:");
    for (int i = 0; i < 8; i++) {
        for (int j = 0; j < 8; j++) {
            Serial.print(matrix[i][j]);
            Serial.print(" ");
        }
        Serial.println();
    }
    Serial.println();
    delay(500);
}

// Custom shiftIn function to read the state of the shift register
byte shiftIn(byte dataPin, byte clockPin, byte bitOrder){
  byte value = 0;
  for (int i = 0; i < 8; ++i) {
    digitalWrite(clockPin, HIGH);
    delayMicroseconds(5); // Ensure a stable clock pulse
    if (bitOrder == LSBFIRST) {
      value |= digitalRead(dataPin) << i;
    } else {
      value |= digitalRead(dataPin) << (7 - i);
    }
    digitalWrite(clockPin, LOW);
    delayMicroseconds(5); // Ensure a stable clock pulse
  }
  return value;
}
74HC165
74HC165
74HC165
74HC165
74HC165
74HC165
74HC165
74HC165