"use strict";
this.name = "ShipConfiguration_NPCShips";
this.author = "phkb";
this.copyright = "2016 phkb";
this.description = "Routines for applying ShipConfiguration details to NPC ships.";
this.licence = "CC BY-NC-SA 3.0";

this._useExclusions = true; // flag to control which method will be used to apply config: includeRoles or excludeRoles
//this._disable_NShields_compatibility = true;

// includeRoles only used when "this._useExclusions === false". All ships with these primaryRoles will get config applied. No other ships will.
this._includeRoles = ["trader", "trader-courier", "trader-smuggler", "shuttle", "scavenger", "miner",
	"pirate", "pirate-light-fighter", "pirate-medium-fighter", "pirate-heavy-fighter", "pirate-interceptor",
	"pirate-light-freighter", "pirate-medium-freighter", "pirate-heavy-freighter",
	"pirate-light-fighter", "pirate-medium-fighter", "pirate-heavy-fighter",
	"hunter", "hunter-medium", "hunter-heavy", "assassin-light", "assassin-medium", "assassin-heavy",
	"escort", "escort-light", "escort-medium", "escort-heavy", "wingman", "police", "police-station-patrol", "police-witchpoint-patrol"
];

// excludeRoles only used when "this._useExclusions === true". Ships with these primaryRoles won't get configuration. All other ships will.
// however, only those ships with data defined in core._NPCConfig will have a specific configuration. If no config is found, ships will be given default equipment.
this._excludeRoles = ["RSsatellite"];

/* note the following roles/ship types are already excluded:
	Thargoids
	Stations
	Cargo
	Missiles/Mines/Weapons
	Subentities
	Visual Effects
	Ships with no pilot
	Rocks

	To exclude a particular ship, include the following in the "script_info" section of the shipdata.plist entry for the ship:
		"no_sc_config" = "yes";
*/
this._trueValues = ["yes", "1", "true", 1, true];

//-------------------------------------------------------------------------------------------------------------
this.startUpComplete = function () {
	if (worldScripts.ShipConfiguration_Core._disabled) {
		delete this.shipSpawned;
		delete this.startUpComplete;
		return;
	}
}

