#include <WiFiNINA.h>
#include <LiquidCrystal_I2C.h>

char ssid[] = "Arduino_Server";
char pass[] = "12345678";

WiFiServer server(80);

struct Cliente {
  WiFiClient client;
  String nombre;
  int id;
  int estadoBoton = 0;
  bool activo = false;
};

struct DatosCliente {
  int idCliente;
  int dato;
};

const int MAX_CLIENTES = 5;
Cliente clientes[MAX_CLIENTES];
DatosCliente datos[MAX_CLIENTES];

bool aceptarNuevosClientes = true;

int keys = 0, carrera = 0, recorrido = 0, recorrido_azar = 0, JUEGO4 = 0;
bool recorrido_finalizado = false;
bool azar_finalizado = false;
bool carrera_finalizada = false;

int posicion_array = 0;
int dist[4];
float velocidad = 0;
unsigned long t_inicio = 0;
unsigned long tiempo = 0;

const int ROWS = 4;
const int COLS = 4;

LiquidCrystal_I2C lcd(0x27, 16, 2);

char keysMatrix[ROWS][COLS] = {
  {'1','2','3','A'},
  {'4','5','6','B'},
  {'7','8','9','C'},
  {'*','0','#','D'}
};

int rowPins[ROWS] = {2, 3, 4, 5}; //a los D_X
int colPins[COLS] = {6, 7, 8, 9}; 
//4 pines: ground, 3.3, sda, scl

void setup() {
  Serial.begin(115200);
  lcd.init();
  lcd.backlight();
  while (!Serial);

  if (!WiFi.beginAP(ssid, pass)) {
    Serial.println("❌ Error al crear AP");
    while (true);
  }

  while (WiFi.status() != WL_AP_LISTENING) {
    delay(100);
  }

  server.begin();
  Serial.println("✅ Servidor iniciado");

  for (int col = 0; col < COLS; col++) {
    pinMode(colPins[col], INPUT_PULLUP);
  }
}

