Logica_ColocarPiezas.js

import Soldado from "./Piezas/Soldado.js";
import Caballeria from "./Piezas/Caballeria.js";
import Comandante from "./Piezas/Comandante.js";
import Artilleria from "./Piezas/Artilleria.js";
import Celda from "./Celda.js";
import { Eventos } from "../Events.js";
import { EventBus } from "../EventBus.js";
import Equipo from "./Equipo.js";
import Tablero from "./Tablero.js";
import Pieza from "./Pieza.js";

/**
 * Clase que gestiona la colocación de piezas en el tablero.
 * @class ColocarPiezas
 * @memberof Logica
 * @extends Tablero
 */
class ColocarPiezas {
    /**
     * Constructor de la clase ColocarPiezas.
     * @param {Equipo} equipo1 - primer equipo
     * @param {Equipo} equipo2 - segundo equipo
     * @param {number} _filas - número de filas del tablero
     * @param {number} _columnas - número de columnas del tablero
     */
    constructor(equipo1, equipo2, _filas = 8, _columnas = 10) {
        this.filas = _filas;
        this.columnas = _columnas;
        this.tablero = this.crearTablero(); //creamos el tablero lleno de celdas

        this.equipo1 = equipo1;
        this.equipo2 = equipo2;

        this.celdasJ1 = 0;
        this.celdasJ2 = 0;

        this.equipoActual = this.equipo1;

        this.tipo;

        EventBus.on(Eventos.CHANGE_TEAM_SET_PIECES, ()=> {
            this.cambiarEquipo();
        });
    }

    /**
     * Crea el tablero lleno de celdas.
     * @returns {Tablero}
     */
    crearTablero() {
        let tab = [];
        for (let i = 0; i < this.filas; i++) {
            tab[i] = [];
            for (let j = 0; j < this.columnas; j++) {
                tab[i][j] = new Celda(i, j);
            }
        }
        return tab;
    }

    /**
     * Devuelve la celda en la posición (fila, columna).
     * @param {number} fila - fila
     * @param {number} columna - columna
     * @returns {Celda} - celda en la posición (fila, columna)
     */
    getCelda(fila, columna) {
        return this.tablero[fila][columna];
    }

    /**
     * Genera una pieza del tipo seleccionado / especificado en la posición (fil, col).
     * @param {number} fil - fila
     * @param {number} col - columna
     * @param {string} p - tipo de pieza
     */
    generarPieza(fil, col, p=null) {
        let nombre = this.equipoActual.getNombre();
        let pieza = null;
        let tipo;
        if(!p) tipo = this.tipo;
        else tipo = p;
        if (tipo == 'Soldado' && this.equipoActual.getSoldados() > 0)  {
            pieza =  new Soldado(this.tablero, fil, col, nombre);
            this.equipoActual.setSoldado(pieza);
        }
        else if (tipo == 'Artilleria' && this.equipoActual.getArtilleria() > 0) {
            pieza = new Artilleria(this.tablero, fil, col, nombre);
            this.equipoActual.setArtilleria(pieza);
        }
        else if (tipo == 'Caballeria' && this.equipoActual.getCaballeria() > 0) {
            pieza = new Caballeria(this.tablero, fil, col, nombre);
            this.equipoActual.setCaballeria(pieza);
        }
        else if (tipo == 'Comandante' && this.equipoActual.getComandante() > 0){
            pieza = new Comandante(this.tablero, fil, col, nombre);
            this.equipoActual.setComandante(pieza);
        }
        this.tablero[fil][col].setContenido(pieza);
        if (pieza != null)EventBus.emit(Eventos.PIECE_POSITION, pieza); // On ElegirPiezaEscena - Inicio
        return pieza;
    }