//-------------------------------------------------------------------------------------------------------------
this.shipSpawned = function (ship) {
	if (ship.isStation === true) return;
	if (ship.isThargoid === true) return;
	if (ship.isPiloted === false) return;
	if (ship.isCargo === true) return;
	if (ship.isRock === true) return;
	if (ship.isWeapon === true) return;
	if (ship.isMissile === true) return;
	if (ship.isMine === true) return;
	if (ship.isSubEntity === true) return;
	if (ship.isBeacon === true) return;
	if (ship.isVisualEffect === true) return;
	if (ship.scanClass == "CLASS_NO_DRAW") return;
	if (ship.scanClass == "CLASS_ROCK") return;
	if ((this._useExclusions === false && this.$indexInList(ship.primaryRole, this._includeRole) >= 0) ||
		(this._useExclusions === true && this.$indexInList(ship.primaryRole, this._excludeRoles) === -1)) {
		if (!ship.scriptInfo.no_sc_config || this.$indexInList(ship.scriptInfo.no_sc_config, this._trueValues) === -1) {
			var conf = worldScripts.ShipConfiguration_Core;
			if (conf._applyToNPC === true) {
				// add base equipment
				conf.$installBaseEquipment(ship);
				// adjust performance
				conf.$updateShipSpecs(ship);

				ship.script._debugShipConfig = false;

				// make sure existing scripts are kept in monkey patch locations
				// check if NShields are installed
				if (worldScripts.NShields && worldScripts.NShields.$shipHasShields(ship)) {
					// add our shipTaking damage script to NShield's array
					if (!ship.script.NShields_damageHandlers) ship.script.NShields_damageHandlers = []; //N-Shields alteration: set up handler array if it doesn't yet
					ship.script.NShields_damageHandlers.push(this.$npc_shipTakingDamage); //and push the shipconfig handler onto it
				} else {
					// otherwise do a standard monkey patch
					if (ship.script.shipTakingDamage) ship.script.$sc_hold_shipTakingDamage = ship.script.shipTakingDamage;
					// attach our new scripts
					ship.script.shipTakingDamage = this.$npc_shipTakingDamage;
				}

				// attach our new scripts
				if (ship.script.shipRemoved) ship.script.$sc_hold_shipRemoved = ship.script.shipRemoved;
				ship.script.shipRemoved = this.$npc_shipRemoved;
				if (ship.script.shipDied) ship.script.$sc_hold_shipDied = ship.script.shipDied;
				ship.script.shipDied = this.$npc_shipDied;
				if (ship.script.shilWillEnterWormhole) ship.script.$sc_hold_shipWillEnterWormhole = ship.script.shipWillEnterWormhole;
				ship.script.shipWillEnterWormhole = this.$npc_shipWillEnterWormhole;

				ship.script.$sc_stopTimer = this.$sc_stopTimer;
				if (conf._heatControl === true) {
					ship.script.$npc_checkTemp = this.$npc_checkTemp;
					ship.script.$initialise = this.$initialiseTimer;
				}
				ship.script.$damageDirection = worldScripts.ShipConfiguration_Armour.$damageDirection;

				ship.script._frontArmourType = conf.$equipmentItemInUse("frontarmour", ship);
				if (ship.script._frontArmourType != "") {
					var info = EquipmentInfo.infoForKey(ship.script._frontArmourType);
					ship.script._armourFront = 100;
					ship.script._frontDamagePoint = info.scriptInfo.damage_point;
					ship.script._frontArmourStrength = info.scriptInfo.armour_strength;
				}
				ship.script._aftArmourType = conf.$equipmentItemInUse("aftarmour", ship);
				if (ship.script._aftArmourType != "") {
					var info = EquipmentInfo.infoForKey(ship.script._aftArmourType);
					ship.script._armourAft = 100;
					ship.script._aftDamagePoint = info.scriptInfo.damage_point;
					ship.script._aftArmourStrength = info.scriptInfo.armour_strength;
				}

				// add special routines to ship so they can be executed by NPC equipment damage
				if (worldScripts.NPC_Equipment_Damage) {
					ship.script.$equipmentDamaged_Event = conf.$equipmentDamaged_Event;
					ship.script.$awaitEquipmentDamaged = conf.$awaitEquipmentDamaged;
					ship.script.$checkRemainingEnergy = conf.$checkRemainingEnergy;
					ship.script._damagedItems = []; // list of damaged items that required config adjustment
					ship.script._checkForDamage = null; // timer used to call "$awaitEquipmentDamaged" to check the "damagedItems" array
				}

				ship.script.$initialise();
			}
		}
	}
}

//-------------------------------------------------------------------------------------------------------------
// sets up the timer to apply heat transfer to NPC's
this.$initialiseTimer = function $initialiseTimer() {
	// set up the timer with a random interval between 0.5 and 1.5 so we don't get lots of ships updating at the same time
	var freq = parseInt((Math.random() + 0.5) * 100) / 100;
	this.ship.script.$sc_stopTimer();
	this.ship.script._tempCheck = new Timer(this, this.ship.script.$npc_checkTemp, 2 + freq, freq);
}

//-------------------------------------------------------------------------------------------------------------
// stops all the timers
this.$sc_stopTimer = function $sc_stopTimer() {
	if (this.ship.script._tempCheck && this.ship.script._tempCheck.isRunning) {
		this.ship.script._tempCheck.stop();
	}
	delete this.ship.script._tempCheck;

	if (this.ship.script._energyTimer && this.ship.script._energyTimer.isRunning) {
		this.ship.script._energyTimer.stop();
	}
	delete this.ship.script._energyTimer;

	if (this.ship.script._checkForDamage && this.ship.script._checkForDamage.isRunning) {
		this.ship.script._checkForDamage.stop();
	}
	delete this.ship.script._checkForDamage;
}

