globalThis.isItemAccessible = (function() {
	return {
		array: array,
		entry: entry,
	};

	/**
	 * Checks whether an item is accessible
	 * @param {string} string Name of wearable item
	 * @param {string} [category="clothing"] that item is in clothing, collar, etc
	 * @param {App.Entity.SlaveState} [slave]
	 * @returns {boolean|string} Returns true if item is accessible, and false if it is not. If the slave param is set, it may sometimes return a string instead of false, explaining why the item can't be used with that slave.
	 */
	function entry(string, category = "clothing", slave) {
		if (V.cheatMode === 1) {
			return true;
		}
		let niceDB = [];
		let harshDB = [];
		switch (category) {
			case "clothing":
			case "clothes":
				niceDB = App.Data.misc.niceClothes;
				harshDB = App.Data.misc.harshClothes;
				break;
			case "collar":
				niceDB = App.Data.misc.niceCollars;
				harshDB = App.Data.misc.harshCollars;
				break;
			case "bellyAccessory":
				niceDB = App.Data.misc.bellyAccessories;
				break;
			case "buttplug":
				niceDB = App.Data.misc.buttplugs;
				break;
			case "buttplugAttachment":
				niceDB = App.Data.misc.buttplugAttachments;
				break;
			case "vaginalAccessory":
				niceDB = App.Data.misc.vaginalAccessories;
				break;
			case "vaginalAttachment":
				niceDB = App.Data.misc.vaginalAttachments;
				break;
			case "dickAccessory":
				niceDB = App.Data.misc.vaginalAccessories;
				break;
			case "shoes":
				niceDB = App.Data.misc.shoes;
				break;
			case "chastity":
				niceDB = App.Data.misc.chastityDevices;
				break;
			default:
				console.log(`made a category for ${category} automatically, may need to define this by hand`);
				niceDB = App.Data.misc[category];
				break;
		}
		let item = niceDB.find((i) => i.value === string);
		if (!item && (typeof harshDB !== 'undefined')) {
			item = harshDB.find((i) => i.value === string);
		}
		if (!item) {
			console.log(`${string} is not a registered piece of clothing! Check App.Data.misc.${category}`);
			return false; /* couldn't be found */
		}
		return isAvailable(item, category, slave);
	}
	/**
	 * Returns array of wearable clothing in format [name, value], basically player facing / game data.
	 * @param {Array} db Array to look in (such as App.Data.misc.niceClothes)
	 * @returns {Array}
	 */
	function array(db) {
		const array = [];
		db.forEach((i) => {
			if (V.cheatMode || isAvailable(i)) {
				let name = i.name;
				if (i.fs) {
					name = i.name + ` (FS)`;
				}
				array.push([name, i.value]);
			}
		});
		return array;
	}

	function isAvailable(item, category, slave) {
		let slaveResults;
		if (slave) {
			slaveResults = isAvailableForSlave(item, category, slave);
			if (slaveResults === false) {
				return slaveResults;
			}
		}
		if (!(item.hasOwnProperty("fs")) && !(item.hasOwnProperty("rs"))) {
			// No restriction, this clothing item is available to everyone
			return true;
		}
		if (item.hasOwnProperty("rs")) {
			if (typeof item.rs === 'object' && item.rs !== null) {
				let fail = false;
				// Scan rs requirments one by one if they are an object
				Object.keys(item.rs).forEach((requirement) => {
					if (typeof item.rs[requirement] === 'object' && item.rs[requirement] !== null) {
						let eitherCheck = 0;
						if (requirement === "either") { // If we find the "either" nested object, we need to see if just one of the conditions in it are true.
							Object.keys(item.rs.either).forEach((nestedRequirement) => {
								if (typeof item.rs.either[nestedRequirement] === 'object' && item.rs.either[nestedRequirement] !== null) { // And yes sometimes even in either we have a nested object, to check V.PC mostly, so check for it.
									let insideEitherCheck = 0;
									Object.keys(item.rs.either[nestedRequirement]).forEach((insideNestedRequirement) => {
										if (V[nestedRequirement][insideNestedRequirement] === item.rs.either[nestedRequirement][insideNestedRequirement]) {
											insideEitherCheck++;
										}
									});
									if (insideEitherCheck > 0) {
										eitherCheck++;
									}
									if (V[nestedRequirement] === item.rs.either[nestedRequirement]) {
										eitherCheck++;
									}
								} else {
									if (item.rs.either[nestedRequirement] === V[nestedRequirement]) {
										eitherCheck++;
									}
								}
							});
						} else {
							Object.keys(item.rs.either).forEach((nestedRequirement) => { // Otherwise we are probably recording a nested V variable to be checked.
								if (V[requirement][nestedRequirement] === item.rs[requirement][nestedRequirement]) {
									eitherCheck++;
								}
							});
						}
						if (eitherCheck === 0) {
							fail = true;
						}
					} else {
						if (V[requirement] !== item.rs[requirement]) {
							fail = true;
						}
					}
				});
				if (fail) {
					return false;
				} else {
					return true;
				}
			} else { // string
				if (V[item.rs] < 1) {
					return false;
				} else {
					return true;
				}
			}
		}
		if (item.hasOwnProperty("fs")) {
			if (!(V.arcologies[0][item.fs] > 0)) {
				return false;
			}
		}
		if (slaveResults) { // If we still haven't returned true or false, then we display why this particular slave can't use the item.
			return slaveResults;
		}
		return true;
	}

	function isAvailableForSlave(item, category, slave) {
		switch (category) {
			case "clothing":
			case "clothes":
				break;
			case "collar":
				break;
			case "bellyAccessory": {
				switch (item.value) {
					case "a support band": {
						if (slave.belly > 10000) {
							return true;
						} else {
							return `Slave belly too small for this`;
						}
					}
					case "a small empathy belly":
					case "a medium empathy belly":
					case "a large empathy belly":
					case "a huge empathy belly": {
						if (slave.belly < 1500 && slave.weight < 130) {
							return true;
						} else {
							return `Slave belly too big for this`;
						}
					}
					default:
						return true;
				}
			}
			case "buttplug": {
				switch (item.value) {
					case "huge plug": {
						if (slave.anus < 2) {
							return `Slave's anus is too small for this right now`;
						} else {
							return true;
						}
					}
					case "long plug":
					case "long, large plug": {
						if (!(slave.breedingMark !== 1 || V.propOutcome === 0 || V.eugenicsFullControl === 1 || V.arcologies[0].FSRestart === "unset")) {
							return "Elites frown on this";
						} else {
							return true;
						}
					}
					case "long, huge plug": {
						if (slave.anus < 2) {
							return `Slave's anus is too small for this right now`;
						} else if (!(slave.breedingMark !== 1 || V.propOutcome === 0 || V.eugenicsFullControl === 1 || V.arcologies[0].FSRestart === "unset")) {
							return "Elites frown on this";
						} else {
							return true;
						}
					}
					default:
						return true;
				}
			}
			case "buttplugAttachment":
				break;
			case "vaginalAccessory": {
				if (slave.vagina < 0) {
					return false;
				}
				switch (item.value) {
					case "huge dildo": {
						if (slave.vagina < 2) {
							return `Slave's vagina is too small for this right now`;
						} else {
							return true;
						}
					}
					case "long dildo":
					case "long, large dildo": {
						if (!(slave.breedingMark !== 1 || V.propOutcome === 0 || V.eugenicsFullControl === 1 || V.arcologies[0].FSRestart === "unset")) {
							return "Elites frown on this";
						} else {
							return true;
						}
					}
					case "long, huge dildo": {
						if (slave.vagina < 2) {
							return `Slave's vagina is too small for this right now`;
						} else if (!(slave.breedingMark !== 1 || V.propOutcome === 0 || V.eugenicsFullControl === 1 || V.arcologies[0].FSRestart === "unset")) {
							return "Elites frown on this";
						} else {
							return true;
						}
					}
					default:
						return true;
				}
			}
			case "vaginalAttachment": {
				if (slave.vagina < 0) {
					return false;
				}
				switch (item.value) {
					case "vibrator":
						if (slave.vaginalAccessory === "none") {
							return "No vaginal accessory to attach it to";
						} else if (slave.vaginalAccessory === "bullet vibrator" || slave.vaginalAccessory === "smart bullet vibrator") {
							return "Vaginal accessory already vibrates";
						} else {
							return true;
						}
				}
				break;
			}
			case "dickAccessory":
				if (slave.dick < 1) {
					return false;
				}
				return true;
			case "shoes":
				break;
			case "chastity": {
				switch (item.value) {
					case "chastity belt":
					case "combined chastity belt": {
						if (slave.vagina > -1) {
							return true;
						} else {
							return false;
						}
					}
					case "chastity cage":
					case "combined chastity cage": {
						if (slave.dick > 0) {
							return true;
						} else {
							return false;
						}
					}
					case "genital chastity":
					case "full chastity": {
						if (slave.vagina > -1 && slave.dick > 0) {
							return true;
						} else {
							return false;
						}
					}
					case "choose own chastity": {
						if (slave.choosesOwnChastity !== 1 && slave.devotion > 20 && slave.trust > 0) {
							return true;
						}
						return false;
					}
					case "revoke choosing own chastity": {
						if (slave.choosesOwnChastity > 0) {
							return true;
						}
						return false;
					}
					default:
						return true;
				}
			}
			default:
				break;
		}
		return true;
	}
})();

