/// <reference path="./Classes/Rulebook.ts" />
/// <reference path="./Classes/Rule.ts" />

module TurnSequence {
    export var rulebook = new Rulebook<Action>("Turn Sequence");
    export var playerActions = [];

    export let lastTurnTime : number = 0;

    export async function execute (action? : Action) {
        // Only one action at a time
        if (playerActions.push(action) == 1) {
            let t0 = performance.now();
            console.debug(Rulebook.getIndentation() + "   Player Action: " + (action ? action.getCommandText() : "none"));
            await rulebook.execute({
                noun: action
            });

            playerActions = [];
            let t1 = performance.now();
            lastTurnTime = t1 - t0;
            console.debug("Total: " + (t1 - t0) + " milliseconds.");
            if (Settings.sayTurnTime) {
                Elements.CurrentTurnHandler.printAsContent(new Say(new SayBold("Time taken for turn: "), (t1 - t0), " milliseconds."));
            }
        }
    }

    /**
     * This is the Prepare Elements for the turn rule.
     * @type {Rule}
     */
    export var PrepareElementsRule = new Rule({
        firstPriority: Rule.PRIORITY_HIGHEST, // This will be about the first Rule to be executed
        priority: Rule.PRIORITY_MEDIUM, // This needs to be done before the majority of the rules, but we don't want it to be the very first no matter what
        name: "Begin Turn (Elements Side)",
        code: function (runner : RulebookRunner<Action>) {
            Elements.CurrentTurnHandler.startTurn(runner.noun);
        }
    });
    TurnSequence.rulebook.addRule(PrepareElementsRule);

    /**
     * This is the Do Player Action Rule.
     * @type {Rule}
     */
    export let PlayerActionRule = new Rule({
        firstPriority: Rule.PRIORITY_HIGH,
        priority: Rule.PRIORITY_MEDIUM,
        name: "Do Player Action",
        code: async (rulebook : RulebookRunner<Action>) => {
            let playerAction = <Action> rulebook.noun;
            if (playerAction != undefined) {
                let promise = playerAction.execute();

                await promise;

                Elements.CurrentTurnHandler.printAsContent(playerAction.say);

                if (playerAction.requiresTurn) {
                    WorldState.incrementPlayerTurn();
                }
            }
        }
    });
    rulebook.addRule(PlayerActionRule);

    /**
     * This is the Run Every Turn Rules.
     * @type {Rule}
     */
    export var RunEveryTurnRulesRule = new Rule({
        firstPriority: Rule.PRIORITY_MEDIUM,
        priority: Rule.PRIORITY_MEDIUM,
        name: "Run Every Turn Rules",
        code: async function () {
            while (WorldState.isTurnWaiting()) {
                await EveryTurn.EveryTurn.execute({});
            }
        }
    });
    rulebook.addRule(RunEveryTurnRulesRule);

    /**
     * This is the Inform Elements the turn has ended rule.
     * @type {Rule}
     */
    export var RemoveTurnFromElementsRule = new Rule({
        firstPriority: Rule.PRIORITY_LOWEST, // This will be about the last rule to be executed
        priority: Rule.PRIORITY_MEDIUM, // This needs to be done after the majority of the rules, but we don't care if it's the very last or not
        name: "End Turn (Elements/Controls Side)",
        code: async function (rulebook : RulebookRunner<Action>) {
            Elements.CurrentTurnHandler.endTurn();

            Controls.KeyHandler.reset();

            await Elements.HyperlinkHandler.hyperlinkCommonActions();
            await Elements.RoomHandler.updateRoom();
            await Elements.RememberedHandler.updateMap();

            let playerAction = <Action> rulebook.noun;
            if (playerAction) {
                await Elements.HyperlinkHandler.hyperlinkObject(playerAction.getNoun(0));
            } else {
                await Elements.HyperlinkHandler.hyperlinkObject();
            }

            await Elements.InventoryHandler.updateInventory();
            await Elements.AppearanceHandler.updateAppearance();
            //await Elements.StatusLineHandler.updateStatusLine();
        }
    });
    TurnSequence.rulebook.addRule(RemoveTurnFromElementsRule);
}