import { GameObject } from "../../GameObject/GameObject";
import { addGameObjectToGameState } from "../../GameState/GameState";
import { BC_CAMERA, BC_CHUNKS_SETTINGS, BC_TERRAIN_SETTINGS, BC_VIEWPORT } from "../../GlobalVariables/GlobalVariables";
import { PointInt2D } from "../../Utils/Math.utils";
import { NavigationGridChunk } from "../../World/NavigationGrid/NavigationGrid";
import { Chunk } from "../Chunk/Chunk";
import { Terrain } from "../Terrain/Terrain";
import { Tile } from "../Tiles/Tile";

export class TerrainManger {
    /**
     * @type {Terrain}
     */
    currentTerrain;

    /**
     * @type {Array<Chunk>}
     */
    #chunkFillingQueue = [];

    /**
     * @type {Chunk}
     */
    #focusedChunk = null;
    #chunkVisibilityPeriod = 0.3;
    #currentChunkVisibilityState = 0.0;

    chunkVisibilityOptions = {
        min: -2,
        max: 3,
    };

    /**
     *
     * @returns {Terrain}
     */
    createNewTerrain() {
        let t = new Terrain(false);
        addGameObjectToGameState(t);
        return t;
    }

    /**
     * 
     * @param {Terrain} terrain 
     */
    changeTerrain(terrain)
    {
        BC_VIEWPORT.removeChild(this.currentTerrain.drawObject);
        BC_VIEWPORT.addChild(terrain.drawObject);
        this.currentTerrain = terrain;
    }

    /**
     *
     * @param {PointInt2D} pivot
     * @returns {Chunk}
     */
    createNewChunk(pivot) {
        let t = new Chunk(false, pivot);
        addGameObjectToGameState(t);
        t.navigationChunk = new NavigationGridChunk();
        return t;
    }

    tickHandler(ticker) {
        // console.log(this.currentTerrain);
        if (this.#chunkFillingQueue.length > 0) {
            this.currentTerrain.fillChunk(this.#chunkFillingQueue.pop());
        }
        this.#currentChunkVisibilityState += ticker.deltaMS / 1000;
        if (this.#currentChunkVisibilityState >= this.#chunkVisibilityPeriod) {
            this.handleChunkVisibility();
            this.#currentChunkVisibilityState = 0.0;
        }
    }

    /**
     * adds chunk to terrain. Chunk object must contain pivot coordinates. This function is a part of terrain manager interface.
     * @param {Chunk} chunk
     */
    addChunk(chunk) {
        this.currentTerrain.addChunk(chunk);
        if (!chunk.filled) this.#chunkFillingQueue.push(chunk);
    }

    /**
     * 
     * @param {PointInt2D} point 
     */
    getChunkAt(point)
    {
        let w = BC_CHUNKS_SETTINGS.width * BC_TERRAIN_SETTINGS.totalSize;
        let h = BC_CHUNKS_SETTINGS.height * BC_TERRAIN_SETTINGS.totalSize;
        let chunkId = Math.floor(point.getX() / w) + "_" + Math.floor(point.getY() / h);
        return this.currentTerrain.chunkStorage.get(chunkId);
    }

    /**
     * update tile in terrain
     * @param {Tile} tile new tile
     * @param {Number} layerId chunk layer id
     * @param {PointInt2D} coordinates world coordinates
     */
    updateTile(tile, layer, coordinates) {}

    addEntity() {}

    /**
     *
     * @param {PointInt2D} point in world coordinates
     * @param {Number} layerId chunk layer id
     * @returns {Tile | undefined}
     */
    getTileAt(point, layerId) {
        return this.currentTerrain.getTileAt(point, layerId);
    }

    handleChunkVisibility() {
        let w = BC_CHUNKS_SETTINGS.width * BC_TERRAIN_SETTINGS.tileSize * BC_TERRAIN_SETTINGS.scale;
        let h = BC_CHUNKS_SETTINGS.height * BC_TERRAIN_SETTINGS.tileSize * BC_TERRAIN_SETTINGS.scale;
        let cx = Math.floor(BC_CAMERA.position.x / w);
        let cy = Math.floor(BC_CAMERA.position.y / h);
        let chunkId = cx + "_" + cy;
        if (!this.#focusedChunk || !GameObject.isEqual(this.#focusedChunk, this.currentTerrain.chunkStorage.get(chunkId))) {
            for (const i of this.currentTerrain.visibleChunks) {
                i[1].changeVisibility(false);
            }
            this.currentTerrain.visibleChunks.clear();
            let t_chunkId;
            let t_chunk;
            for (let i = this.chunkVisibilityOptions.min; i < this.chunkVisibilityOptions.max; i++) {
                for (let j = this.chunkVisibilityOptions.min; j < this.chunkVisibilityOptions.max; j++) {
                    t_chunkId = cx + i + "_" + (cy + j);
                    if (this.currentTerrain.chunkStorage.has(t_chunkId)) {
                        t_chunk = this.currentTerrain.chunkStorage.get(t_chunkId);
                        t_chunk.changeVisibility(true);
                        if (i === 0 && j === 0) this.#focusedChunk = t_chunk;
                        this.currentTerrain.visibleChunks.set(t_chunkId, t_chunk);
                    } else {
                        t_chunk = this.createNewChunk(new PointInt2D(cx + i, cy + j));
                        this.#chunkFillingQueue.push(t_chunk);
                        if (i === 0 && j === 0) this.#focusedChunk = t_chunk;
                        this.currentTerrain.addChunk(t_chunk);
                        this.currentTerrain.visibleChunks.set(t_chunkId, t_chunk);
                    }
                }
            }
        }
    }
}
