/**
 * Stores AI settings for all players.
 * Note that tby default, this does not assign AI
 * unless an AI bot is explicitly specified.
 * This is because:
 *  - the regular GameSetup does that on its own
 *  - this makes campaign/autostart scenarios easier to handle.
 *  - cleans the code here.
 */
const DEFAULT_AI_ECONOMY = { "supportRatio": 0.3 };

function encodeAIBehaviorWithEconomy(behavior, economy, military, priorities)
{
	const ratio = economy?.supportRatio ?? DEFAULT_AI_ECONOMY.supportRatio;
	let result = (behavior || "random") + ";supportRatio=" + ratio;

	// Encode all Economy parameters
	if (economy) {
		for (const key in economy) {
			if (key !== "supportRatio" && economy[key] !== undefined && economy[key] !== DEFAULT_AI_ECONOMY[key]) {
				result += "," + key + "=" + economy[key];
			}
		}
	}

	// Encode all Military parameters
	if (military) {
		for (const key in military) {
			if (military[key] !== undefined) {
				result += "," + key + "=" + military[key];
			}
		}
	}

	// Encode all Priority parameters
	if (priorities) {
		for (const key in priorities) {
			if (priorities[key] !== undefined) {
				result += "," + key + "=" + priorities[key];
			}
		}
	}

	return result;
}

function parseAIBehaviorAndEconomy(aiBehavior)
{
	const behavior = (aiBehavior || "random").split(";")[0].trim() || "random";
	let economy = { ...DEFAULT_AI_ECONOMY };
	let military = {};
	let priorities = {};

	// Parse supportRatio from AIBehavior string (legacy format)
	const match = (aiBehavior || "").match(/supportRatio=([\d.]+)/);
	if (match)
		economy.supportRatio = parseFloat(match[1]);

	// Parse all other parameters from AIBehavior string
	const paramString = (aiBehavior || "").split(";")[1] || "";
	const pairs = paramString.split(",");
	for (const pair of pairs) {
		const [key, value] = pair.split("=");
		if (!key || !value)
			continue;

		// Determine which object the key belongs to
		if (key === "supportRatio") {
			economy[key] = parseFloat(value);
		} else if (["popPhase2", "workPhase3", "workPhase4", "popForDock", "targetNumWorkers", "targetNumTraders", "targetNumFishers", "provisionFields"].includes(key)) {
			economy[key] = parseFloat(value);
		} else if (["rushAttack", "hugeAttack", "defaultAttack", "towerLapseTime", "fortressLapseTime", "popForBarracks1", "popForBarracks2", "popForForge", "numSentryTowers"].includes(key)) {
			military[key] = parseFloat(value);
		} else {
			priorities[key] = parseFloat(value);
		}
	}

	return { behavior, economy, military, priorities };
}