void loop() {
  WiFiClient nuevo = server.available();
  if (nuevo && aceptarNuevosClientes && nuevo.connected()) {
    int slot = -1;
    for (int i = 0; i < MAX_CLIENTES; i++) {
      if (!clientes[i].activo) {
        slot = i;
        break;
      }
    }
    if (slot != -1) {
      clientes[slot].client = nuevo;
      clientes[slot].activo = true;
      clientes[slot].nombre = "";
      clientes[slot].estadoBoton = 0;

      Serial.print("🔗 Cliente conectado desde IP: ");
      Serial.println(nuevo.remoteIP());
    } else {
      nuevo.stop(); 
    }
  }

  for (int i = 0; i < MAX_CLIENTES; i++) {
    if (clientes[i].activo) {
      WiFiClient &c = clientes[i].client;

      if (!c.connected()) {
        Serial.print("❌ Cliente desconectado: Cono_");
        Serial.println(clientes[i].id);
        c.stop();
        clientes[i].activo = false;
        clientes[i].nombre = "";
      }

      if (c.available()) {
        String msg = c.readStringUntil('\n');
        msg.trim();

        Serial.print("📥 Mensaje recibido de Cono_");
        Serial.print(clientes[i].id);
        Serial.print(": ");
        Serial.println(msg);

        if (msg.startsWith("NOMBRE:") && clientes[i].nombre == "") {
          String nombre = msg.substring(7);
          if (!nombre.startsWith("Cono_")) {
            c.println("NOMBRE_INVALIDO");
            c.stop();
            clientes[i].activo = false;
            continue;
          }

          bool repetido = false;
          for (int j = 0; j < MAX_CLIENTES; j++) {
            if (i != j && clientes[j].activo && clientes[j].nombre == nombre) {
              repetido = true;
              break;
            }
          }
          if (repetido) {
            c.println("DUPLICADO");
            c.stop();
            clientes[i].activo = false;
          } else {
            clientes[i].nombre = nombre;
            clientes[i].id = extraerID(nombre);
            c.println("ID_CONFIRMADO");
            datos[i].idCliente = clientes[i].id;
            datos[i].dato = 0;
          }
        } else if (msg.startsWith("ESTADO:")) {
          int nuevoEstado = msg.substring(7).toInt();
          clientes[i].estadoBoton = nuevoEstado;
          
        } else if (msg.startsWith("[")) {
          interpretarMsg(msg);
        }
      }
    }
  }

  tiempo = millis() - t_inicio;
  bool check = comprobarTodosClientesConValor1(); //ojo la condicion del check

  if (carrera == 1 && comprobarDatoCliente(clientes[0].id) && !carrera_finalizada) {
    int dist_array = dist[0]*100 + dist[1]*10 + dist[2];
    float velocidad = dist_array / (float(tiempo) / 1000);

    Serial.print("🚀 Velocidad: ");
    Serial.print(velocidad, 2);
    Serial.println(" m/s");
    carrera = 0;
    recorrido = 0;
    recorrido_azar = 0;
    JUEGO4 = 0;

    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Modo Carrera");
    lcd.setCursor(1, 1);
    lcd.print("Vel: ");
    lcd.setCursor(5, 1);
    lcd.print(velocidad);
    lcd.setCursor(10, 1);
    lcd.print("m/s");

    check = 0;
    carrera_finalizada = true;
  }

  if (recorrido == 1){ 
    
    if(check){
    carrera = 0;
    recorrido = 0;
    recorrido_azar = 0;
    JUEGO4 = 0;
    recorrido_finalizado = true;
     Serial.print("✅ Recorrido completo en: ");
    Serial.print(tiempo / 1000);
    Serial.println(" s");

    float x = tiempo / 1000;
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Modo Recorrido");
    lcd.setCursor(0, 1);
    lcd.print("Tiempo: ");
    lcd.setCursor(9, 1);
    lcd.print(x);
    lcd.setCursor(12, 1);
    lcd.print("s");
    }
  }

  if (recorrido_azar == 1 && check && !azar_finalizado){
    Serial.print("🎲 Recorrido azar completo en: ");
    Serial.print(tiempo / 1000);
    Serial.println(" s");
    carrera = 0;
    recorrido = 0;
    recorrido_azar = 0;
    JUEGO4 = 0;

    float x = tiempo / 1000;
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Modo Aleatorio");
    lcd.setCursor(0, 1);
    lcd.print("Tiempo: ");
    lcd.setCursor(9, 1);
    lcd.print(x);
    lcd.setCursor(12, 1);
    lcd.print("s");
    check = 0;
    azar_finalizado = true;
  }

  escanearTeclado();
}
void escanearTeclado() {
  for (int row = 0; row < ROWS; row++) {
    pinMode(rowPins[row], OUTPUT);
    digitalWrite(rowPins[row], LOW);

    for (int col = 0; col < COLS; col++) {
      if (digitalRead(colPins[col]) == LOW) {
        char tecla = keysMatrix[row][col];
        while (digitalRead(colPins[col]) == LOW) {
          delay(10);
        }
        interpretarTecla(tecla);
      }
    }

    pinMode(rowPins[row], INPUT);
  }
}