//-------------------------------------------------------------------------------------------------------------
// routine to apply temp transfer
this.$npc_checkTemp = function $npc_checkTemp() {
	if (this.ship.isValid === false) {
		this.ship.script.$sc_stopTimer();
		return;
	}

	var injectorsEngaged = false;
	if (this.ship.maxSpeed > 0 && this.ship.speed > this.ship.maxSpeed) injectorsEngaged = true;

	var curr = this.ship.temperature;

	// adjust the ship's temp, based on what it's doing at the moment
	// work out the factor, based on the tempcheck interval and heatsink factor
	// that means, if we ever adjust the timer interval, we won't have to also change the factor calc
	// we're also not making the factor as strong as for the player, as the player has real shields and NPC's don't
	var factor = (this.ship.script._tempCheck.interval / (5 / parseFloat(this.ship.script._heat_sink_factor))) * 0.5;
	var exch_rate = (this.ship.temperature > 0.5 ? (1.5 - this.ship.temperature) : 1);
	this.ship.temperature += (((this.ship.laserHeatLevelAft + this.ship.laserHeatLevelForward + this.ship.laserHeatLevelPort + this.ship.laserHeatLevelStarboard +
		(this.ship.isCloaked ? 0.8 : 0) +
		(injectorsEngaged ? (this.ship.injectorBurnRate / 0.25) : 0)) * exch_rate) / 3) * factor;

	//if (this.ship.script._debugShipConfig && curr != this.ship.temperature) {
	//	log(this.name, "Use Orig temp " + curr + ", New temp " + this.ship.temperature + " applied to " + this.ship);
	//}
	if (this.ship.temperature > 0.75 && this.ship.equipmentStatus("EQ_HEAT_SINK") !== "EQUIPMENT_OK") {
		// better turn off that cloak, son
		if (this.ship.isCloaked) this.ship.isCloaked = false;
	}
	// NPC's have to operate with auto-deployment of heat-sinks
	if (this.ship.temperature > 0.75 && this.ship.equipmentStatus("EQ_HEAT_SINK") === "EQUIPMENT_OK") {
		worldScripts.ShipConfiguration_HeatSink.$deployHeatSink(this.ship);
	}
}

