Render_TableroGrafico.js

import { Eventos } from "../Events.js";
import { EventBus } from "../EventBus.js";
import { turnoJugador } from "../Logica/Turno.js";
import Celda from "../Logica/Celda.js";

/**
 * Gestión gráfica del tablero principal: crea rectángulos por casilla,
 * pinta rangos de movimiento, fragmentos de mapa y gestiona interacciones.
 * @class TableroGrafico
 * @memberof Render
 */
class TableroGrafico {
    /**
     * Constructor de la capa gráfica del tablero.
     * @param {Phaser.Scene} escena - escena de Phaser donde se dibuja
     * @param {Tablero} tablero - instancia de la lógica del tablero
     * @param {PanelLateral} PanelLateral - panel lateral para mostrar confirmaciones
     * @param {number} [tamCasilla=64] - tamaño en píxeles de cada casilla
     */
    constructor(escena, tablero, PanelLateral, PanelEventos, tamCasilla = 64) {
        this.escena = escena;
        this.tablero = tablero;
        this.tamCasilla = tamCasilla;
        this.graficos = this.dibujarTablero(); //Este tablero visual esta lleno de "rects" //Phaser.GameObjects.Rectangle

        this.mapaTopografico = this.escena.textures.get('mapaTopo').getSourceImage();
        this.mapaSatelital = this.escena.textures.get('mapaSat').getSourceImage();

        this.mapaTopograficoWidth = this.mapaTopografico.width;
        this.mapaTopograficoHeight = this.mapaTopografico.height;

        this.fragmentoAncho = this.mapaTopograficoWidth / this.tablero.columnas;
        this.fragmentoAlto = this.mapaTopograficoHeight / this.tablero.filas;

        this.celdaSeleccionada = null; // La celda que estas seleccionando
        this.celdasColoreadas = []; // Las celdas a las que te puedes mover
        this.PanelLateral = PanelLateral;
        this.panelEventos = PanelEventos;

        // Separar capas de selección/movimiento de capas de eventos
        this.casillasPintadas = [];      // Para selección/movimiento (se limpian con limpiarTablero)
        this.casillasEventos = [];       // Para eventos (persisten hasta limpiarEventos)
        this.casillasFichasMovidas = []; // Para piezas movidas (persisten hasta resetear turno)

        //Si se esta moviendo
        this.moviendoPieza = false;

        EventBus.on(Eventos.PIECE_END_ACTIONS, () => {
            this.restTablero();
        });

        EventBus.on(Eventos.CHANGE_TURN, () => {
            this.resetCasillasFichasMovidas();
        });

        for (let fila = 0; fila < this.tablero.filas; fila++) {
            for (let col = 0; col < this.tablero.columnas; col++) {

                if (col < 3) {
                    this.dibujarFragmentoMapa(fila, col, "J1")
                }
                else if (col > 6) {
                    this.dibujarFragmentoMapa(fila, col, "J2")
                }
            }
        }
    }

    /**
     * Dibuja la malla gráfica del tablero: rectángulos interactivos por casilla.
     * @returns {Array<Array<Phaser.GameObjects.Rectangle>>} matriz de rectángulos que representan las celdas
     */
    dibujarTablero() {
        let graficos = [];

        for (let fila = 0; fila < this.tablero.filas; fila++) {
            graficos[fila] = [];
            for (let col = 0; col < this.tablero.columnas; col++) {
                const color = (fila + col) % 2 === 0 ? 0xffffff : 0xcccccc;
                //los rectangulos se empiezan a dibujar desde el centro (por eso, +tamCasillas/2)
                const x = col * this.tamCasilla + this.tamCasilla / 2;
                const y = fila * this.tamCasilla + this.tamCasilla / 2;

                // new Rectangle(scene, x, y, [width], [height], [fillColor], [fillAlpha])
                const rect = this.escena.add.rectangle(
                    x, y, this.tamCasilla, this.tamCasilla, color
                ).setStrokeStyle(1, 0x000000)
                    .setInteractive();

                // Detectar click
                rect.on('pointerdown', () => {
                    this.onCeldaClick(fila, col);
                });

                graficos[fila][col] = rect;
            }
        }

        return graficos;
    }

