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

class ActionGo extends Action {
    public static check = new Rulebook<ActionGo>("Check Going");
    public static carry = new Rulebook<ActionGo>("Carry out Going");

    public roomGoneFrom : Room;
    public roomGoneTo : Room;
    public originalTarget : Room | number;

    public constructor (actor : Thing, ...nouns : Array<any>) {
        super(actor, ...nouns);
        this.originalTarget = nouns[0];
        this.requiresNoun = false;
        this.requiresVisibility = false;
    }

    /**
     * Needs to return a string explaining what the player will do if he does this action.
     * For instance, ActionTaking should return something like return "take " + this.nouns[0].getName(),
     *   which would read as "take thing".
     *   remember that things implement PRINTABLE interface, so you can get their names.
     * @returns {string}
     */
    public getCommandText () {
        let name;
        if (typeof this.originalTarget == "number") {
            name = DirectionNames[Direction[this.originalTarget]];
        } else if (this.originalTarget instanceof Room) {
            name = "to " + (<Room> this.originalTarget).getPrintedName();
        }
        return "go " + name;
    }

    public static ruleCheckRestoreOriginalNoun = ActionGo.check.createAndAddRule({
        firstPriority : Rule.PRIORITY_HIGHEST,
        priority : Rule.PRIORITY_HIGHEST,
        name : "Check Going - Restore original noun",
        code : (rulebook) => {
            let action = rulebook.noun;
            action.setNoun(0, action.originalTarget);
        }
    });

    public static ruleCheckIsthereactor = ActionGo.check.createAndAddRule({
        firstPriority : Rule.PRIORITY_HIGHEST,
        priority : Rule.PRIORITY_HIGHEST,
        name : "Check Going - Is there an actor?",
        code : (rulebook) => {
            let action = <ActionGo> rulebook.noun;
            if (action.actor == undefined) {
                return false; // Rule fails
            }
        }
    });

    public static ruleCheckConvertRoomToDirection = ActionGo.check.createAndAddRule({
        firstPriority : Rule.PRIORITY_HIGHEST,
        priority : Rule.PRIORITY_HIGH,
        name : "Change Room to Direction",
        code : (rulebook) => {
            let action = <ActionGo> rulebook.noun;

            // Someone asked for a room...
            if (action.getNoun(0) instanceof RoomRandom) {
                let actor = action.actor;
                let cRoom = actor.getRoom();
                if (cRoom instanceof RoomRandom) {
                    let dRoom = action.getNoun(0);

                    if (cRoom == dRoom) {
                        if (actor.isPlayer()) {
                            action.say.add("You are already there!");
                        }
                        action.stop();
                        return false;
                    }

                    let code;

                    if (actor == WorldState.player) {
                        code = (room: Room) => {
                            return WorldState.isRoomRemembered(room);
                        }
                    }

                    let direction = cRoom.getAStarBestDirectionTo(dRoom, code);
                    if (direction == undefined) {
                        if (actor.isPlayer()) {
                            action.say.add("You don't remember how to get there.");
                        }
                        return false;
                    } else {
                        action.setNoun(0, direction);
                    }
                }
            }
        }
    });

    public static ruleCheckIstheredirectionandroom = ActionGo.check.createAndAddRule({
        name : "Check Going - is there a direction? Does it lead anywhere?",
        code : (rulebook) => {
            let action = <ActionGo> rulebook.noun;
            let cRoom = action.actor.getRoom();
            if (cRoom == undefined) {
                return false; // Rule fails, actor is out of world
            }
            let direction = action.getNoun(0);
            if (direction == undefined) {
                return false; // Rule fails, no direction
            }
            let nextRoom = cRoom.connections[direction];
            if (nextRoom == undefined) {
                return false; // Rule fails, can't go direction that doesn't have a room
            }

            action.roomGoneFrom = action.actor.getRoom();
            action.roomGoneTo = nextRoom;
        }
    });

    public static ruleCarryMove = ActionGo.carry.createAndAddRule({
        name : "Going - Move Actor to Next Room",
        code : (rulebook) => {
            let action = <ActionGo> rulebook.noun;
            let roomGoneFrom = action.actor.getRoom();
            let direction = action.getNoun(0);
            let roomGoneInto = roomGoneFrom.connections[direction];
            roomGoneInto.place(action.actor);

            let actor = action.actor;
            if (actor == WorldState.player) {
                action.say.add("You go " + DirectionNames[Direction[direction]].toLowerCase() + ".");
            } else {
                if (roomGoneFrom == WorldState.player.getRoom()) {
                    action.say.add(new SayThe(), actor, " goes " + DirectionNames[Direction[direction]].toLowerCase() + ".");
                } else {
                    let oppositeName = DirectionNames[
                        OppositeDirection[direction]
                    ];
                    action.say.add(new SayThe(), actor, " arrives from the " + oppositeName.toLowerCase() + ".");
                }
            }
        }
    });

    public static ruleCarryRememberRooms = ActionGo.carry.createAndAddRule({
        name : "Going - Remember the involved rooms",
        code : (rulebook) => {
            let action = <ActionGo> rulebook.noun;
            let actor = action.actor;
            let roomGoneInto = action.actor.getRoom();
            let direction = action.getNoun(0);
            let roomGoneFrom = roomGoneInto.connections[OppositeDirection[Direction[direction]]];

            if (actor.isPlayer()) {
                WorldState.rememberRoom(roomGoneFrom, roomGoneInto);
            }
        }
    });
}