    /**
     * Genera una pieza del tipo seleccionado / especificado en la posición (fil, col) - del equipo contrario
     * @param {number} fil - fila
     * @param {number} col - columna
     * @param {string} p - tipo de pieza
     */
    generarPiezaEnemiga(fil, col, p=null) {
        console.log(fil, col);
        let nombre;
        nombre = ((this.equipoActual === this.equipo1)) ? this.equipo2.getNombre() : this.equipo1.getNombre();
        console.log(nombre);
        let pieza = null;
        let tipo;

        let casillas = this.piezaSeleccionada(fil, col);
        console.log(casillas);
        let select = false;
        for (let i=0; i < casillas.length && !select; i++)
        { console.log(casillas[i].tipo);
            if (casillas[i].tipo == "vacia") {
            fil = casillas[i].fil;
            col = casillas[i].col;
            console.log(i, fil, col);
            select = true;}
        }
        if(this.tablero[fil][col].estaVacia()) {
        if(!p) tipo = this.tipo;
        else tipo = p;
        if (tipo == 'Soldado' && this.equipoActual.getSoldados() > 0)  {
            pieza =  new Soldado(this.tablero, fil, col, nombre);
        }
        else if (tipo == 'Artilleria' && this.equipoActual.getArtilleria() > 0) {
            pieza = new Artilleria(this.tablero, fil, col, nombre);
        }
        else if (tipo == 'Caballeria' && this.equipoActual.getCaballeria() > 0) {
            pieza = new Caballeria(this.tablero, fil, col, nombre);
        }
        else if (tipo == 'Comandante' && this.equipoActual.getComandante() > 0){
            pieza = new Comandante(this.tablero, fil, col, nombre);
        }
        this.tablero[fil][col].setContenido(pieza);
        if (pieza != null){EventBus.emit(Eventos.PIECE_POSITION, pieza); // On ElegirPiezaEscena - Inicio
        ((this.equipoActual === this.equipo1)) ? this.equipo2.piezas.push(pieza) : this.equipo1.piezas.push(pieza);}
        return pieza;}
    }

    /**
     * Genera soldados en la cruz de ataque de la artilleria, empezando por la fila y columna especificadas (fil, col) - del equipo contrario
     * @param {number} fil - fila
     * @param {number} col - columna
     */
    generarPiezaEnemigaArtilleria(fil, col) 
    {
        let nombre;
        nombre = ((this.equipoActual === this.equipo1)) ? this.equipo2.getNombre() : this.equipo1.getNombre();
        const direcciones = [
            { df: 0, dc: 0 },     // centro
            { df: -1, dc: 0 },  // arriba
            { df: 1, dc: 0 },   // abajo
            { df: 0, dc: -1 },  // izquierda
            { df: 0, dc: 1 }    // derecha
        ];
        let fila ;
        let columna;
        for(let i=0; i<5;i++){
            fila = fil + direcciones[i].df;
            columna = col + direcciones[i].dc;
            let pieza = new Soldado(this.tablero, fila, columna, nombre);
            this.tablero[fila][columna].setContenido(pieza);
        if (pieza != null){EventBus.emit(Eventos.PIECE_POSITION, pieza); // On ElegirPiezaEscena - Inicio
        ((this.equipoActual === this.equipo1)) ? this.equipo2.piezas.push(pieza) : this.equipo1.piezas.push(pieza);
        }   
        }
    }

    /**
     * Elimina la pieza en la posición (fil, col).
     * @param {number} fil - 
     * @param {number} col - columna
     * @param {Pieza} pieza - pieza a eliminar
     */
    eliminarPieza(fil, col, pieza) {
        if((this.equipoActual === this.equipo1 && col < 3) || (this.equipoActual === this.equipo2 && col < 10 && col > 6) ) {
        this.equipoActual.eliminarPieza(pieza);
        this.tablero[fil][col].setContenido(null);
        EventBus.emit(Eventos.PIECE_DELETE, pieza); }// On ElegirPiezaEscena - Inicio
    }

    /**
     * Elimina todas las piezas 
     */
    eliminarTodasLasPiezas(){
        for (let i = 0; i < this.filas; i++) {
            for (let j = 0; j < this.columnas; j++) {
                if (!this.tablero[i][j].estaVacia()) {
                let pieza = this.tablero[i][j].getPieza();
                EventBus.emit(Eventos.PIECE_DELETE, pieza); // On ElegirPiezaEscena - Inicio
                this.tablero[pieza.fil][pieza.col].setContenido(null);
                (pieza.getJugador == 'J2') ? this.equipo2.eliminarPieza(pieza) : this.equipo1.eliminarPieza(pieza);
            }}
        }
        
    }
    