    /**
     * Manejador de clic en una celda gráfica.
     * Determina acciones (seleccionar pieza, mover, atacar o disparar artillería).
     * @param {number} fila - fila de la celda clicada
     * @param {number} col - columna de la celda clicada
     */
    onCeldaClick(fila, col) {
        if (this.panelEventos.getInput()) {
            const celda = this.tablero.getCelda(fila, col);
            const pieza = celda.getPieza();
            const jugador = pieza ? pieza.getJugador() : "";

            // Si no hay celda seleccionada y no esta vacía se marcan las oppciones de la pieza
            if (pieza && this.celdaSeleccionada == null && !this.moviendoPieza && !pieza.getMovida() && jugador == turnoJugador) {
                // Si la celda contiene una pieza
                if (!celda.estaVacia()) {
                    this.colorearRango(fila, col);
                }
            }
            else {
                // Como ya hay una celda seleccionada, vemos si la nueva celda es vacía o enemigo, para ver si movemos o atacamos
                if (!this.tablero.getPiezaActiva()) return;

                //Si es artilleria va aparte
                if (this.tablero.getPiezaActiva().getTipo() === "Artilleria" && this.tablero.getPiezaActiva().puedeDisparar() && this.esTipoCelda(fila, col)) {

                    this.tablero.getPiezaActiva().lanzarProyectil(fila, col, this.escena, this.tablero);
                    EventBus.emit(Eventos.PIECE_MOVED, this.tablero.getPiezaActiva(), false);

                    this.limpiarTablero();
                    this.tablero.resetPiezaActiva();
                }
                // Si es vacía se mueve
                else if (this.esTipoCelda(fila, col, "vacia") && !this.tablero.getPiezaActiva().getMovida() && this.tablero.getPiezaActiva().getJugador() == turnoJugador) {
                    //Dibuja la conquista
                    this.dibujarFragmentoMapa(fila, col, this.tablero.getPiezaActiva().getJugador())

                    this.moviendoPieza = true;
                    //Se limpia el tablero
                    this.limpiarTablero();

                    //Se informa del movimiento de pieza
                    this.tablero.moverPieza(fila, col);
                    if (this.tablero.getPiezaActiva() && !this.tablero.getPiezaActiva().getMovida()) {
                        this.colorearRango(fila, col);
                    }
                }
                else if (this.esTipoCelda(fila, col, "enemigo") && !this.tablero.getPiezaActiva().getMovida() && this.tablero.getPiezaActiva().getJugador() == turnoJugador) {
                    this.moviendoPieza = false;

                    // Combate
                    this.confirmarAtaque(fila, col, this.celdaSeleccionada);
                    // Posible Ataque si se confirma en el panel Lateral
                    this.tablero.ataque(fila, col);
                }
                else if (!this.moviendoPieza) {
                    this.limpiarTablero();
                    this.celdaSeleccionada = null;
                }
            }
        }
    }

    /**
     * Colorea el rango de movimiento/ataque de la pieza situada en (fila,col).
     * Marca la casilla de la pieza y añade capas para las celdas alcanzables.
     * @param {number} fila - fila de la pieza seleccionada
     * @param {number} col - columna de la pieza seleccionada
     */
    colorearRango(fila, col) {
        let celda = this.tablero.getCelda(fila, col);

        this.limpiarCapas();

        this.celdasColoreadas = this.tablero.piezaSeleccionada(fila, col);
        //La de la ficha actual
        this.graficos[fila][col].setStrokeStyle(3, 0xf5a927);
        this.casillasPintadas.push(this.crearCapa(fila, col, 0xffc107, 0.3));

        for (let cel of this.celdasColoreadas) {
            if (cel.tipo == "vacia") {
                this.graficos[cel.fil][cel.col].setStrokeStyle(3, 0x69CF4E);
                this.casillasPintadas.push(this.crearCapa(cel.fil, cel.col, 0x00ff00, 0.3));
            }
            else if (cel.tipo == "enemigo") {
                this.graficos[cel.fil][cel.col].setStrokeStyle(3, 0xF23A1D);
                this.casillasPintadas.push(this.crearCapa(cel.fil, cel.col, 0xff0000, 0.3));
            }
        }

        this.celdaSeleccionada = celda;
    }

