globalThis.endWeek = (function() {
	function doEndWeek() {
		// purge SugarCube's expired state cache
		State.expired.length = 0;

		// report setup
		setupLastWeeksCash();
		setupLastWeeksRep();

		// globals setup
		resetSlaveMarkets();
		resetSlaveCounters();
		for (const s of V.slaves) {
			slavePrep(s);
		}
		setUseWeights();
		saveWeekTotals();

		// pass time for objects that need it
		organs();
		prosthetics();
		nursery();
		food();
		PC();

		// week end is done, move on to gameover or SA reports
		resetMiscGlobals();
		advance();
	}

	function resetSlaveMarkets() {
		V.gingering = 0;
		delete V.slaveMarket;
		for (const school of ["TSS", "GRI", "SCP", "LDE", "TGA", "HA", "TFS", "TCR", "NUL"]) {
			V[school].schoolSale = 0;
		}
	}

	function resetSlaveCounters() {
		V.inflatedSlavesCum = 0;
		V.inflatedSlavesMilk = 0;
		V.subSlaves = 0;
	}

	function slavePrep(s) {
		if (s.inflationMethod === 1 || s.inflationMethod === 2) {
			if (s.inflationType === "milk") {
				V.inflatedSlavesMilk++;
			} else if (s.inflationType === "cum") {
				V.inflatedSlavesCum++;
			}
		}
		if (s.assignment === Job.SUBORDINATE && s.subTarget === 0) {
			V.subSlaves++;
		}
		s.lastWeeksCashIncome = 0;
		s.lastWeeksRepIncome = 0;
		s.lastWeeksRepExpenses = 0;
	}

	function setUseWeights() {
		V.oralUseWeight = 5;
		V.vaginalUseWeight = 5;
		V.analUseWeight = 5;
		V.mammaryUseWeight = 1;
		V.penetrativeUseWeight = 1;

		if (V.policies.oralAppeal === 1) {
			V.oralUseWeight += 2;
			V.vaginalUseWeight -= 1;
			V.analUseWeight -= 1;
		} else if (V.policies.vaginalAppeal === 1) {
			V.oralUseWeight -= 1;
			V.vaginalUseWeight += 2;
			V.analUseWeight -= 1;
		} else if (V.policies.analAppeal === 1) {
			V.oralUseWeight -= 1;
			V.vaginalUseWeight -= 1;
			V.analUseWeight += 2;
		}

		if (V.policies.oralAppeal === -1) {
			V.oralUseWeight -= 2;
			V.vaginalUseWeight += 1;
			V.analUseWeight += 1;
		}
		if (V.policies.vaginalAppeal === -1) {
			V.oralUseWeight += 1;
			V.vaginalUseWeight -= 2;
			V.analUseWeight += 1;
		}
		if (V.policies.analAppeal === -1) {
			V.oralUseWeight += 1;
			V.vaginalUseWeight += 1;
			V.analUseWeight -= 2;
		}

		if (V.policies.sexualOpeness === 1) {
			V.penetrativeUseWeight += 1;
		}
	}

	function saveWeekTotals() {
		V.cashLastWeek = V.cash;
		V.repLastWeek = V.rep;
		V.foodLastWeek = V.food;
	}

	function organs() {
		function advanceOrgan(o) {
			if (o.weeksToCompletion > 0) {
				if (V.organFarmUpgrade === 3) {
					o.weeksToCompletion -= 4;
				} else if (V.organFarmUpgrade === 2) {
					o.weeksToCompletion -= 2;
				} else {
					o.weeksToCompletion--;
				}
			}
		}

		for (const organ of V.organs) {
			advanceOrgan(organ);
		}

		// move completed non-incubator organs to V.completedOrgans
		V.organs = V.organs.filter(function(o) {
			if (o.weeksToCompletion <= 0) {
				V.completedOrgans.push(o);
				return false;
			}
			return true;
		});

		for (const organ of V.incubatorOrgans) {
			advanceOrgan(organ);
		}

		// TODO: nurseryOrgans too, if those ever exist...
	}

	function prosthetics() {
		for (const p of V.adjustProsthetics) {
			if (p.workLeft > 0) {
				if (V.prostheticsUpgrade === 3) {
					p.workLeft -= 40;
				} else if (V.prostheticsUpgrade === 2) {
					p.workLeft -= 20;
				} else {
					p.workLeft -= 10;
				}
				if (p.workLeft <= 0) {
					V.adjustProstheticsCompleted++;
				}
			}
		}
	}

	function nursery() {
		for (const c of V.cribs) {
			c.growTime--;
			c.birthWeek++;
			if (c.birthWeek >= 52) {
				c.birthWeek = 0;
				c.actualAge++;
			}
			if (c.actualAge >= 3) {
				App.Facilities.Nursery.infantToChild(c);
			}
		}
	}

	function food() {
		function foodConsumption(s) {
			if (s.diet === "restricted") {
				return 1.8;
			} else if (s.diet === "slimming") {
				return 1.9;
			} else if (s.diet === "muscle building") {
				return 2.1;
			} else if (s.diet === "fattening") {
				return 2.2;
			} else {
				return 2;
			}
		}

		if (V.foodMarket > 0) {
			V.foodConsumption = ((V.lowerClass*V.foodRate.lower) + (V.middleClass*V.foodRate.middle) + (V.upperClass*V.foodRate.upper) + (V.topClass*V.foodRate.top));
			V.foodConsumption += V.slaves.reduce((acc, cur) => acc += foodConsumption(cur), 0);
			V.food -= Math.max(V.foodConsumption, V.food);
			V.foodConsumption = 0;
		}
	}

	function PC() {
		V.PC.sexualEnergy = 4;
		if (V.PCSlutContacts === 2) {
			V.PC.sexualEnergy -= 3;
		}
		if (V.personalAttention === "sex") {
			V.PC.sexualEnergy += 2;
		}
		if (V.PC.physicalAge >= 80) {
			V.PC.sexualEnergy -= 6;
		} else if (V.PC.physicalAge >= 72) {
			V.PC.sexualEnergy -= 5;
		} else if (V.PC.physicalAge >= 65) {
			V.PC.sexualEnergy -= 4;
		} else if (V.PC.physicalAge >= 58) {
			V.PC.sexualEnergy -= 3;
		} else if (V.PC.physicalAge >= 50) {
			V.PC.sexualEnergy -= 2;
		} else if (V.PC.physicalAge >= 42) {
			V.PC.sexualEnergy -= 1;
		} else if (V.PC.physicalAge >= 35) {
			V.PC.sexualEnergy += 0;
		} else if (V.PC.physicalAge >= 31) {
			V.PC.sexualEnergy += 1;
		} else if (V.PC.physicalAge >= 28) {
			V.PC.sexualEnergy += 2;
		} else if (V.PC.physicalAge >= 21) {
			V.PC.sexualEnergy += 3;
		} else if (V.PC.physicalAge >= 13) {
			V.PC.sexualEnergy += 4;
		} else if (V.PC.physicalAge === 12) {
			V.PC.sexualEnergy += 1;
		} else if (V.PC.physicalAge === 11) {
			V.PC.sexualEnergy -= 2;
		} else if (V.PC.physicalAge >= 0) {
			V.PC.sexualEnergy -= 6;
		}
		if (V.PC.balls >= 10) {
			V.PC.sexualEnergy += 2;
		} else if (V.PC.balls >= 5) {
			V.PC.sexualEnergy++;
		}
		if (V.PC.preg > 20) {
			if (V.PC.pregMood === 2) {
				V.PC.sexualEnergy += 4;
			} else {
				V.PC.sexualEnergy -= 3;
			}
		} else if (V.PC.preg > 0) {
			V.PC.sexualEnergy -= 1;
		} else {
			if (V.PC.fertDrugs === 1) {
				V.PC.sexualEnergy++;
			}
			if (V.PC.forcedFertDrugs > 0) {
				V.PC.sexualEnergy += 2;
			}
		}
		if (V.PC.staminaPills > 0) {
			V.PC.sexualEnergy += 2;
		}
		if (V.PC.preg > 0) {
			WombProgress(V.PC, 1, 1);
			WombNormalizePreg(V.PC);
			V.PC.pregWeek = V.PC.preg;
			let newBelly = WombGetVolume(V.PC);
			if (newBelly >= V.PC.belly) {
				V.PC.belly = newBelly;
			} else if (V.PC.belly > 500) {
				V.PC.belly *= 0.75;
			}
			V.PC.fertDrugs = 0;
		} else if (V.PC.belly > 0) {
			if (V.PC.belly < 100) {
				V.PC.belly = 0;
			} else {
				V.PC.belly *= 0.75;
			}
		}
		if (V.PC.pregWeek < 0) {
			V.PC.pregWeek++;
		}
	}

	function resetMiscGlobals() {
		// if a global is going to be used by the end-of-week reports, it must be reset here instead of in Next Week
		V.encyclopedia = "How to Play";
		V.expiree = 0;
		V.retiree = 0;

		V.HGEnergy = 0;
		V.HGCum = 0;
		V.HGSlaveSuccess = 0;
		V.HeadGirl = 0;
		V.Recruiter = 0;
		V.Madam = 0;
		V.unMadam = 0;
		V.madamCashBonus = 0;
		V.whorePriceAdjustment = {};
		V.DJ = 0;
		V.unDJ = 0;
		V.DJRepBonus = 0;
		V.Milkmaid = 0;
		V.Farmer = 0;
		V.Schoolteacher = 0;
		V.Wardeness = 0;
		V.Concubine = 0;
		V.Matron = 0;
		V.Nurse = 0;
		V.Bodyguard = 0;
		V.StudID = 0;
		V.StudCum = 0;
		V.fuckSlaves = 0;
		V.freeSexualEnergy = 0;
		V.publicServants = 0;
		V.cumSlaves = 0;
		V.averageDick = 0;
		V.slavesWithWorkingDicks = 0;
		V.slaveJobValues = {};
	}

	function advance() {
		if (V.slaves.length < 1) {
			V.gameover = "no slaves";
			Engine.play("Gameover");
		} else if (V.arcologies[0].ownership < V.arcologies[0].minority) {
			V.gameover = "ownership";
			Engine.play("Gameover");
		} else {
			Engine.play("Slave Assignments Report");
		}
	}

	function confirmEndWeek() {
		if (!V.sideBarOptions.confirmWeekEnd || confirm("Are you sure you want to end the week?")) {
			App.UI.EndWeekAnim.start();
			setTimeout(doEndWeek, 0); // execute immediately, but after the event loop runs, so the loading screen gets shown
		}
	}

	return confirmEndWeek;
})();

src/endWeek/endWeek.js