    /**
     * Establece el tipo de pieza a colocar.
     * @param {string} tipo - tipo de pieza a colocar
     */
    setTipo(tipo) {
        this.tipo = tipo;
    }

    /**
     * Obtiene la pieza actualmente seleccionada.
     * @returns {Pieza|null} pieza activa o null si ninguna está seleccionada
     */
    getPiezaActiva() {
        return this.piezaActiva;
    }

    /**
     * Reinicia la pieza activa (deselecciona).
     */
    resetPiezaActiva() {
        this.piezaActiva = null;
    }

    /**
     * Cambia el equipo actual.
     */
    cambiarEquipo() {
        this.equipoActual = (this.equipoActual === this.equipo1) ? this.equipo2 : this.equipo1;
    }

    // Selecciona las casillas de movimiento/ataque de la pieza
    /**
     * Calcula las casillas disponibles para mover o atacar cuando se selecciona una pieza.
     * Devuelve las casillas según el tipo de pieza y su alcance.
     * @param {number} fil - fila de la pieza seleccionada
     * @param {number} col - columna de la pieza seleccionada
     * @returns {Array<Object>} array de objetos con coordenadas y tipo de acción (vacia/enemigo)
     */
    piezaSeleccionada(fil, col) {

        let celda = this.tablero[fil][col];
        let celdasSeleccionadas = [];

        let pieza = celda.getPieza();

        // Si la pieza ya no puede actuar, no devuelvas casillas
        if (!pieza || pieza.getMovida()) return [];

        if (pieza != this.piezaActiva) {
            //Lanzamos el evento de pieza seleccionada
            EventBus.emit(Eventos.PIECE_SELECTED, pieza);
            this.piezaActiva = pieza;
        }

        //if (pieza.getTipo() == "Artilleria" && !pieza.puedeDisparar()) return [];

        // Direcciones cardinales
        const direcciones = [
            { df: -1, dc: 0 },  // arriba
            { df: 1, dc: 0 },   // abajo
            { df: 0, dc: -1 },  // izquierda
            { df: 0, dc: 1 }    // derecha
        ];

        if (pieza.getTipo() == "Artilleria") {
            const jugador = pieza.getJugador();

            let iniCol;
            let maxCol;
            if (jugador === "J1") {
                iniCol = pieza.getPosicion().col + 1; // La siguiente a la artilleria
                maxCol = iniCol + 4;
            }
            else {
                iniCol = pieza.getPosicion().col - 4; // La siguiente a la artilleria
                maxCol = pieza.getPosicion().col;
            }

            for (let col = iniCol; col < maxCol; col++) {
                for (let fil = 0; fil < this.filas; fil++) {
                    let celda = this.tablero[fil][col];

                    let esRival;
                    if (!celda.estaVacia()) esRival = jugador !== celda.getPieza().getJugador();

                    if (esRival) {
                        celdasSeleccionadas.push({ fil: fil, col: col, tipo: "enemigo" });
                    }
                    else {
                        celdasSeleccionadas.push({ fil: fil, col: col, tipo: "vacia" });
                    }
                }
            }

        }
        else if (pieza.getTipo() != "Comandante") { //solo el comandante puede moverse en diagonal
            for (let dir of direcciones) {
                const f = fil + dir.df;
                const c = col + dir.dc;
                // fuera de tablero → deja de mirar en esta dirección
                if (f < 0 || c < 0 || f >= this.filas || c >= this.columnas) continue;

                const cel = this.tablero[f][c];

                if (cel.estaVacia()) {
                    // casilla libre: se puede mover; sigue mirando más lejos
                    celdasSeleccionadas.push({ fil: f, col: c, tipo: "vacia" });
                } else {
                    // hay pieza: si es rival, puedes atacar esa casilla; en ambos casos paras
                    const esRival = cel.getPieza().getJugador() !== celda.getPieza().getJugador();
                    if (esRival) celdasSeleccionadas.push({ fil: f, col: c, tipo: "enemigo" });
                    if (pieza.getTipo() == "Caballeria" && pieza.getSaltoCaballeria()) {

                        const f2 = f + dir.df;
                        const c2 = c + dir.dc;

                        // Comprobar límites correctamente
                        if (f2 >= 0 && f2 < this.filas && c2 >= 0 && c2 < this.columnas) {

                            // Puede saltar solo si la casilla destino está vacía
                            if (this.tablero[f2][c2].estaVacia()) {
                                celdasSeleccionadas.push({ fil: f2, col: c2, tipo: "vacia" });
                            }
                        }
                    }


                }
            }
        }
        else {
            for (let i = col - 1; i <= col + 1; i++) {
                for (let j = fil - 1; j <= fil + 1; j++) {
                    // fuera de tablero → deja de mirar en esta dirección
                    if (j < 0 || i < 0 || j >= this.filas || i >= this.columnas) continue;
                    if (j == fil && i == col) continue; // saltar la casilla central
                    const cel = this.tablero[j][i];

                    if (cel.estaVacia()) {
                        // casilla libre: se puede mover; sigue mirando más lejos
                        celdasSeleccionadas.push({ fil: j, col: i, tipo: "vacia" });
                    } else {
                        // hay pieza: si es rival, puedes atacar esa casilla; en ambos casos paras
                        const esRival = cel.getPieza().getJugador() !== celda.getPieza().getJugador();
                        if (esRival) celdasSeleccionadas.push({ fil: j, col: i, tipo: "enemigo" });
                    }
                }
            }
        }
        return celdasSeleccionadas;
    }