GameSettings.prototype.Attributes.PlayerAI = class PlayerAI extends GameSetting
{
	init()
	{
		// NB: watchers aren't auto-triggered when modifying array elements.
		this.values = [];
		this.settings.playerCount.watch(() => this.maybeUpdate(), ["nbPlayers"]);
	}

	toInitAttributes(attribs)
	{
		if (!attribs.settings.PlayerData)
			attribs.settings.PlayerData = [];
		while (attribs.settings.PlayerData.length < this.values.length)
			attribs.settings.PlayerData.push({});
		for (let i = 0; i < this.values.length; ++i)
			if (this.values[i])
			{
				attribs.settings.PlayerData[i].AI = this.values[i].bot;
				attribs.settings.PlayerData[i].AIDiff = this.values[i].difficulty;
				const isR28 = this.values[i].bot && String(this.values[i].bot).toLowerCase().includes("r28");
				attribs.settings.PlayerData[i].AIBehavior = isR28
					? encodeAIBehaviorWithEconomy(this.values[i].behavior, this.values[i].economy || DEFAULT_AI_ECONOMY, this.values[i].military, this.values[i].priorities)
					: (this.values[i].behavior || "random");
				attribs.settings.PlayerData[i].AIEconomy = this.values[i].economy || { ...DEFAULT_AI_ECONOMY };
				if (this.values[i].military)
					attribs.settings.PlayerData[i].AIMilitary = this.values[i].military;
				if (this.values[i].priorities)
					attribs.settings.PlayerData[i].AIPriorities = this.values[i].priorities;
			}
			else
				attribs.settings.PlayerData[i].AI = false;
	}

	fromInitAttributes(attribs)
	{
		if (!this.getLegacySetting(attribs, "PlayerData"))
			return;
		const pData = this.getLegacySetting(attribs, "PlayerData");
		for (let i = 0; i < this.values.length; ++i)
		{
			// Also covers the "" case.
			if (!pData[i] || !pData[i].AI)
			{
				this.set(+i, undefined);
				continue;
			}
			const parsed = parseAIBehaviorAndEconomy(pData[i].AIBehavior);
			const economy = pData[i].AIEconomy
				? { ...DEFAULT_AI_ECONOMY, ...pData[i].AIEconomy, ...parsed.economy }
				: parsed.economy;
			const military = pData[i].AIMilitary || parsed.military;
			const priorities = pData[i].AIPriorities || parsed.priorities;
			this.set(+i, {
				"bot": pData[i].AI,
				"difficulty": pData[i].AIDiff ?? +Engine.ConfigDB_GetValue("user", "gui.gamesetup.aidifficulty"),
				"behavior": parsed.behavior,
				"economy": economy,
				"military": military,
				"priorities": priorities,
			});
		}
	}

	_resize(nb)
	{
		while (this.values.length > nb)
			this.values.pop();
		while (this.values.length < nb)
			this.values.push(undefined);
	}

	maybeUpdate()
	{
		if (this.values.length === this.settings.playerCount.nbPlayers)
			return;
		this._resize(this.settings.playerCount.nbPlayers);
		this.trigger("values");
	}

	swap(sourceIndex, targetIndex)
	{
		[this.values[sourceIndex], this.values[targetIndex]] = [this.values[targetIndex], this.values[sourceIndex]];
		this.trigger("values");
	}

	set(playerIndex, botSettings)
	{
		if (botSettings && botSettings.economy === undefined)
			botSettings = { ...botSettings, "economy": { ...DEFAULT_AI_ECONOMY } };
		this.values[playerIndex] = botSettings;
		this.trigger("values");
	}

	setAI(playerIndex, ai)
	{
		const old = this.values[playerIndex] ? this.values[playerIndex].bot : undefined;
		if (!ai)
			this.values[playerIndex] = undefined;
		else
			this.values[playerIndex].bot = ai;
		if (old !== (this.values[playerIndex] ? this.values[playerIndex].bot : undefined))
			this.trigger("values");
	}

	setBehavior(playerIndex, value)
	{
		if (!this.values[playerIndex])
			return;
		this.values[playerIndex].behavior = value;
		this.trigger("values");
	}

	setDifficulty(playerIndex, value)
	{
		if (!this.values[playerIndex])
			return;
		this.values[playerIndex].difficulty = value;
		this.trigger("values");
	}

	setEconomy(playerIndex, value)
	{
		if (!this.values[playerIndex])
			return;
		this.values[playerIndex].economy = value;
		this.trigger("values");
	}

	setMilitary(playerIndex, value)
	{
		if (!this.values[playerIndex])
			return;
		this.values[playerIndex].military = value;
		this.trigger("values");
	}

	setPriorities(playerIndex, value)
	{
		if (!this.values[playerIndex])
			return;
		this.values[playerIndex].priorities = value;
		this.trigger("values");
	}

	get(playerIndex)
	{
		return this.values[playerIndex];
	}

	describe(playerIndex)
	{
		if (!this.values[playerIndex])
			return "";
		const v = this.values[playerIndex];
		const out = {
			"AI": v.bot,
			"AIDiff": v.difficulty,
			"AIBehavior": v.behavior,
		};
		if (v.economy)
			out.AIEconomy = v.economy;
		return translateAISettings(out);
	}
};