//-------------------------------------------------------------------------------------------------------------
this.$npc_shipTakingDamage = function $npc_shipTakingDamage(amount, whom, type) {
	// run monkey patch, if present
	if (this.ship.script.$sc_hold_shipTakingDamage) this.ship.script.$sc_hold_shipTakingDamage(amount, whom, type);

	var thisship = this.ship;
	var scr = thisship.script;

	var fsh = 0;
	var ash = 0;
	// check for NPC shields
	if (worldScripts["NPC-shields"]) {
		fsh += scr.shieldStrength;
		ash += scr.shieldStrength;
	}
	// check for customshields
	if (worldScripts.customshields) {
		fsh += scr.customshieldsforwardshieldlevel;
		ash += scr.customshieldsaftshieldlevel;
	}

	if (whom) scr._lastDamageDirection = scr.$damageDirection(thisship, whom);

	// pass energy damage onto the unshielded ship as a heat increase
	var sc = worldScripts.ShipConfiguration_Core;
	if (type === "energy damage" && sc._heatControl === true && amount > 0 && ((scr._lastDamageDirection === true && fsh === 0) || (scr._lastDamageDirection === false && ash === 0))) {
		var curr = thisship.temperature;
		// npc's already dont have shields, so we're decreasing the ratio of energy that is passed through, as compared with the player.
		var amt = amount / ((thisship.maxEnergy * thisship.heatInsulation) * 2);
		thisship.temperature += Math.pow((1.1 - thisship.temperature), 4) * amt;

		//if (scr._debugShipConfig && curr != thisship.temperature) {
		//	log(this.name, "Hit Orig temp " + curr + ", New temp " + thisship.temperature + " applied to " + thisship);
		//}
	}

	// don't go any further if we don't have ShipConfig armour installed
	if (scr._frontArmourType === "" && scr._aftArmourType === "") return;

	if (whom) {
		// record where the last damage came from. True = front, false = aft
		if (scr._lastDamageDirection === true) {
			scr._lastDamageDirectionArmour = scr._armourFront;
			//if (scr._debugShipConfig && fsh > 0) log(this.name, amount + " hit (absorbed by front shield " + fsh + ") from " + (this._lastDamageDirection === true ? "front" : "aft") + ", ship = " + thisship);
			if (fsh > 0) return; // if we have shields and they're still in place, we don't need to do anything
		} else {
			scr._lastDamageDirectionArmour = scr._armourAft;
			//if (scr._debugShipConfig && ash > 0) log(this.name, amount + " hit (absorbed by front shield " + ash + ") from " + (this._lastDamageDirection === true ? "front" : "aft") + ", ship = " + thisship);
			if (ash > 0) return; // if we have shields and they're still in place, we don't need to do anything
		}
		//if (scr._debugShipConfig) log(this.name, amount + " hit from " + (this._lastDamageDirection === true ? "front" : "aft") + ", ship = " + thisship);

		if (amount && amount > 0 && type === "energy damage") { // if we have energy damage
			var shield = 0;
			if (scr._lastDamageDirection === true) {
				shield = scr._frontArmourStrength * (scr._armourFront / 100);
			} else {
				shield = scr._aftArmourStrength * (scr._armourAft / 100);
			}

			//if (scr._debugShipConfig) log(this.name, "amount = " + amount + ", front = " + scr._armourFront + ", aft = " + scr._armourAft + ", shield = " + shield + ", energy = " + thisship.energy + ", maxEnergy = " + thisship.maxEnergy + ", ship = " + thisship);

			if (amount < shield && shield > 0) {
				if ((thisship.energy + amount) < thisship.maxEnergy) {
					thisship.energy += amount;
				} else {
					thisship.energy = thisship.maxEnergy;
				}

				if (scr._lastDamageDirection === true) {
					scr._armourFront -= Math.floor(100 * (amount / scr._frontArmourStrength));
					//if (scr._armourFront < 100) {
					//	if (scr._debugShipConfig) log(this.name, "Forward armour down to " + scr._armourFront + "%, ship = " + thisship);
					//}
				} else {
					scr._armourAft -= Math.floor(100 * (amount / scr._aftArmourStrength));
					//if (scr._armourAft < 100) {
					//	if (scr._debugShipConfig) log(this.name, "Aft armour down to " + scr._armourAft + "%, ship = " + thisship);
					//}
				}
			} else {
				if (shield > 0) thisship.energy += shield;
				if (scr._lastDamageDirection === true) {
					scr._armourFront = 0;
				} else {
					scr._armourAft = 0;
				}
			}
		}
		// check to see if the armour has been completely destroyed
		if (amount && amount > 0 && thisship.equipmentStatus(scr._frontArmourType) === "EQUIPMENT_OK" && scr._armourFront <= 0) { // if the armour is destroyed by damage
			thisship.removeEquipment(scr._frontArmourType);
			scr._frontArmourType = "";
		}
		if (amount && amount > 0 && thisship.equipmentStatus(scr._aftArmourType) === "EQUIPMENT_OK" && scr._armourAft <= 0) { // if the armour is destroyed by damage
			thisship.removeEquipment(scr._aftArmourType);
			scr._aftArmourType = "";
		}
	}
}

//-------------------------------------------------------------------------------------------------------------
// may not be neccesary if the ent destroyed function runs in all cases.
this.$npc_shipRemoved = function $npc_shipRemoved(suppressDeathEvent) {
	// run monkey patch, if present
	if (this.ship.script.$sc_hold_shipRemoved) this.ship.script.$sc_hold_shipRemoved(suppressDeathEvent);
	//if (this.ship.script._debugShipConfig) log(this.name, "*** shipRemoved for " + this.ship);
	this.ship.script.$sc_stopTimer();
}

//-------------------------------------------------------------------------------------------------------------
this.$npc_shipDied = function $npc_shipDied(whom, why) {
	if (this.ship.script.$sc_hold_shipDied) this.ship.script.$sc_hold_shipDied(whom, why);
	this.ship.script.$sc_stopTimer();
}

//-------------------------------------------------------------------------------------------------------------
this.$npc_shipWillEnterWormhole = function $npc_shipWillEnterWormhole() {
	if (this.ship.script.$sc_hold_shipWillEnterWormhole) this.ship.script.$sc_hold_shipWillEnterWormhole();
	this.ship.script.$sc_stopTimer();
}

//-------------------------------------------------------------------------------------------------------------
// by cag for speed improvements instead of using "indexOf"
this.$indexInList = function $indexInList(item, list) { // for arrays only
	var k = list.length;
	while (k--) {
		if (list[k] === item) return k;
	}
	return -1;
}