"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getRandomJSONRoom = exports.getRandomJSONEntity = exports.getJSONRoomsOfSubType = exports.getJSONRoomOfVariant = exports.getJSONRoomDoorSlotFlags = void 0;
const isaac_typescript_definitions_1 = require("isaac-typescript-definitions");
const array_1 = require("./array");
const doors_1 = require("./doors");
const enums_1 = require("./enums");
const flag_1 = require("./flag");
const log_1 = require("./log");
const random_1 = require("./random");
const types_1 = require("./types");
const utils_1 = require("./utils");
/**
 * Helper function to calculate what the resulting `BitFlags<DoorSlotFlag>` value would be for a
 * given JSON room.
 *
 * (A JSON room is an XML file converted to JSON so that it can be directly imported into your mod.)
 */
function getJSONRoomDoorSlotFlags(jsonRoom) {
    const roomShapeString = jsonRoom.$.shape;
    const roomShape = (0, types_1.parseIntSafe)(roomShapeString);
    (0, utils_1.assertDefined)(roomShape, `Failed to parse the "shape" field of a JSON room: ${roomShapeString}`);
    if (!(0, enums_1.isEnumValue)(roomShape, isaac_typescript_definitions_1.RoomShape)) {
        error(`Failed to parse the "shape" field of a JSON room since it was an invalid number: ${roomShape}`);
    }
    let doorSlotFlags = isaac_typescript_definitions_1.DoorSlotFlagZero;
    for (const door of jsonRoom.door) {
        const existsString = door.$.exists;
        if (existsString !== "True" && existsString !== "False") {
            error(`Failed to parse the "exists" field of a JSON room door: ${existsString}`);
        }
        if (existsString === "False") {
            continue;
        }
        const xString = door.$.x;
        const x = (0, types_1.parseIntSafe)(xString);
        (0, utils_1.assertDefined)(x, `Failed to parse the "x" field of a JSON room door: ${xString}`);
        const yString = door.$.y;
        const y = (0, types_1.parseIntSafe)(yString);
        (0, utils_1.assertDefined)(y, `Failed to parse the "y" field of a JSON room door: ${yString}`);
        const doorSlot = (0, doors_1.getRoomShapeDoorSlot)(roomShape, x, y);
        (0, utils_1.assertDefined)(doorSlot, `Failed to retrieve the door slot for a JSON room door at coordinates: [${x}, ${y}]`);
        const doorSlotFlag = (0, doors_1.doorSlotToDoorSlotFlag)(doorSlot);
        doorSlotFlags = (0, flag_1.addFlag)(doorSlotFlags, doorSlotFlag);
    }
    return doorSlotFlags;
}
exports.getJSONRoomDoorSlotFlags = getJSONRoomDoorSlotFlags;
/**
 * Helper function to find a specific room from an array of JSON rooms.
 *
 * (A JSON room is an XML file converted to JSON so that it can be directly imported into your mod.)
 *
 * @param jsonRooms The array of rooms to search through.
 * @param variant The room variant to select. (The room variant can be thought of as the ID of the
 *                room.)
 */
function getJSONRoomOfVariant(jsonRooms, variant) {
    const jsonRoomsOfVariant = jsonRooms.filter((jsonRoom) => {
        const roomVariantString = jsonRoom.$.variant;
        const roomVariant = (0, types_1.parseIntSafe)(roomVariantString);
        if (roomVariant === undefined) {
            error(`Failed to convert a JSON room variant to an integer: ${roomVariantString}`);
        }
        return roomVariant === variant;
    });
    // The room variant acts as an ID for the room. We assume that there should only be a single room
    // per variant.
    if (jsonRoomsOfVariant.length === 0) {
        return undefined;
    }
    if (jsonRoomsOfVariant.length === 1) {
        return jsonRoomsOfVariant[0];
    }
    error(`Found ${jsonRoomsOfVariant.length} JSON rooms with a variant of ${variant}, when there should only be 1.`);
}
exports.getJSONRoomOfVariant = getJSONRoomOfVariant;
/**
 * Helper function to find all of the JSON rooms that match the sub-type provided.
 *
 * (A JSON room is an XML file converted to JSON so that it can be directly imported into your mod.)
 *
 * @param jsonRooms The array of rooms to search through.
 * @param subType The sub-type to match.
 */
