/src/map.ts
import gs from './gamestate';
import { ID, generateID } from './id';
import { GameObject, GameObjectType } from './object';
import { Cell, CellProperties } from './cell';
import Storable from './storable';

/* GameMap

The Map tracks positions of all the objects on the map, including the player.

*/

interface GameMapProperties {
    id: ID
    width: number
    height: number
    cells: CellProperties[]
}

export default class GameMap extends Storable {
    id: ID
    width: number
    height: number
    cells: Cell[][]
    playerLocation: [number, number]
    saveProperties = ['id', 'width', 'height']

    constructor() {
        super();
        this.id = generateID();
        this.width = this.height = 1;
        this.build();
    }

    build() {
        this.cells = this.cells || [];
        for (let j = 0; j < this.height; j++) {
            this.cells[j] = this.cells[j] || [];

            for (let i = 0; i < this.width; i++) {
                if (this.cells[j][i])
                    continue;
                this.cells[j][i] = new Cell(i, j);
            }
        }

        if (!this.player) {
            this.playerLocation = [0, 0];
        }
    }

    movePlayer(x: number, y: number) {
        const [ oldX, oldY ] = this.playerLocation;
        let rebuild = false;

        if (x + 1 > this.width) {
            this.width = x + 1;
            rebuild = true;
        } else if (x < 0) {
            x = 0;
        }

        if (y + 1 > this.height) {
            this.height = y + 1;
            rebuild = true;
        } else if (y < 0) {
            y = 0;
        }

        if (x == oldX && y == oldY)
            return;

        if (rebuild)
            this.build();

        this.playerLocation = [x, y];

        gs.mapView.updateAll();
        gs.mapView.updateCoords(x, y);
    }

    movePlayerRel(dx: number, dy: number) {
        const [ x, y ] = this.playerLocation;
        this.movePlayer(x + dx, y + dy);
    }

    getPlayerPosition(): [number, number] {
        const [ x, y ] = this.playerLocation;
        return [ x, y ];
    }

    getCurrentCell(): Cell {
        const [ x, y ] = this.playerLocation;
        return this.cells[y][x];
    }

    toggleNavigable() {
        const [ x, y ] = this.playerLocation;
        this.cells[y][x].navigable = !this.cells[y][x].navigable;
        gs.mapView.update(x, y);
    }

    editDescription() {
        gs.cellEditor.onClose = (cell: Cell) => {
            gs.mapView.update(cell.x, cell.y);
            gs.enterCell();
        };
        gs.cellEditor.open(this.getCurrentCell());
    }

    store() {
        const obj = <GameMapProperties>super.store();

        const cells: CellProperties[] = [];
        for (let j = 0; j < this.height; j++) {
            for (let i = 0; i < this.width; i++) {
                if (this.cells[j][i].navigable) {
                    cells.push(this.cells[j][i].store());
                }
            }
        }

        obj.cells = cells;

        return obj;
    }

    load(obj: GameMapProperties) {
        super.load(obj);
        this.build();

        for (let c of obj.cells) {
            const { x, y } = c;
            this.cells[y][x].load(c);
            const p = Array.from(this.cells[y][x].objects).find(
                ([id, obj]) => obj.type == GameObjectType.Player
            );
        }

        gs.mapView.updateAll();
    }
}