import { BaseAI } from "simulation/ai/common-api/baseAI.js";
import { Entity } from "simulation/ai/common-api/entity.js";
import { Config } from "simulation/ai/r28/config.js";
import { Headquarters } from "simulation/ai/r28/headquarters.js";
import { Queue } from "simulation/ai/r28/queue.js";
import { QueueManager } from "simulation/ai/r28/queueManager.js";

export function R28Bot(settings)
{
	BaseAI.call(this, settings);

	this.playedTurn = 0;
	this.elapsedTime = 0;

	this.uniqueIDs = {
		"armies": 1,	// starts at 1 to allow easier tests on armies ID existence
		"bases": 1,	// base manager ID starts at one because "0" means "no base" on the map
		"plans": 0,	// training/building/research plans
		"transports": 1	// transport plans start at 1 because 0 might be used as none
	};

	this.Config = new Config(settings.difficulty, settings.behavior);

	this.savedEvents = {};
}

R28Bot.prototype = Object.create(BaseAI.prototype);

R28Bot.prototype.CustomInit = function(gameState)
{
	if (this.isDeserialized)
	{
		// WARNING: the deserializations should not modify the metadatas infos inside their init functions
		this.turn = this.data.turn;
		this.playedTurn = this.data.playedTurn;
		this.elapsedTime = this.data.elapsedTime;
		this.savedEvents = this.data.savedEvents;
		for (const key in this.savedEvents)
		{
			for (const i in this.savedEvents[key])
			{
				if (!this.savedEvents[key][i].entityObj)
					continue;
				const evt = this.savedEvents[key][i];
				const evtmod = {};
				for (const keyevt in evt)
				{
					evtmod[keyevt] = evt[keyevt];
					evtmod.entityObj = new Entity(gameState.sharedScript, evt.entityObj);
					this.savedEvents[key][i] = evtmod;
				}
			}
		}

		this.Config.Deserialize(this.data.config);

		this.queueManager = new QueueManager(this.Config, {});
		this.queueManager.Deserialize(gameState, this.data.queueManager);
		this.queues = this.queueManager.queues;

		this.HQ = new Headquarters(this.Config);
		this.HQ.init(gameState, this.queues);
		this.HQ.Deserialize(gameState, this.data.HQ);

		this.uniqueIDs = this.data.uniqueIDs;
		this.isDeserialized = false;
		this.data = undefined;

		// initialisation needed after the completion of the deserialization
		this.HQ.postinit(gameState);
	}
	else
	{
		// Parse base behavior and all parameters from AIBehavior string (may contain ";supportRatio=0.1,popPhase2=80,...")
		const rawBehavior = this.Config.behavior || "";
		const semicolon = rawBehavior.indexOf(";");
		let userParams = {};
		if (semicolon !== -1)
		{
			this.Config.behavior = rawBehavior.substring(0, semicolon).trim() || "random";
			const paramString = rawBehavior.substring(semicolon + 1);
			const pairs = paramString.split(",");
			for (const pair of pairs) {
				const [key, value] = pair.split("=");
				if (key && value !== undefined) {
					userParams[key] = isNaN(value) ? value : parseFloat(value);
				}
			}
		}
		this.Config.setConfig(gameState);
		// Apply user's parameters from AIBehavior string after setConfig (setConfig overwrites them by difficulty)
		for (const key in userParams) {
			if (this.Config.Economy[key] !== undefined) {
				this.Config.Economy[key] = userParams[key];
			} else if (this.Config.Military[key] !== undefined) {
				this.Config.Military[key] = userParams[key];
			} else if (this.Config.priorities[key] !== undefined) {
				this.Config.priorities[key] = userParams[key];
			}
		}
		const playerData = globalThis.g_InitPlayerData && globalThis.g_InitPlayerData[this.player];

		// Apply all custom AIEconomy fields
		if (playerData && playerData.AIEconomy) {
			for (const key in playerData.AIEconomy) {
				if (this.Config.Economy[key] !== undefined) {
					this.Config.Economy[key] = playerData.AIEconomy[key];
				}
			}
		}

		// Apply all custom AIMilitary fields
		if (playerData && playerData.AIMilitary) {
			for (const key in playerData.AIMilitary) {
				if (this.Config.Military[key] !== undefined) {
					this.Config.Military[key] = playerData.AIMilitary[key];
				}
			}
		}

		// Apply all custom AIPriorities fields
		if (playerData && playerData.AIPriorities) {
			for (const key in playerData.AIPriorities) {
				if (this.Config.priorities[key] !== undefined) {
					this.Config.priorities[key] = playerData.AIPriorities[key];
				}
			}
		}

		// this.queues can only be modified by the queue manager or things will go awry.
		this.queues = {};
		for (const i in this.Config.priorities)
			this.queues[i] = new Queue();

		this.queueManager = new QueueManager(this.Config, this.queues);

		this.HQ = new Headquarters(this.Config);

		this.HQ.init(gameState, this.queues);

		// Try to analyze our starting position and set a strategy.
		this.canPlay = this.HQ.gameAnalysis(gameState);
	}
};

R28Bot.prototype.OnUpdate = function(sharedScript)
{
	if (this.gameFinished || this.gameState.playerData.state == "defeated")
		return;

	for (const i in this.events)
	{
		if (i == "AIMetadata")   // not used inside r28
			continue;
		if (this.savedEvents[i] !== undefined)
			this.savedEvents[i] = this.savedEvents[i].concat(this.events[i]);
		else
			this.savedEvents[i] = this.events[i];
	}

	// Run the update every n turns, offset depending on player ID to balance the load
	this.elapsedTime = this.gameState.getTimeElapsed() / 1000;
	if (!this.playedTurn || (this.turn + this.player) % 8 == 5)
	{
		Engine.ProfileStart("R28Bot bot (player " + this.player +")");

		this.playedTurn++;

		if (!this.canPlay)
		{
			Engine.ProfileStop();
			return;
		}

		this.HQ.update(this.gameState, this.queues, this.savedEvents);

		this.queueManager.update(this.gameState);

		for (const i in this.savedEvents)
			this.savedEvents[i] = [];

		Engine.ProfileStop();
	}

	this.turn++;
};

R28Bot.prototype.Serialize = function()
{
	const savedEvents = {};
	for (const key in this.savedEvents)
	{
		savedEvents[key] = this.savedEvents[key].slice();
		for (const i in savedEvents[key])
		{
			if (!savedEvents[key][i] || !savedEvents[key][i].entityObj)
				continue;
			const evt = savedEvents[key][i];
			const evtmod = {};
			for (const keyevt in evt)
				evtmod[keyevt] = evt[keyevt];
			evtmod.entityObj = evt.entityObj._entity;
			savedEvents[key][i] = evtmod;
		}
	}

	return {
		"uniqueIDs": this.uniqueIDs,
		"turn": this.turn,
		"playedTurn": this.playedTurn,
		"elapsedTime": this.elapsedTime,
		"savedEvents": savedEvents,
		"config": this.Config.Serialize(),
		"queueManager": this.queueManager.Serialize(),
		"HQ": this.HQ.Serialize()
	};
};

R28Bot.prototype.Deserialize = function(data, sharedScript)
{
	this.isDeserialized = true;
	this.data = data;
};