/**
 * @param {App.Entity.SlaveState} slave
 * @param {string} prosthetic
 * @returns {boolean}
 */
globalThis.isProstheticAvailable = function(slave, prosthetic) {
	return slave.readyProsthetics.findIndex(function(p) { return p.id === prosthetic; }) !== -1;
};

/**
 * @param {App.Entity.SlaveState} slave
 * @param {string} prosthetic
 */
globalThis.addProsthetic = function(slave, prosthetic) {
	if (!isProstheticAvailable(slave, prosthetic)) {
		let limb = prostheticToLimb(prosthetic);
		if (limb > 0) {
			let p = {
				id: prosthetic,
				arm: {left: new App.Entity.LimbState(), right: new App.Entity.LimbState()},
				leg: {left: new App.Entity.LimbState(), right: new App.Entity.LimbState()}
			};
			p.arm.left.type = limb;
			p.arm.right.type = limb;
			p.leg.left.type = limb;
			p.leg.right.type = limb;
			slave.readyProsthetics.push(p);
		} else {
			slave.readyProsthetics.push({id: prosthetic});
		}
	}
};

/**
 * @param {App.Entity.SlaveState} slave
 * @param {string} prosthetic
 * @returns {{}}
 */
globalThis.findProsthetic = function(slave, prosthetic) {
	return slave.readyProsthetics.find(p => p.id === prosthetic);
};

/**
 * @param {string} prosthetic
 * @returns {number}
 */
globalThis.prostheticToLimb = function(prosthetic) {
	switch (prosthetic) {
		case "basicL":
			return 2;
		case "sexL":
			return 3;
		case "beautyL":
			return 4;
		case "combatL":
			return 5;
		case "cyberneticL":
			return 6;
		default:
			return 0;
	}
};

/**
 *
 * @param {number} limb
 * @returns {string}
 */
globalThis.limbToProsthetic = function(limb) {
	switch (limb) {
		case 2:
			return "basicL";
		case 3:
			return "sexL";
		case 4:
			return "beautyL";
		case 5:
			return "combatL";
		case 6:
			return "cyberneticL";
		default:
			return "";
	}
};

src/js/itemAvailability.js