/*
*
* This file focuses on slave related functions that assess qualities about slaves.  Are they/can they X?
*
*/

/**
 * @param {App.Entity.SlaveState} slave
 * @returns {string}
 */
globalThis.getSlaveDevotionClass = function(slave) {
	if ((!slave) || (!State)) {
		return undefined;
	}
	if (slave.fetish === "mindbroken") {
		return "mindbroken";
	}
	if (slave.devotion < -95) {
		return "very-hateful";
	} else if (slave.devotion < -50) {
		return "hateful";
	} else if (slave.devotion < -20) {
		return "resistant";
	} else if (slave.devotion <= 20) {
		return "ambivalent";
	} else if (slave.devotion <= 50) {
		return "accepting";
	} else if (slave.devotion <= 95) {
		return "devoted";
	} else {
		return "worshipful";
	}
};

/**
 * @param {App.Entity.SlaveState} slave
 * @returns {string}
 */
globalThis.getSlaveTrustClass = function(slave) {
	if ((!slave) || (!State)) {
		return undefined;
	}

	if (slave.fetish === "mindbroken") {
		return "";
	}

	if (slave.trust < -95) {
		return "extremely-terrified";
	} else if (slave.trust < -50) {
		return "terrified";
	} else if (slave.trust < -20) {
		return "frightened";
	} else if (slave.trust <= 20) {
		return "fearful";
	} else if (slave.trust <= 50) {
		if (slave.devotion < -20) {
			return "hate-careful";
		} else {
			return "careful";
		}
	} else if (slave.trust <= 95) {
		if (slave.devotion < -20) {
			return "bold";
		} else {
			return "trusting";
		}
	} else if (slave.devotion < -20) {
		return "defiant";
	} else {
		return "profoundly-trusting";
	}
};

/**
 * Returns a "disobedience factor" between 0 (perfectly obedient) and 100 (completely defiant)
 * @param {App.Entity.SlaveState} slave
 * @returns {number}
 */
globalThis.disobedience = function(slave) {
	const devotionBaseline = 20; // with devotion above this number slaves will obey completely
	const trustBaseline = -20; // with trust below this number slaves will obey completely

	if (slave.devotion > devotionBaseline || slave.trust < trustBaseline) {
		return 0; // no chance of disobedience
	}

	// factors are between 0 (right on the boundary of perfectly obedient) and 10 (completely disobedient)
	let devotionFactor = 10 - ((10 * (slave.devotion + 100)) / (devotionBaseline + 100));
	let trustFactor = (10 * (slave.trust - trustBaseline)) / (100 - trustBaseline);
	return Math.round(devotionFactor * trustFactor);
};

/**
 * Returns how exposing a slave's outfit is, after taking into consideration a topless outfit is more revealing for beboobed slaves or female ones.
 * @param {App.Entity.SlaveState} slave
 * @returns {0|1|2|3|4}
 */
globalThis.getExposure = function(slave) {
	const clothes = App.Data.clothes.get(slave.clothes);
	return (clothes.topless && clothes.exposure < 3 && (slave.boobs > 299 || (slave.genes === 'XX' && slave.vagina >= 0))) ? 3 : clothes.exposure;
};

/**
 * @param {App.Entity.SlaveState} slave
 * @returns {boolean}
 */
globalThis.canImproveIntelligence = function(slave) {
	let origIntel = V.genePool.find(function(s) { return s.ID === slave.ID; }).intelligence;
	return (slave.intelligence < origIntel + 15) && (slave.intelligence < 100);
};

/**
 * @param {App.Entity.SlaveState} slave
 * @returns {number}
 */
globalThis.maxHeight = function(slave) {
	let max = Math.trunc(Math.clamp((Height.mean(slave) * 1.25), 0, 274)); /* max achievable height is expected height plus 25% */

	if (slave.geneticQuirks.dwarfism === 2 && slave.geneticQuirks.gigantism !== 2) {
		max = Math.min(max, 160);
	}

	return max;
};

/**
 * @param {App.Entity.SlaveState} slave
 * @returns {boolean}
 */
globalThis.canImproveHeight = function(slave) {
	return slave.height < maxHeight(slave);
};

/**
 * @param {App.Entity.SlaveState} slave
 * @param {FC.HumanState} target
 * @returns {boolean}
 */
globalThis.haveRelationshipP = function(slave, target) {
	return slave.relationshipTarget === target.ID;
};

/**
 * @param {App.Entity.SlaveState} slave
 * @param {App.Entity.SlaveState} target
 * @returns {boolean}
 */
globalThis.isRivalP = function(slave, target) {
	return slave.rivalryTarget === target.ID;
};

/**
 * @param {FC.HumanState} slave
 * @returns {boolean}
 */
globalThis.supremeRaceP = function(slave) {
	return V.arcologies[0].FSSupremacistRace === slave.race;
};

/**
 * @param {FC.HumanState} slave
 * @returns {boolean}
 */
globalThis.inferiorRaceP = function(slave) {
	return V.arcologies[0].FSSubjugationistRace === slave.race;
};

/**
 * @param {App.Entity.SlaveState} slave
 * @returns {boolean}
 */
globalThis.isLeaderP = function(slave) {
	const leaders = [S.HeadGirl, S.Bodyguard, S.Recruiter, S.Concubine, S.Nurse, S.Attendant, S.Matron, S.Madam, S.DJ, S.Milkmaid, S.Farmer, S.Stewardess, S.Schoolteacher, S.Wardeness];

	return leaders.some(leader => leader && leader.ID === slave.ID);
};

/** Get the written variant of a slave's title for the player, without messing with global state.
 * @param {App.Entity.SlaveState} [slave]
 * @returns {string}
 */
