import State from './state';
import Room from './room';
import Player from './player';
import Output from './output';
import Input from './input';
import Commands from './commands';
import Serialization from './serialization';
import MobileCommands from './mobile-commands';

export default class Game {
    constructor(newGame = true) {
        this.newGame = newGame;
        this.player = new Player();
        this.state = new State();
        this.rooms = [];
        this.items = [];
        this.output = new Output();
        this.commandHandler = new Commands(this);
        this.input = new Input(this.commandHandler, this.output);
        this.mobileCommands = null;
        this.visitedRooms = new Map();
        this.interval = null;
        this.Serialization = new Serialization(this);
    }

    print(string) {
        this.output.say(string);
    }

    async tell(lines, time) {
        for (let line of lines) {
            this.print(line);
            await this.wait(time);
        }
    }

    init(data) {
        this.rooms = data.rooms.map((room) => {
            room.context = this;
            return room;
        });
        this.items = data.items.map((item) => {
            item.context = this;
            return item;
        });
        this.state = data.state || new State();
        this.commandHandler.addCommands(data.commands);
        this.player = new Player();
        this.player.context = this;
        if (this.newGame) {
            this.move(this.player.currentRoom);
        } else {
            this.Serialization.load();
        }
        this.start();
    }

    advanceTick() {
        this.items.forEach((item) => item.onTick());
        this.rooms.forEach((room) => room.onTick());
    }

    start() {
        this.interval = setInterval(() => this.advanceTick(), 1000);
    }

    stop() {
        clearInterval(this.interval);
        this.interval = null;
    }

    examineRoom() {
        const room = this.getRoom(this.player.currentRoom);
        this.output.say(room.title);
        if (!this.visitedRooms.get(this.player.currentRoom) && room.firstDescription != "") {
            this.output.say(room.firstDescription);
        } else {
            this.output.say(room.description);
        }
        this.examineItems();
        this.examineExits();
    }

    examineItems() {
        const room = this.getRoom(this.player.currentRoom);
        const items = room.getItems();
        if (items.length < 1) return;
        let itemDescription = `You see `;
        items.forEach((item, index) => {
            if (index < items.length - 2) {
                itemDescription += `${item.name}, `;
            } else if (index < items.length - 1) {
                itemDescription += `${item.name} and `;
            } else {
                itemDescription += item.name
            }
        });
        this.output.say(itemDescription + ".");
    }

    examineExits() {
        const room = this.getRoom(this.player.currentRoom);
        let exits = [];
        let exitDescription = "You can go ";
        const exitKeys = room.exits.keys();
        for (let exit of exitKeys) {
            exits.push(exit);
        }
        exits.forEach((item, index) => {
            if (index < exits.length - 2) {
                exitDescription += `${item}, `;
            } else if (index < exits.length - 1) {
                exitDescription += `${item} and `;
            } else {
                exitDescription += item
            }
        });
        this.output.say(exitDescription + ".");
    }

    getRoom(id) {
        return this.rooms.find((room) => room.id == id);
    }

    getItem(id) {
        return this.items.find((item) => item.getID() == id);
    }

    wait(ms) {
        return new Promise((resolve, reject) => {
            setTimeout(resolve, ms);
        });
    }

    async move(roomID) {
        const currentRoom = this.getRoom(this.player.currentRoom);
        const newRoom = this.getRoom(roomID);
        if (currentRoom.canExit() && newRoom.canEnter()) {
            await currentRoom.onExit();
            await newRoom.onEnter();
            this.player.currentRoom = roomID;
            this.examineRoom();
            this.visitedRooms.set(roomID, true);
            
            // Refresh mobile commands when room changes
            this.commandHandler.refreshMobileCommands();
        }
    }

    enableCommandInput(value) {
        this.commandHandler.enabled = value;
    }

    setInputEcho(value) {
        this.input.setEcho(value);
    }

    save() {
        this.Serialization.save();
    }

    load() {
        this.Serialization.load();
    }
}