var rooma = new RoomRandom("Room A");
rooma.description = new Say(
    "You are inside a box of metal painted blue. Your head almost touches the ceiling, making the room look smaller than it really is.",
    Say.LINE_BREAK,
    new SayIf(
        () => {
            return Thing.InsideRoomRelation.getLeft(vase) == rooma || Thing.InsideRoomRelation.getLeft(urn) == rooma;
        },
        " Still, there are some things thrown about."
    )
);

PlayBegins.setStartingRoom(rooma);

let paddedBra = new Clothing({name : "Padded Bra", unique : true});
paddedBra.breastPadding = 3;
paddedBra.slots = [Humanoid.SLOT_BREASTS];

let paddedUnderwear = new Clothing({name : "Padded Underwear", unique : true});
paddedUnderwear.crotchPadding = 15;
paddedUnderwear.slots = [Humanoid.SLOT_CROTCH_BACK, Humanoid.SLOT_CROTCH_FRONT];

// Thing.WearRelation.setRelation(WorldState.player, paddedBra);
// Thing.WearRelation.setRelation(WorldState.player, paddedUnderwear);

let player = WorldState.player;
WorldState.player = player;
var vase = new Thing({name : "Vase"});
vase.description = new Say("This is an ornamental vase that'd look very nice on your lap. Why? Who knows.");
rooma.place(vase);

let mapOfTest = new MapNote({name: "Map of this Region",
description : "This is a simple map showing all the rooms here.", unique : true});
rooma.place(mapOfTest);

let urn = new Thing({unique: true, name : "Urn of Dreams", image : "image001"});
urn.description = new Say("This appears to be a simple, black urn containing the ashes of your dreams.");
rooma.place(urn);

var roomb = new RoomRandom("Room B");

roomb.place(vase);


let frillyPouch = new CoinPouch({
    name : "Frilly Pouch",
    //image : new SayImage("image001"),
    description : new Say("This is a very gay little pink pouch full of little hearts.")
});
frillyPouch.addCoins(502);
rooma.place(frillyPouch);

// ActionTake.check.addRule(new Rule(<RuleOptions<any>> {
//     code : function () {
//         let promise = new Promise(() => {});
//         return promise;
//     }
// }));

let bigSack = new CoinPouch({name : "Big Sack"});
bigSack.addCoins(100);
rooma.place(bigSack);

let region = new RegionRandom("Test Region");


region.place(rooma, roomb);
for (let i = 1; i < 6; i++) {
    let room = new RoomRandom("Room " + i.toString());
    // let randomDirection = new Shuffler([Room.NORTH, Room.SOUTH, Room.EAST, Room.WEST])
    // room.connectableOn = [randomDirection.getOne()];
    //room.connectableOn = [Room.NORTH, Room.SOUTH, Room.EAST, Room.WEST];
    region.place(room);

    if (Math.random() > 0.5) {
        //mapOfTest.addRoom(room);
    }

}

mapOfTest.addRegion(region);

let southestRoom = new RoomRandom("Southest Room");
//southestRoom.appearChance = 0;

region.place(southestRoom);

//southestRoom.connectableOn = [Room.NORTH];
southestRoom.appearChance = 100;
southestRoom.backgroundImage = "bloo";

southestRoom.trickyCode = (options : TrickyOptions) => {
    let myCoordinates = [options.x, options.y];
    for (let i = 0; i < Room.DIRECTIONS.length; i++) {
        let direction = Room.DIRECTIONS[i];
        if (direction != Direction.NORTH) {
            let coordinates = Room.shift(myCoordinates, direction);
            if (!options.map.isFree(coordinates[0], coordinates[1])) {
                return false; // This room can only have rooms on north of it
            }
            coordinates = Room.shift(coordinates, direction);
            if (!options.map.isFree(coordinates[0], coordinates[1])) {
                return false; // This room can only have rooms on north of it
            }
        }
    }
    return true;
};

RegionRandom.rulebookAfterPlaceRoom.addRule(new Rule({
    name : "After placing the southest room",
    code : runner => {
        let options = <RandomizingRoomOptions> runner.noun;
        let myCoordinates = options.map.getCoordinates(options.room);
        if (myCoordinates != undefined) {
            for (let i = 0; i < Room.DIRECTIONS.length; i++) {
                let direction = Room.DIRECTIONS[i];
                if (direction != Direction.NORTH) {
                    let coordinates = Room.shift(myCoordinates, direction);
                    options.map.block(coordinates[0], coordinates[1]);
                    coordinates = Room.shift(coordinates, direction);
                    options.map.block(coordinates[0], coordinates[1]);
                }
            }
        }
    },
    conditions : runner => {
        return runner.noun.room == southestRoom;
    }
}))

Thing.CarryRelation.setRelation(WorldState.player, mapOfTest);

// Settings.setDebug(false);
PlayBegins.rulebook.addRule(new Rule({
    name : "randomize region",
    firstPriority : Rule.PRIORITY_HIGHEST,
    code : async runner => {
        //let t0 = performance.now();
        await region.randomize();
        //let t1 = performance.now();
    }
}));

