#include <WiFi.h>
#include <ESPAsyncWebServer.h>
#include <map>
#include <array>

const char* ssid = "MIT";
const char* password = <PASSWORD>;

const int redPin = 2;
const int greenPin = 3;
const int bluePin = 4;

// Initial board state (empty tile represented by "")
String grid[3][3] = {
  {"BABA", "IS", "RED"},
  {"GREEN", "BLUE", "YELLOW"},
  {"CYAN", "MAGENTA", ""}
};

int emptyRow = 2;
int emptyCol = 2;

// Map from color names to RGB values
std::map<String, std::array<int, 3>> colorMap = {
    {"RED", {0, 255, 255}},
    {"GREEN", {255, 0, 255}},
    {"BLUE", {255, 255, 0}},
    {"YELLOW", {0, 0, 255}},
    {"CYAN", {255, 0, 0}},
    {"MAGENTA", {0, 255, 0}}
};

AsyncWebServer server(80);

void setup() {
  Serial.begin(9600);
  while (!Serial) {
    delay(100);
  }

  pinMode(redPin, OUTPUT);
  pinMode(greenPin, OUTPUT);
  pinMode(bluePin, OUTPUT);

  // Initialize RGB LED to be off initially
  analogWrite(redPin, 255);
  analogWrite(greenPin, 255);
  analogWrite(bluePin, 255);

  Serial.print("Connecting to WiFi");
  WiFi.begin(ssid, password);

  // Wait for connection
  unsigned long startMillis = millis();
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    if (millis() - startMillis > 30000) {
      Serial.println("\nFailed to connect to Wi-Fi after 30 seconds.");
      return;
    }
  }

  Serial.println("\nConnected to Wi-Fi.");
  Serial.print("IP Address: ");
  Serial.println(WiFi.localIP());

  // Generate game page
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    String html = generateGamePage();
    request->send(200, "text/html", html);
  });

  // Handle tile movement
  server.on("/move", HTTP_GET, [](AsyncWebServerRequest *request){
    String row = request->getParam("row")->value();
    String col = request->getParam("col")->value();
    moveTile(row.toInt(), col.toInt());
    String html = generateGamePage();
    request->send(200, "text/html", html);
  });

  server.begin();
}

void loop() {
}

// Generate HTML for the puzzle
String generateGamePage() {
  String html = "<html><head><style>";
  html += "body { font-family: Arial, sans-serif; background-color: #f0f0f0; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; padding: 20px;}";
  html += "h2 { text-align: center; color: #333; }";
  html += ".grid { display: grid; grid-template-columns: repeat(3, 100px); grid-template-rows: repeat(3, 100px); gap: 10px; }";
  html += ".tile { width: 100px; height: 100px; display: flex; align-items: center; justify-content: center; background-color: #ddd; cursor: pointer; font-size: 16px; text-align: center;}";
  html += ".tile.empty { background-color: #fff; }";
  html += "button { margin-top: 20px; padding: 10px 20px; background-color: #4CAF50; color: white; border: none; border-radius: 5px; cursor: pointer; }";
  html += ".footer { font-size: 14px; color: #777; margin-top: 20px; text-align: center; }";
  html += "</style></head><body>";

  html += "<div class='grid'>";
  
  for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
      String tile = grid[i][j];
      if (tile == "") {
        html += "<div class='tile empty'></div>";
      } else {
        // Call moveTile to update board state and change color if tile clicked
        html += "<div class='tile' onclick='moveTile(" + String(i) + "," + String(j) + ")'>" + tile + "</div>";
      }
    }
  }

  html += "</div>";
  html += "</body><script>";
  html += "function moveTile(row, col) {";
  html += "  window.location.href = '/move?row=' + row + '&col=' + col;";
  html += "}";
  html += "</script>";
  html += "</html>";
  
  return html;
}

// Handle tile movement
void moveTile(int row, int col) {
  // Check if the tile is adjacent to the empty space
  if ((abs(row - emptyRow) == 1 && col == emptyCol) || (abs(col - emptyCol) == 1 && row == emptyRow)) {
    // Swap the clicked tile with the empty space
    grid[emptyRow][emptyCol] = grid[row][col];
    grid[row][col] = "";

    // Update the empty space position
    emptyRow = row;
    emptyCol = col;

    // After each move, check if any row or column spells "BABA IS <COLOR>"
    checkForColorChange();
  }
}

// Check if any row or column spells "BABA IS <COLOR>"
void checkForColorChange() {
  for (int i = 0; i < 3; i++) {
    // Check rows
    if (grid[i][0] == "BABA" && grid[i][1] == "IS" && colorMap.count(grid[i][2]) > 0) {
      setColor(grid[i][2]);
      return;
    }
    // Check columns
    if (grid[0][i] == "BABA" && grid[1][i] == "IS" && colorMap.count(grid[2][i]) > 0) {
      setColor(grid[2][i]);
      return;
    }
  }
  analogWrite(redPin, 255);
  analogWrite(greenPin, 255);
  analogWrite(bluePin, 255);
}

// Set RGB LED color
void setColor(String color) {
  if (colorMap.count(color) > 0) {
    std::array<int, 3> rgb = colorMap[color];
    analogWrite(redPin, rgb[0]);
    analogWrite(greenPin, rgb[1]);
    analogWrite(bluePin, rgb[2]);
  }
}