globalThis.getWrittenTitle = function(slave) {
	if (slave && slave.custom.title !== undefined && slave.custom.title !== "" && slave.rudeTitle === 0) {
		return slave.custom.title;
	}
	if (V.PC.customTitle !== undefined) {
		return V.PC.customTitle;
	} else if (V.PC.title !== 0) {
		return "Master";
	} else {
		return "Mistress";
	}
};

/**
 * @param {App.Entity.SlaveState} slave
 * @returns {string}
 */
globalThis.SlaveFullName = function(slave) {
	const pair = slave.slaveSurname ? [slave.slaveName, slave.slaveSurname] : [slave.slaveName];
	if ((V.surnameOrder !== 1 && ["Cambodian", "Chinese", "Hungarian", "Japanese", "Korean", "Mongolian", "Taiwanese", "Vietnamese"].includes(slave.nationality)) || (V.surnameOrder === 2)) {
		pair.reverse();
	}
	return pair.join(" ");
};

/** Is the slave a shelter slave?
 * @param {App.Entity.SlaveState} slave
 * @returns {boolean}
 */
globalThis.isShelterSlave = function(slave) {
	return (typeof slave.origin === "string" && slave.origin.includes("Slave Shelter"));
};

/**
 * Returns if a slave appears male, female, or androgynous.
 *
 * @param {App.Entity.SlaveState} slave
 * @returns {number}
 */
globalThis.perceivedGender = function(slave) {
	return -1;
};

/**
 * @param {App.Entity.SlaveState} A
 * @param {App.Entity.SlaveState} B
 * @returns {boolean}
 */
globalThis.sameAssignmentP = function(A, B) {
	return A.assignment === B.assignment;
};

/** Determine whether a given penthouse slave can move into a private room or not.
 * @param {App.Entity.SlaveState} slave
 * @returns {boolean}
 */
globalThis.canMoveToRoom = function(slave) {
	const partner = slave.relationship >= 4 ? getSlave(slave.relationshipTarget) : null;
	const partnerHasRoom = partner && assignmentVisible(partner) && partner.rules.living === "luxurious";
	return partnerHasRoom || V.rooms - V.roomsPopulation >= 1;
};

/**
 * @param {App.Entity.SlaveState} slave
 * @returns {0|1|2|3} 0: No heel boost at all.  1: Pumps, slight boost.  2: High heels.  3: Painfully/extreme high heels
 */
globalThis.shoeHeelCategory = function(slave) {
	const height = App.Data.shoes.get(slave.shoes) ? App.Data.shoes.get(slave.shoes).heelHeight : 0;  // Height is in cm
	if (height > 20) {
		return 3;
	} else if (height > 5) {
		return 2;
	} else if (height > 0) {
		return 1;
	} else {
		return 0;
	}
};

/**
 * @param {App.Entity.SlaveState} slave
 * @returns {number} shoe height in cm (heel + platform height)
 */
globalThis.shoeHeight = function(slave) {
	const heelHeight = App.Data.shoes.get(slave.shoes) ? App.Data.shoes.get(slave.shoes).heelHeight : 0;
	const platformHeight = App.Data.shoes.get(slave.shoes) ? App.Data.shoes.get(slave.shoes).platformHeight : 0;
	return heelHeight + platformHeight;
};

/**
 * @param {App.Entity.SlaveState} slave
 * @returns {0|1|2|3}
 */
globalThis.plugWidth = function(slave) {
	const plug = App.Data.buttplug.get(slave.buttplug) || V.customItem.buttplug.get(slave.buttplug);
	return plug.width || 0;
};

/**
 * @param {App.Entity.SlaveState} slave
 * @returns {0|1|2|3}
 */
globalThis.plugLength = function(slave) {
	const plug = App.Data.buttplug.get(slave.buttplug) || V.customItem.buttplug.get(slave.buttplug);
	return plug.length || 0;
};

/**
 * @param {App.Entity.SlaveState} slave
 * @returns {0|1|2|3}
 */
globalThis.dildoWidth = function(slave) {
	const dildo = App.Data.vaginalAccessory.get(slave.vaginalAccessory) || V.customItem.vaginalAccessory.get(slave.vaginalAccessory);
	if (dildo === undefined) {
		console.log("missing dildo: ", slave.vaginalAccessory);
		return 0;
	}
	return dildo.width || 0;
};

/**
 * @param {App.Entity.SlaveState} slave
 * @returns {0|1|2}
 */
globalThis.dildoLength = function(slave) {
	const dildo = App.Data.vaginalAccessory.get(slave.vaginalAccessory) || V.customItem.vaginalAccessory.get(slave.vaginalAccessory);
	return dildo.length || 0;
};

/**
 * Returns the best vibe mode available between the dildo itself, and any attachment that may be present.
 * @param {App.Entity.SlaveState} slave
 * @returns {0|1|2}
 */
globalThis.dildoVibeLevel = function(slave) {
	// Vaginal accessory/dildo
	const dildo = App.Data.vaginalAccessory.get(slave.vaginalAccessory) || V.customItem.vaginalAccessory.get(slave.vaginalAccessory);
	const dildoVibrationLevel = ((dildo) ? dildo.vibrates : 0) || 0;

	// Attachment, if present
	const vaginalAttachment = App.Data.slaveWear.vaginalAttachment.get(slave.vaginalAttachment) || 0;
	const vaginalAttachmentVibrationLevel = ((vaginalAttachment) ? vaginalAttachment.vibrates : 0) || 0;
	return Math.max(dildoVibrationLevel, vaginalAttachmentVibrationLevel);
};