function getJSONRoomsOfSubType(jsonRooms, subType) {
    return jsonRooms.filter((jsonRoom) => {
        const roomSubTypeString = jsonRoom.$.subtype;
        const roomSubType = (0, types_1.parseIntSafe)(roomSubTypeString);
        if (roomSubType === undefined) {
            error(`Failed to convert a JSON room sub-type to an integer: ${roomSubTypeString}`);
        }
        return roomSubType === subType;
    });
}
exports.getJSONRoomsOfSubType = getJSONRoomsOfSubType;
/**
 * Helper function to get a random JSON entity from an array of JSON entities.
 *
 * (A JSON entity is an entity inside of a JSON room. A JSON room is an XML file converted to JSON
 * so that it can be directly imported into your mod.)
 *
 * Note that this function does not simply choose a random element in the provided array; it will
 * properly account for each room weight using the algorithm from:
 * https://stackoverflow.com/questions/1761626/weighted-random-numbers
 *
 * If you want an unseeded entity, you must explicitly pass `undefined` to the `seedOrRNG`
 * parameter.
 *
 * @param jsonEntities The array of entities to randomly choose between.
 * @param seedOrRNG The `Seed` or `RNG` object to use. If an `RNG` object is provided, the
 *                  `RNG.Next` method will be called. If `undefined` is provided, it will default to
 *                  a random seed.
 * @param verbose Optional. If specified, will write entries to the "log.txt" file that describe
 *                what the function is doing. Default is false.
 */
function getRandomJSONEntity(jsonEntities, seedOrRNG, verbose = false) {
    const totalWeight = getTotalWeightOfJSONObject(jsonEntities);
    if (verbose) {
        (0, log_1.log)(`Total weight of the JSON entities provided: ${totalWeight}`);
    }
    const chosenWeight = (0, random_1.getRandomFloat)(0, totalWeight, seedOrRNG);
    if (verbose) {
        (0, log_1.log)(`Randomly chose weight for JSON entity: ${chosenWeight}`);
    }
    const randomJSONEntity = getJSONObjectWithChosenWeight(jsonEntities, chosenWeight);
    (0, utils_1.assertDefined)(randomJSONEntity, `Failed to get a JSON entity with chosen weight: ${chosenWeight}`);
    return randomJSONEntity;
}
exports.getRandomJSONEntity = getRandomJSONEntity;
/**
 * Helper function to get a random JSON room from an array of JSON rooms.
 *
 * (A JSON room is an XML file converted to JSON so that it can be directly imported into your mod.)
 *
 * Note that this function does not simply choose a random element in the provided array; it will
 * properly account for each room weight using the algorithm from:
 * https://stackoverflow.com/questions/1761626/weighted-random-numbers
 *
 * If you want an unseeded room, you must explicitly pass `undefined` to the `seedOrRNG` parameter.
 *
 * @param jsonRooms The array of rooms to randomly choose between.
 * @param seedOrRNG The `Seed` or `RNG` object to use. If an `RNG` object is provided, the
 *                  `RNG.Next` method will be called. If `undefined` is provided, it will default to
 *                  a random seed.
 * @param verbose Optional. If specified, will write entries to the "log.txt" file that describe
 *                what the function is doing. Default is false.
 */
function getRandomJSONRoom(jsonRooms, seedOrRNG, verbose = false) {
    const totalWeight = getTotalWeightOfJSONObject(jsonRooms);
    if (verbose) {
        (0, log_1.log)(`Total weight of the JSON rooms provided: ${totalWeight}`);
    }
    const chosenWeight = (0, random_1.getRandomFloat)(0, totalWeight, seedOrRNG);
    if (verbose) {
        (0, log_1.log)(`Randomly chose weight for JSON room: ${chosenWeight}`);
    }
    const randomJSONRoom = getJSONObjectWithChosenWeight(jsonRooms, chosenWeight);
    (0, utils_1.assertDefined)(randomJSONRoom, `Failed to get a JSON room with chosen weight: ${chosenWeight}`);
    return randomJSONRoom;
}
exports.getRandomJSONRoom = getRandomJSONRoom;
function getTotalWeightOfJSONObject(jsonOjectArray) {
    const weights = jsonOjectArray.map((jsonObject) => {
        const weightString = jsonObject.$.weight;
        const weight = tonumber(weightString);
        (0, utils_1.assertDefined)(weight, `Failed to parse the weight of a JSON object: ${weightString}.`);
        return weight;
    });
    return (0, array_1.sumArray)(weights);
}
function getJSONObjectWithChosenWeight(jsonOjectArray, chosenWeight) {
    let weightAccumulator = 0;
    for (const jsonObject of jsonOjectArray) {
        const weightString = jsonObject.$.weight;
        const weight = tonumber(weightString);
        (0, utils_1.assertDefined)(weight, `Failed to parse the weight of a JSON object: ${weightString}`);
        weightAccumulator += weight;
        if (weightAccumulator >= chosenWeight) {
            return jsonObject;
        }
    }
    return undefined;
}