RegionRandom.rulebookRandomizeRegion.addRule(new Rule({
    name : "Add room A and B to region",
    firstPriority : Rule.PRIORITY_HIGHEST,
    code : async runner => {
        await RegionRandom.rulebookPlaceRoom.execute({
            noun : <RandomizingRoomOptions> {
                map : region.map,
                room : rooma,
                region : region
            }
        }).then();
        await RegionRandom.rulebookPlaceRoom.execute({
            noun : <RandomizingRoomOptions> {
                map : region.map,
                room : roomb,
                region : region
            }
        }).then();
    },
    conditions : runner => {
        return runner.noun == region;
    }
}));

function getPath (rooma : Room, roomb : Room) {
    console.debug("The best path from " + rooma.getPrintedName() + " to " + roomb.getPrintedName() + " is:");
    let t0, dir, t1;
    t0 = performance.now();
    dir = rooma.bestDirectionTo(roomb);
    t1 = performance.now();
    if (dir != undefined) {
        console.debug(DirectionNames[Direction[dir]]);
    } else {
        console.debug("There is no path.");
    }
    console.debug("Call to doSomething took " + (t1 - t0) + " milliseconds.");
}

// let t0 = performance.now();
// for (var i = 0; i < 1; i++) {
//     getPath(rooma, roomj);
// }
// window['roome'] = roome;
// let t1 = performance.now();
// console.debug("Total: " + (t1 - t0) + " milliseconds.");


let wanderRegion = new RegionRandom("OrcableRegion");
wanderRegion.place(rooma, roomb);
region.place(wanderRegion);

// Test done with a thousand orcs searching paths and reading rooms. Total time taken: 200ms.
// 1000 orcs, no wanderers: 130ms
// 100 orcs, no wanderer: 13ms (so the same amount of time as not having any orcs)
// 100 orcs, wanderer: 30ms
// 100 orcs, no shiny picker, wanderer: 30ms
// 100 orcs, no wanderer, no shiny picker: 13ms
// 1000 orcs, no wanderer, no shiny picker: 51ms
/**
 * Conclusion:
 * The test had all orcs placed in the same room, so every time ShinyPicker AI runs, all the orcs were looking at all the other orcs.
 * Game feels "instant" up to 100ms turns, which was reached at 250 orcs with JUST one PIcks Shiny and Wanderer rules.
 * With 100 EMPTY shiny rules, 250 orcs already take up 500ms per turn!
 * If the orcs fail the conditions for wanderer and shiny rules, then there is just a small overhead to having these rules there.
 * 100 Rules failing the conditions allows for 800 orcs below 100ms.
 * 50 orcs with 150 Shiny Rules which go through all the other orcs while they wander towards a region results in a 250ms wait.
 * That's not good since those numbers are similar to what end game should have, however real rules might not be as expensive as Pick Shinies with a room full of orcs..
 * Wait is bearable for 100 orcs with 800 disabled rules, so it should be "fine" to have all rules in the same rulebook.
 * First possible optimization: make AI rules have a static "check" and bake them into each NPC's rulebook so that they don't have to be checked every turn.
 * Second possible optimization: use maximum distance from player to decide which NPCs have their AIs executed (even better: only run AI for the current region).
 * Third optimization: reduce number of rules. Last resort.
 */
let rooms = region.getRooms();
let shuffler = new Shuffler(rooms);
for (let i = 0; i < 0; i++) {
    AI.rules.createAndAddRule({
        name : "Pick Shinies",
        firstPriority : AIRules.PRIORITY_ACTING_ON_PLACE,
        conditions : (runner : RulebookRunner<Person>) => {
            let person = runner.noun;
            return person.AI.picksShinies;
        },
        code : (runner : RulebookRunner<Person>) => {
            let person = runner.noun;
            let room = person.getRoom();
            let visibleThings = room.getContainedAndVisibleTo(person);

            if (visibleThings.length > 0) {
                for (let i = 0; i < visibleThings.length; i++) {
                    if (!visibleThings[i].fixedInPlace && visibleThings[i].getShiny()) {
                        return new ActionTake(person, visibleThings[i]);
                    }
                }
            }
        }
    });
}
let randomOrc;
let randomOrc2;
for (let i = 0; i < 10; i++) {
    let orc = new OrcDebugger();
    randomOrc = orc;
    if (randomOrc2 == undefined) {
        randomOrc2 = orc;
    }
    orc.AI.wanderer = true;
    orc.AI.picksShinies = true;
    orc.AI.wandersOn = wanderRegion;
    let room = new Shuffler(region.getRooms()).getOne();
    room.place(orc);
}

var fTarget = new ContentGroup();
fTarget.addUnit(
    new FuckingUnit()
        .setFucked(WorldState.player)
        .setFucker(randomOrc)
        .setHole(WorldState.player.getPart(HumanoidVagina))
        .setStick(randomOrc.getPart(HumanoidPenis))
        .addMarker(FuckingState.CUM_START)
);


(new FuckingDescription("Specific Orc in Vagina"))
    .setDescription(new Say("Specific Orc in Vagina"))
    .addUnit()
    .setFucker(randomOrc)
    .setHole(WorldState.player.getPart(HumanoidVagina))
    .setStick(randomOrc.getPart(HumanoidPenis));

(new FuckingDescription("Specific Orc Starts Cumming in Vagina"))
    .setDescription(new Say("Specific Orc Starts Cumming in Vagina"))
    .addUnit()
    .setFucker(randomOrc)
    .setHole(WorldState.player.getPart(HumanoidVagina))
    .addMarker(FuckingState.CUM_START)
    .setStick(randomOrc.getPart(HumanoidPenis));