    // Mueve la pieza a fil, col
    /**
     * Mueve la pieza activa a una nueva posición en el tablero.
     * @param {number} fil - fila de destino
     * @param {number} col - columna de destino
     */
    moverPieza(fil, col) {

        //Limpia la celda de origen
        let origen = this.piezaActiva.getPosicion()
        this.tablero[origen.fila][origen.col].limpiar();

        //Añade la pieza a la celda de destino
        this.piezaActiva.moverse(fil, col);
        this.tablero[fil][col].setContenido(this.piezaActiva);

        EventBus.emit(Eventos.PIECE_MOVED, this.piezaActiva, false);
    }

    /**
     * Registra la conquista de una celda por un jugador.
     * Actualiza los contadores de territorio y verifica condición de victoria.
     * @param {string} jugador - identificador del jugador ('J1' o 'J2')
     * @param {boolean} ocupada - indica si la celda estaba previamente ocupada por el enemigo
     */
    conquistarCelda(jugador, ocupada) {
        if (jugador == "J1") {
            this.celdasJ1++;
            if (ocupada) this.celdasJ2--;
        }
        else {
            this.celdasJ2++;
            if (ocupada) this.celdasJ1--;
        }
        let j1Porcentaje = this.celdasJ1 * 100 / 80;
        let j2Porcentaje = this.celdasJ2 * 100 / 80;

        EventBus.emit(Eventos.CONQUER_CELL, j1Porcentaje, j2Porcentaje)

        if (this.celdasJ1 >= 64 || this.celdasJ2 >= 64) EventBus.emit(Eventos.END_GAME, {
            jugador: this.piezaActiva.getJugador(),
            tipo: "TERRITORIO"
        });
    }

    /**
     * Elimina una celda conquistada por el jugador especificado (por lluvia).
     * @param {string} jugadorAnterior - identificador del jugador anterior ('J1' o 'J2')
     */
    borrarCelda(jugadorAnterior) {
        if (jugadorAnterior === 'J1') {
            this.celdasJ1--;
        } else if (jugadorAnterior === 'J2') {
            this.celdasJ2--;
        }

        let j1Porcentaje = this.celdasJ1 * 100 / 80;
        let j2Porcentaje = this.celdasJ2 * 100 / 80;
        EventBus.emit(Eventos.UPDATE_PERCENTAGES, j1Porcentaje, j2Porcentaje);
    }
}

export default ColocarPiezas;