/src/gamestate.ts
import GameMap from './map';
import CellEditor, { CellEditorCallback } from './celleditor';
import ObjectEditor from './objecteditor';
import MapView from './mapview';
import TextView from './textview';
import Actions from './actions';

import { ID } from './id';
import { GameEvent, GameEventType } from './event';
import { GameObject, GameObjectProperties } from './object';
import { GameAgent } from './agent';

/* GameState

This is the root object for all things the game needs to know about.  It tracks
Maps, Objects, Events, and Agents.

Unlike all other objects, GameState instantiated and made available as a
default export for convenient access from anywhere.  Because of this, it does
not have a normal constructor and it should be initialized after page load with
the init() method.

*/


interface Globals {
    objects: GameObjectProperties[]
    inventory: object
}

class GameState {
    map: GameMap;
    events: Map<ID, GameEvent>;
    objects: Map<ID, GameObject>;
    agents: Map<ID, GameAgent>; 
    playerInventory: Map<ID, number>;

    textView: TextView;
    cellEditor: CellEditor;
    objectEditor: ObjectEditor;
    mapView: MapView;
    actions: Actions;
    editMode: boolean = true;

    init() {
        this.events = new Map();
        this.objects = new Map();
        this.agents = new Map();
        this.map = new GameMap();
        this.playerInventory = new Map();

        document.body.classList.toggle('edit-mode', this.editMode);

        this.textView = new TextView();
        this.cellEditor = new CellEditor();
        this.objectEditor = new ObjectEditor();
        this.mapView = new MapView();
        this.mapView.updateAll();
        this.actions = new Actions();

        if (localStorage['map']) {
            this.map.load(JSON.parse(localStorage.map));
        }
        if (localStorage['globals']) {
            this.loadGlobals(JSON.parse(localStorage.globals));
        }

        this.enterCell();
    }

    print(...args: string[]) {
        this.textView.print(...args);
    }

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

    movePlayer(x: number, y: number) {
        this.map.movePlayer(x, y);
        this.enterCell();
    }

    printCellText() {
        const cell = this.map.getCurrentCell();
        gs.textView.clear();
        gs.textView.print("You have entered the", cell.title);
        gs.textView.print();
        gs.textView.print(cell.description);
    }

    showCellActions() {
        const cell = this.map.getCurrentCell();

        this.actions.clear();
        for (let [k, e] of cell.events) {
            if (e.type == GameEventType.Action) {
                this.actions.addAction(e.title, e.getCallback());
            }
        }

        if (this.editMode) {
            this.actions.addAction('Toggle Navigable', () => this.map.toggleNavigable());
            this.actions.addAction('Edit Cell', () => this.map.editDescription());
            this.actions.addAction('Object Editor', () => this.objectEditor.open());
        }
    }

    enterCell() {
        this.printCellText();
        this.showCellActions();
    }

    addObject(o: GameObject) {
        this.objects.set(o.id, o);
    }

    removeObject(o: GameObject | ID) {
        if (o instanceof GameObject) {
            this.objects.delete(o.id);
        } else {
            this.objects.delete(o);
        }
    }

    addInventory(o: ID, count=1) {
        if (this.playerInventory.has(o)) {
            const currentCount = this.playerInventory.get(o);
            this.playerInventory.set(o, currentCount + count);
        } else {
            this.playerInventory.set(o, count);
        }
    }

    storeGlobals(): Globals {
        const objects: GameObjectProperties[] = [];
        for (let [k, o] of this.objects) {
            objects.push(o.store());
        }

        const inventory = Object.fromEntries(this.playerInventory);

        return { objects, inventory };
    }

    loadGlobals(globals: Globals) {
        if (globals.objects) {
            for (let o of globals.objects) {
                const obj = new GameObject();
                obj.load(o);
                this.addObject(obj);
            }
        }

        if (globals.inventory) {
            this.playerInventory = new Map(Object.entries(globals.inventory));
        }
    }
}

// Create a default GameState object and export it
const gs = new GameState();
export default gs;