    /**
     * Crea una capa coloreada sobre una celda.
     * @param {number} fila - fila
     * @param {number} col - columna
     * @param {number|string} color - color en formato hexadecimal (0x...)
     * @param {number} transparencia - nivel de alfa (0.0 - 1.0)
     * @returns {Phaser.GameObjects.Rectangle} la capa creada
     */
    crearCapa(fila, col, color, transparencia) {
        const x = col * this.tamCasilla + this.tamCasilla / 2;
        const y = fila * this.tamCasilla + this.tamCasilla / 2;
        const capa = this.escena.add.rectangle(x, y, this.tamCasilla, this.tamCasilla, color, transparencia);
        return capa;
    }

    /**
     * Limpia todas las capas coloreadas del tablero.
     */
    limpiarCapas() {
        this.casillasPintadas.forEach(o => o.destroy());
        this.casillasPintadas = [];
    }

    /**
     * Comprueba si una coordenada dada está entre las celdas coloreadas y, opcionalmente, si coincide el tipo.
     * @param {number} fil - fila objetivo
     * @param {number} col - columna objetivo
     * @param {string} [tipo=""] - tipo de celda a comprobar ('vacia'|'enemigo')
     * @returns {boolean} true si la celda coincide con alguna de las coloreadas
     */
    esTipoCelda(fil, col, tipo = "") {
        for (let celda of this.celdasColoreadas) {
            if (tipo == "") {
                if (celda.fil == fil && celda.col == col) return true;
            }
            else {
                if (celda.fil == fil && celda.col == col && celda.tipo == tipo) return true;
            }
        }

        return false;
    }

    /**
     * Resetea las casillas coloreadas y elimina la selección actual.
     * Restaura el estilo por defecto de las casillas afectadas.
     */
    limpiarTablero() {

        this.limpiarCapas();
        let i = 0;
        // Descolorear las anteriores
        for (let i = 0; i < this.celdasColoreadas.length; i++) {
            let { fil, col } = this.celdasColoreadas[i];
            this.graficos[fil][col].setStrokeStyle(1, 0x000000);
        }

        if (this.celdaSeleccionada) {
            //Desmarcamos la casilla central
            let f = this.celdaSeleccionada.getPosicion().fila;
            let c = this.celdaSeleccionada.getPosicion().col;
            this.graficos[f][c].setStrokeStyle(1, 0x000000);
        }

        this.celdasColoreadas = [];
        EventBus.emit(Eventos.CLEAN_SIDE_PANEL);
    }

    /**
     * Confirma un ataque entre la pieza seleccionada y la celda objetivo.
     * @param {number} fila - fila de la celda objetivo
     * @param {number} columna - columna de la celda objetivo
     * @param {Celda} celdaSeleccionada - celda de la pieza atacante
     */
    confirmarAtaque(fila, columna, celdaSeleccionada) {

        let casillaAtacante = this.tablero.getCelda(celdaSeleccionada.fila, celdaSeleccionada.columna);
        let casillaDefensa = this.tablero.getCelda(fila, columna);
        let atacante = casillaAtacante.getPieza().getJugador();
        let defensa = casillaDefensa.getPieza().getJugador();
        let atacantePieza = this.tablero.getCelda(celdaSeleccionada.fila, celdaSeleccionada.columna).getPieza().getTipo();
        let defensaPieza = this.tablero.getCelda(fila, columna).getPieza().getTipo();
        this.PanelLateral.updateInfo(defensaPieza, atacantePieza, atacante, defensa, "Atacar", casillaAtacante, casillaDefensa);
    }

    /**
     * Dibuja un fragmento del mapa (topográfico o satelital) dentro de una celda.
     * @param {number} fila - fila de la celda
     * @param {number} col - columna de la celda
     * @param {string} tipoJugador - 'J1' o 'J2' para elegir mapa
     */
    dibujarFragmentoMapa(fila, col, tipoJugador) {
        // Determina qué mapa usar
        const key = tipoJugador === 'J1' ? 'mapaTopo' : 'mapaSat';

        const textura = this.escena.textures.get(key).getSourceImage();

        const cropX = col * this.fragmentoAncho;
        const cropY = fila * this.fragmentoAlto;

        const x = col * this.tamCasilla + this.tamCasilla / 2;
        const y = fila * this.tamCasilla + this.tamCasilla / 2;

        // Borra la imagen anterior si existe
        if (this.graficos[fila][col].imagen && this.graficos[fila][col].imagen.mapKey != key) {
            this.graficos[fila][col].imagen.destroy();
            this.tablero.conquistarCelda(tipoJugador, true);
        }
        else if (!this.graficos[fila][col].imagen) {
            this.tablero.conquistarCelda(tipoJugador, false);
        }

        const zoom = 1.3;
        const renderSize = this.tamCasilla * zoom;

        // Crea un RenderTexture que actúa como "mini lienzo" para la celda
        const rt = this.escena.add.renderTexture(x, y, renderSize, renderSize)
            .setOrigin(0.5)
            .setDepth(0);

        // Escala proporcional al fragmento del mapa
        const scaleX = renderSize / this.fragmentoAncho;
        const scaleY = renderSize / this.fragmentoAlto;

        // Dibuja el fragmento del mapa en el renderTexture escalado a la celda
        rt.draw(key, -cropX * scaleX, -cropY * scaleY, key)
            .setScale(scaleX, scaleY);
        rt.mapKey = key;

        this.graficos[fila][col].imagen = rt;
    }