void interpretarTecla(char tecla) {
  if (tecla == '#') {
    aceptarNuevosClientes = false;
    int activos = contarActivos();
    for (int i = 0; i < MAX_CLIENTES; i++) {
      if (clientes[i].activo) {
        clientes[i].client.println("CLIENTES:" + String(activos));
      }
    }
    Serial.print("📤 Enviado CLIENTES: ");
    Serial.println(activos);

  } else if (tecla == 'A') {
    enviarATodos("LED_ON");
    carrera_finalizada = false;
    for (int i = 0; i < MAX_CLIENTES; i++) {
      if (clientes[i].activo) {
        String array = "[" + String(clientes[i].id) + ",0]";
        clientes[i].client.println(array);
      }
    }
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Modo Carrera");
    lcd.setCursor(0, 1);
    lcd.print("Sel. distancia");

    keys = 1;
    carrera = 1;
    recorrido = 0;
    recorrido_azar = 0;
    JUEGO4 = 0;
    posicion_array = 0;

  } else if (tecla == 'B') {
    for (int i = 0; i < MAX_CLIENTES; i++) {
      if (clientes[i].activo) {
        String array = "[" + String(clientes[i].id) + ",0]";
        clientes[i].client.println(array);
      }
    }
    carrera_finalizada = false;
    enviarATodos("LED_ON");
    recorrido = 1;
    carrera = 0;
    recorrido_azar = 0;
    JUEGO4 = 0;
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Modo Recorrido");
    lcd.setCursor(1, 1);
    lcd.print("* Para empezar");

    
  } else if (tecla == 'C') {
    for (int i = 0; i < MAX_CLIENTES; i++) {
      if (clientes[i].activo) {
        String array = "[" + String(clientes[i].id) + ",0]";
        clientes[i].client.println(array);
      }
    }
    carrera_finalizada = false;
    enviarAzar("LED_ON");
    recorrido_azar = 1;
    carrera = 0;
    recorrido = 0;
    JUEGO4 = 0;
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Modo Aleatorio");
    lcd.setCursor(1, 1);
    lcd.print("* Para empezar");
    
  } else if (tecla == 'D') {//por desarrollar
    t_inicio = millis();
    for (int i = 0; i < MAX_CLIENTES; i++) {
      if (clientes[i].activo) {
        String array = "[" + String(clientes[i].id) + ",0]";
        clientes[i].client.println(array);
      }
    }
  } else if (tecla == '*') {
    if (carrera == 1) {
      t_inicio = millis();
      int dist_array = dist[0]*100 + dist[1]*10 + dist[2];
      Serial.print("📏 Distancia configurada: ");
      Serial.println(dist_array);

      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Modo Carrera");
      lcd.setCursor(1, 1);
      lcd.print("Dist ");
      lcd.setCursor(8, 1);
      lcd.print(dist_array);
      lcd.setCursor(12, 1);
      lcd.print("m");

    } else if (recorrido == 1 || recorrido_azar == 1) {
      t_inicio = millis();
    }

    // ✅ Al iniciar cualquier juego, encender LED en cada cliente
    for (int i = 0; i < MAX_CLIENTES; i++) {
      if (clientes[i].activo) {
        String array = "[" + String(clientes[i].id) + ",0]";
        clientes[i].client.println(array);
      }
    }

  } else if (keys == 1 && posicion_array < 4 && isDigit(tecla)) {
    dist[posicion_array] = tecla - '0';
    posicion_array++;
  }
}

void enviarATodos(String mensaje) {
  for (int i = 0; i < MAX_CLIENTES; i++) {
    if (clientes[i].activo) {
      clientes[i].client.println(mensaje);
    }
  }
}

void enviarAzar(String mensaje) {
  int activos[MAX_CLIENTES];
  int totalActivos = 0;
  for (int i = 0; i < MAX_CLIENTES; i++) {
    if (clientes[i].activo) {
      activos[totalActivos++] = i;
    }
  }
  if (totalActivos > 0) {
    int elegido = random(0, totalActivos);
    
    clientes[activos[elegido]].client.println(mensaje);
    Serial.print("🎲 LED aleatorio → Cono_");
    Serial.println(clientes[activos[elegido]].id);

  }
}

void interpretarMsg(String mensaje) {
  if (!mensaje.startsWith("[") || !mensaje.endsWith("]")) return;
  mensaje.remove(0, 1);
  mensaje.remove(mensaje.length() - 1);
  int coma = mensaje.indexOf(',');
  int idCliente = mensaje.substring(0, coma).toInt();
  int dato = mensaje.substring(coma + 1).toInt();

  for (int i = 0; i < MAX_CLIENTES; i++) {
    if (clientes[i].activo && clientes[i].id == idCliente) {
      datos[i].idCliente = idCliente;
      datos[i].dato = dato;
      Serial.print("✅ Estado actualizado de Cono_");
      Serial.print(idCliente);
      Serial.print(" → ");
      Serial.println(dato);
      break;
    }
  }
}
bool comprobarDatoCliente(int idCliente) {
  for (int i = 0; i < MAX_CLIENTES; i++) {
    if (datos[i].idCliente == idCliente && clientes[i].estadoBoton == 1) {
      return true;
    }
  }
  return false;

}

bool comprobarTodosClientesConValor1() {
  for (int i = 0; i < MAX_CLIENTES; i++) {
    if (clientes[i].activo && clientes[i].estadoBoton != 1) {
      return false;
    }
  }
  return true;
}

int contarActivos() {
  int count = 0;
  for (int i = 0; i < MAX_CLIENTES; i++) {
    if (clientes[i].activo) {
      count++;
    }
  }
  return count;
}

int extraerID(String nombre) {
  if (nombre.startsWith("Cono_")) {
    return nombre.substring(5).toInt();
  }
  return 0;
}