    /**
     * Borra el fragmento de mapa renderizado en una celda y actualiza contadores.
     * @param {number} fila - fila de la celda
     * @param {number} col - columna de la celda
     * @param {string} jugadorAnterior - 'J1' o 'J2' que había conquistado la casilla
     */
    borrarFragmentoMapa(fila, col, jugadorAnterior) {
        // Verificar que existe imagen
        if (!this.graficos[fila][col].imagen) return;

        // Destruir la imagen del mapa
        this.graficos[fila][col].imagen.destroy();
        this.graficos[fila][col].imagen = null;
        this.tablero.borrarCelda(jugadorAnterior);
    }

    /**
     * Colorea una celda específica con un color dado y nivel de alfa.
     * @param {number} fila - fila de la celda
     * @param {number} col - columna de la celda
     * @param {number|string} color 
     * @param {number} alpha 
     */
    coloreaCelda(fila, col, color, alpha = 0.45) {
        const capa = this.crearCapa(fila, col, color, alpha);
        capa.setDepth(9); // Solo aquí usamos setDepth
        this.casillasEventos.push({ capa, fila, col });
    }

    /**
     * Limpia los eventos gráficos asociados a las casillas.
     */
    limpiarEventos() {
        this.casillasEventos.forEach(obj => obj.capa.destroy());
        this.casillasEventos = [];
    }

    /**
     * Resetea el estado del tablero gráfico.
     * Desactiva movimientos y selecciones actuales.
     */
    restTablero() {
        this.moviendoPieza = false;
        this.movimientoIniciado = false;

        this.limpiarTablero();
        if (this.tablero.getPiezaActiva()) this.marcarCasillaMovida(this.tablero.getPiezaActiva().fil, this.tablero.getPiezaActiva().col);
        this.celdaSeleccionada = null;
    }

    /**
     * Desactiva la interactividad de todas las casillas del tablero.
     */
    desactivarTablero() {
        for (let fila = 0; fila < this.tablero.filas; fila++) {
            for (let col = 0; col < this.tablero.columnas; col++) {
                this.graficos[fila][col].disableInteractive();
            }
        }
    }

    /**
     * Desactiva la interacción con el tablero (para turno de IA)
     */
    desactivarInteraccion() {
        for (let fila = 0; fila < this.tablero.filas; fila++) {
            for (let col = 0; col < this.tablero.columnas; col++) {
                this.graficos[fila][col].disableInteractive();
            }
        }
    }

    /**
     * Activa la interacción con el tablero (para turno de jugador)
     */
    activarInteraccion() {
        for (let fila = 0; fila < this.tablero.filas; fila++) {
            for (let col = 0; col < this.tablero.columnas; col++) {
                this.graficos[fila][col].setInteractive();
            }
        }
    }

    /**
     * Marca una casilla como movida, añadiendo una capa amarilla semi-transparente.
     * @param {number} fila - fila
     * @param {number} col - columna
     */
    marcarCasillaMovida(fila, col) {
        const capa = this.crearCapa(fila, col, 0x3b3b3b3b, 0.5);
        capa.setDepth(8);
        this.casillasFichasMovidas.push({ capa, fila, col });
    }

    /**
     * Resetea las casillas marcadas como movidas.
     */
    resetCasillasFichasMovidas() {
        this.casillasFichasMovidas.forEach(obj => obj.capa.destroy());
        this.casillasFichasMovidas = [];
        this.tablero.resetPiezaActiva();
    }
}

export default TableroGrafico;