"use strict";
this.name = "ShipConfiguration_Armour";
this.author = "phkb";
this.copyright = "2016 phkb";
this.description = "Routines for armour control";
this.licence = "CC BY-NC-SA 3.0";

// Code borrowed heavily from Thargoids "IronHide Armour" OXP and CSotB's "Custom Shields" OXP.

this._debug = false;
this._override = false; // flag to override the equipment damage routine.
this._armourList = [];
this._frontArmourList = [];
this._aftArmourList = [];
this._altArmour = null;
this._holdArmour = {};
this._simRunning = false;

//-------------------------------------------------------------------------------------------------------------
this.startUpComplete = function () {
	if (worldScripts.ShipConfiguration_Core._disabled) {
		delete this.playerWillSaveGame;
		delete this.shipLaunchedFromStation;
		delete this.shipTakingDamage;
		delete this.equipmentDamaged;
		delete this.equipmentRemoved;
		delete this.equipmentAdded;
		delete this.playerBoughtEquipment;
		delete this.guiScreenWillChange;
		delete this.startUpComplete;
		return;
	}

	var ps = player.ship.script;
	ps._frontDamagePoint = 0;
	ps._frontArmourStrength = 0;
	ps._armourFront = 0;
	ps._aftDamagePoint = 0;
	ps._aftArmourStrength = 0;
	ps._armourAft = 0;

	if (missionVariables.ShipConfig_ArmourFront) ps._armourFront = missionVariables.ShipConfig_ArmourFront;
	if (missionVariables.ShipConfig_ArmourAft) ps._armourAft = missionVariables.ShipConfig_ArmourAft;
	this.$grabLocalData();

	if (worldScripts.ShipRespray) {
		var sr = worldScripts.ShipRespray;
		if (sr.$addPreSprayCall) {
			sr.$addPreSprayCall(this.name, "$preResprayFunction");
			sr.$addPostSprayCall(this.name, "$postResprayFunction");
		}
	}
}

//-------------------------------------------------------------------------------------------------------------
this.playerWillSaveGame = function () {
	missionVariables.ShipConfig_ArmourFront = player.ship.script._armourFront;
	missionVariables.ShipConfig_ArmourAft = player.ship.script._armourAft;
}

//-------------------------------------------------------------------------------------------------------------
this.shipLaunchedFromStation = function (station) {
	// get the damage percent for any armour we have in place
	var p = player.ship;
	var ps = p.script;
	var sc = worldScripts.ShipConfiguration_Core;
	if (sc.$simulatorRunning()) {
		this._simRunning = true;
		this.$preResprayFunction();
	}

	ps._frontArmourType = sc.$equipmentItemInUse("frontarmour", p);
	if (ps._frontArmourType != "") {
		var info = EquipmentInfo.infoForKey(ps._frontArmourType);
		ps._frontDamagePoint = info.scriptInfo.damage_point;
		ps._frontArmourStrength = info.scriptInfo.armour_strength;
	} else {
		ps._frontDamagePoint = 0;
		ps._frontArmourStrength = 0;
		ps._armourFront = 0;
	}
	ps._aftArmourType = sc.$equipmentItemInUse("aftarmour", p);
	if (ps._aftArmourType != "") {
		var info = EquipmentInfo.infoForKey(ps._aftArmourType);
		ps._aftDamagePoint = info.scriptInfo.damage_point;
		ps._aftArmourStrength = info.scriptInfo.armour_strength;
	} else {
		ps._aftDamagePoint = 0;
		ps._aftArmourStrength = 0;
		ps._armourAft = 0;
	}
	ps.$resetFlag = this.$resetFlag;
	ps.$damageDirection = this.$damageDirection;
	ps._frontDamageWarning = false;
	ps._aftDamageWarning = false;
	ps._shipDamage = false;

	this.$updateBattleDamage();
}

//-------------------------------------------------------------------------------------------------------------
this.shipTakingDamage = function (amount, whom, type) {

	// if whom isn't valid, don't do anything
	if (!whom || whom.isValid === false) return;

	var p = player.ship;
	var ps = p.script;
	if (!p || !ps) return;
	
	// start a timer to reset the shipDamage flag, so we can know what's damaged from here, and what's scripted from elsewhere
	if (ps._checkTimer == null || ps._checkTimer.isRunning === false) {
		if (!ps.$resetFlag) ps.$resetFlag = this.$resetFlag; // need to check this here for combat simulator
		ps._checkTimer = new Timer(ps, ps.$resetFlag, 1, 0);
	}

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

	ps._shipDamage = true;

	// record where the last damage came from. True = front, false = aft
	ps._lastDamageDirection = ps.$damageDirection(player.ship, whom);
	if (ps._lastDamageDirection === true) {
		ps._lastDamageDirectionArmour = ps._armourFront;
	} else {
		ps._lastDamageDirectionArmour = ps._armourAft;
	}
	//if (this._debug) log(this.name, type + " " + amount + " hit from " + (this._lastDamageDirection === true ? "front" : "aft") + " " + whom);

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

		//if (this._debug) log(this.name, "amount = " + amount + ", front = " + this._armourFront + ", aft = " + this._armourAft + ", shield = " + shield + ", energy = " + player.ship.energy + ", maxEnergy = " + player.ship.maxEnergy);
		if (amount < shield && shield > 0) {
			if ((p.energy + amount) < p.maxEnergy) {
				p.energy += amount;
				//if (this._debug) log(this.name, "armour absorbed hit - returning " + amount + " to energy " + player.ship.energy + ", " + amount + ", " + shield);
			} else {
				//if (this._debug) log(this.name, "armour absorbed hit - returning to energy to max " + player.ship.energy + ", " + amount + ", " + shield);
				p.energy = p.maxEnergy;
			}

			if (ps._lastDamageDirection === true) {
				ps._armourFront -= Math.floor(100 * (amount / ps._frontArmourStrength));
				if (ps._armourFront < 100) {
					player.consoleMessage(expandDescription("[sc_front_armour_warning]", {amount: ps._armourFront}));
					//if (this._debug) log(this.name, "Forward armour down to " + this._armourFront + "%");
					if (ps._armourFront < ps._frontDamagePoint && ps._frontDamageWarning === false) {
						ps._frontDamageWarning = true;
						player.consoleMessage(expandDescription("[sc_front_armour_damagepoint]"));
						//if (this._debug) log(this.name, "Damage point on forward armour reached! Equipment damage may occur!");
					}
				}
			} else {
				ps._armourAft -= Math.floor(100 * (amount / ps._aftArmourStrength));
				if (ps._armourAft < 100) {
					player.consoleMessage(expandDescription("[sc_aft_armour_warning]", {amount: ps._armourAft}));
					//if (this._debug) log(this.name, "Aft armour down to " + this._armourAft + "%");
					if (ps._armourAft < ps._aftDamagePoint && ps._aftDamageWarning === false) {
						ps._aftDamageWarning = true;
						player.consoleMessage(expandDescription("[sc_front_armour_damagepoint]"));
						//if (this._debug) log(this.name, "Damage point on aft armour reached! Equipment damage may occur!");
					}
				}
			}
		} else {
			if (shield > 0) p.energy += shield;
			if (ps._lastDamageDirection === true) {
				ps._armourFront = 0;
			} else {
				ps._armourAft = 0;
			}
		}
	}
	// check to see if the armour has been completely destroyed
	if (ps._armourFront <= 0 && p.equipmentStatus(ps._frontArmourType) === "EQUIPMENT_OK") {
		// if the armour is destroyed by damage
		p.removeEquipment(ps._frontArmourType);
		delete worldScripts.ShipConfiguration_Core._current["frontarmour"];
		player.consoleMessage(expandDescription("[sc_front_armour_destroyed]"), 6);
	}
	if (ps._armourAft <= 0 && p.equipmentStatus(ps._aftArmourType) === "EQUIPMENT_OK") {
		// if the armour is destroyed by damage
		p.removeEquipment(ps._aftArmourType);
		delete worldScripts.ShipConfiguration_Core._current["aftarmour"];
		player.consoleMessage(expandDescription("[sc_aft_armour_destroyed]"), 6);
	}
}

//-------------------------------------------------------------------------------------------------------------
// returns true if hit is from the front, otherwise false
// from CSotB's Custom Shields OXP
this.$damageDirection = function (ship, whom) {

	if (!ship || ship.isValid === false || !whom || whom.isValid === false) return true;

	var shipSpeedCollisionRadiusAdjustment = (ship.maxSpeed / 350) * 8;
	//added for shield collision issue caused by using solid models which are needed to work correctly in 1.76, applies to advanced and basic npcs also
	if (ship.speed > ship.maxSpeed) {
		var collisionRadiusAdjustment = (ship.speed / ship.maxSpeed) * shipSpeedCollisionRadiusAdjustment;
	} else {
		var collisionRadiusAdjustment = 0;
	}

	var impactRadiusFactor = ((3 * (1500 / (ship.position.distanceTo(whom)))) + 16);
	var range = whom.position.distanceTo(ship) - (ship.collisionRadius + 1 + collisionRadiusAdjustment);
	if (isNaN(range)) return true;
	var dist = ship.collisionRadius + collisionRadiusAdjustment + impactRadiusFactor;
	var impactLocation = whom.position.add(whom.vectorForward.multiply(range)); //determine possible forward weapon hit location
	//too far from ship because of delay to hit from plasma weapon or off vector angle because of thargoid laser
	if ((ship.position.distanceTo(impactLocation)) > dist) {
		impactLocation = whom.position.add(whom.vectorForward.multiply(-(range))); //determine possible aft weapon hit location
		//too far from ship because of delay to hit from plasma weapon or off vector angle because of thargoid laser
		if ((ship.position.distanceTo(impactLocation)) > dist) {
			impactLocation = whom.position.add(whom.vectorRight.multiply(range)); //determine possible starboard weapon hit location
			//too far from ship because of delay to hit from plasma weapon or off vector angle because of thargoid laser
			if ((ship.position.distanceTo(impactLocation)) > dist) {
				impactLocation = whom.position.add(whom.vectorRight.multiply(-(range))); //determine possible port weapon hit location
				//too far from ship because of delay to hit from plasma weapon or off vector angle because of thargoid laser
				if ((ship.position.distanceTo(impactLocation)) > dist) {
					var impactInterpolation = ((ship.collisionRadius + 1 + collisionRadiusAdjustment) / (ship.position.distanceTo(whom)));
					impactLocation = Vector3D.interpolate(ship.position, whom.position, impactInterpolation); //interpolate weapon hit directly between ship and attacker as a fallback position
				}
			}
		}
	}
	var impactdirection = impactLocation.subtract(ship.position).direction();
	var impactangle = ship.heading.angleTo(impactdirection);

	if (impactangle <= 1.57) {
		return true;
	} else {
		return false;
	}
}

//-------------------------------------------------------------------------------------------------------------
this.equipmentDamaged = function (equipment) {
	var p = player.ship;
	var ps = p.script;
	if (!p || !ps) return;
	// as it doesn't make sense for the armour to be inoperable-damaged
	if (this._armourList && this._armourList.indexOf(equipment) >= 0) {
		player.ship.setEquipmentStatus(equipment, "EQUIPMENT_OK");
		return;
	}
	// if we're specifically overriding the damage report routine, exit now
	if (this._override === true) return;
	// only continue if damage is coming from shipTakingDamage - if not, it's probably a scripted damage which we want to leave alone
	if (ps._shipDamage === false) return;

	var dp = 0;
	if (ps._lastDamageDirection === true) {
		ps._lastDamageDirectionArmour = ps._armourFront;
		dp = ps._frontDamagePoint;
		// how much armour do we have left? if there's less than the percentage set for each type of armour there's a chance the damage will stay
		if (ps._lastDamageDirectionArmour === 0 && ps._armourFront > 95) ps._lastDamageDirectionArmour = 100;
	} else {
		ps._lastDamageDirectionArmour = ps._armourAft;
		dp = ps._aftDamagePoint;
		// how much armour do we have left? if there's less than the percentage set for each type of armour there's a chance the damage will stay
		if (ps._lastDamageDirectionArmour === 0 && ps._armourAft > 95) ps._lastDamageDirectionArmour = 100;
	}

	//if (this._debug) log(this.name, "Equipment damaged: " + equipment);
	if (ps._lastDamageDirectionArmour <= dp) {
		// the more damaged the armour is, the more change there is of taking equipment damage
		//var diff = (this._lastDamageDirectionArmour / (this._damagePoint + (100 - this._lastDamageDirection)));
		var diff = (ps._lastDamageDirectionArmour / (dp + (dp - ps._lastDamageDirectionArmour)));
		//if (this._debug) log(this.name, "Armour = " + this._lastDamageDirectionArmour + ", diff = " + diff);
		// the less armour we have the greater the chance the damage will stay
		if (Math.random() < diff) {
			p.setEquipmentStatus(equipment, "EQUIPMENT_OK");
			//if (this._debug) log(this.name, "Armour took hit instead - equipment repaired")
		}
	} else {
		// if we still have armour above the damage point, equipment shouldn't get damaged
		p.setEquipmentStatus(equipment, "EQUIPMENT_OK");
		//if (this._debug) log(this.name, "Armour still OK - equipment repaired");
	}
}

//-------------------------------------------------------------------------------------------------------------
this.equipmentRemoved = function (equipment) {
	if (this._frontArmourList && this._frontArmourList.indexOf(equipment) >= 0 && player.ship.isInSpace === false) {
		var ps = player.ship.script;
		ps._frontArmourType = "";
		ps._armourFront = 0;
		this.$updateManifest();
	}
	if (this._aftArmourList && this._aftArmourList.indexOf(equipment) >= 0 && player.ship.isInSpace === false) {
		var ps = player.ship.script;
		ps._aftArmourType = "";
		ps._armourAft = 0;
		this.$updateManifest();
	}
}

//-------------------------------------------------------------------------------------------------------------
this.equipmentAdded = function (equipment) {
	if (this._frontArmourList && this._frontArmourList.indexOf(equipment) >= 0 && player.ship.isInSpace === false) {
		var ps = player.ship.script;
		ps._armourFront = 100;
		ps._frontArmourType = equipment;
	}
	if (this._aftArmourList && this._aftArmourList.indexOf(equipment) >= 0 && player.ship.isInSpace === false) {
		var ps = player.ship.script;
		ps._armourAft = 100;
		ps._aftArmourType = equipment;
	}
}

//-------------------------------------------------------------------------------------------------------------
this.playerBoughtEquipment = function (equipment) {
	var p = player.ship;
	var ps = p.script;
	this.$grabLocalData();
	// if the player installs some alternative armour, remove our one (if installed), so as to not double up
	if (this._altArmour.indexOf(equipment) >= 0) {
		if (ps._frontArmourType != "") {
			// refund the cost 
			var info = EquipmentInfo.infoForKey(ps._frontArmourType);
			var dam = p.equipmentStatus(ps._frontArmourType);
			var refund = (info.price / 10) * worldScripts.ShipConfiguration_Core._refundPct; //stn.equipmentPriceFactor;
			if (dam === "EQUIPMENT_DAMAGED") refund *= 0.5;
			player.credits += refund;

			p.removeEquipment(ps._frontArmourType);
		}
		if (ps._aftArmourType != "") {
			// refund the cost 
			var info = EquipmentInfo.infoForKey(ps._aftArmourType);
			var dam = p.equipmentStatus(ps._aftArmourType);
			var refund = (info.price / 10) * worldScripts.ShipConfiguration_Core._refundPct; //stn.equipmentPriceFactor;
			if (dam === "EQUIPMENT_DAMAGED") refund *= 0.5;
			player.credits += refund;

			p.removeEquipment(ps._aftArmourType);
		}
	}
	// check for repair to armour
	if (equipment.indexOf("_REPAIR") >= 0) {
		// repair the armour
		if (equipment.indexOf(ps._frontArmourType) >= 0 && ps._armourFront < 100) {
			p.removeEquipment(equipment);
			ps._armourFront = 100;
		}
		if (equipment.indexOf(ps._aftArmourType) >= 0 && ps._armourAft < 100) {
			p.removeEquipment(equipment);
			ps._armourAft = 100;
		}
	}
}

//-------------------------------------------------------------------------------------------------------------
this.guiScreenWillChange = function (to, from) {
	if (to === "GUI_SCREEN_MANIFEST") {
		this.$updateManifest();
	}
	if (to === "GUI_SCREEN_STATUS" && this._simRunning === true) {
		this._simRunning = false;
		this.$postResprayFunction();
	}
}

//-------------------------------------------------------------------------------------------------------------
this.$updateManifest = function $updateManifest() {
	var ps = player.ship.script;
	var eqtype_fr = worldScripts.ShipConfiguration_Core.$equipmentItemInUse("frontarmour", player.ship);
	var eqtype_af = worldScripts.ShipConfiguration_Core.$equipmentItemInUse("aftarmour", player.ship);
	if (player.ship.equipmentStatus(eqtype_fr) === "EQUIPMENT_OK" || player.ship.equipmentStatus(eqtype_af) === "EQUIPMENT_OK") {
		var sep = ", ";
		var front_type = expandDescription("[sc_front_armour_type]", {type: (this._frontArmourList.indexOf(eqtype_fr) + 1)});
		var front_desc = expandDescription("[sc_armour_strength]");
		var front_val = ps._armourFront + "%%";
		if (eqtype_fr === "") {
			front_type = "";
			front_desc = "";
			front_val = "";
			sep = "";
		}
		var aft_type = expandDescription("[sc_aft_armour_type]", {type: (this._aftArmourList.indexOf(eqtype_af) + 1)});
		var aft_desc = expandDescription("[sc_armour_strength]");
		var aft_val = ps._armourAft + "%%";
		if (eqtype_af === "") {
			aft_type = "";
			aft_desc = "";
			aft_val = "";
			sep = "";
		}
		mission.setInstructions(expandDescription("[armour_manifest]", {
			front_armour_type: front_type,
			front_armour_desc: front_desc,
			armour_front: front_val,
			armour_sep: sep,
			aft_armour_type: aft_type,
			aft_armour_desc: aft_desc,
			armour_aft: aft_val
		}), this.name);
	} else {
		mission.setInstructions(null, this.name);
	}
}

//-------------------------------------------------------------------------------------------------------------
this.$resetFlag = function $resetFlag() {
	if (player.ship && player.ship.script) {
		delete player.ship.script._checkTimer;
		player.ship.script._shipDamage = false;
	}
}

//-------------------------------------------------------------------------------------------------------------
// if "BattleDamage" is installed, we want to update the shipTakingDamage routine to take into consideration how much armour we have left
// so we'll patch BattleDamage to add the extra logic
this.$updateBattleDamage = function $updateBattleDamage() {
	if (worldScripts["Battle Damage"]) {
		var bd = worldScripts["Battle Damage"];
		delete bd.shipTakingDamage;
		//bd.$sc_shipTakingDamage = bg.shipTakingDamage;
		bd.shipTakingDamage = this.$newBD_shipTakingDamage;
	}
}

//-------------------------------------------------------------------------------------------------------------
// New "shipTakingDamage" routine for BattleDamage
this.$newBD_shipTakingDamage = function $newBD_shipTakingDamage(amount, whom, type) {
	if (amount === 0) return;
	else if (missionVariables.BattleDamage_status === "DAMAGED") {
		player.consoleMessage(expandDescription("[sc_armour_hull_damage]"), 1);
	} else {
		var skip = false;
		var ps = player.ship.script;
		if (ps && ps._lastDamageDirectionArmour > 0) {
			if (ps._lastDamageDirectionArmour < ps._damagePoint) {
				// the less armour we have the greater the chance the damage will stay
				if (Math.random() < (ps._lastDamageDirectionArmour / ps._damagePoint)) skip = true;
			} else {
				skip = true;
			}
		}
		if (skip === false) {
			player.ship.removeEquipment("EQ_HULL_REPAIR"); //remove 'repair' to allow 'repair' to be applied again. This should happen before any risk of damage to 'repair'
			missionVariables.BattleDamage_status = "DAMAGED"; // set to damaged
			player.consoleMessage(expandDescription("[sc_armour_hull_damaged]"), 6);
		}
	}
}

//-------------------------------------------------------------------------------------------------------------
this.$preResprayFunction = function $preResprayFunction() {
	this._holdArmour["front"] = player.ship.script._armourFront;
	this._holdArmour["aft"] = player.ship.script._armourAft;
}

//-------------------------------------------------------------------------------------------------------------
this.$postResprayFunction = function $postResprayFunction() {
	player.ship.script._armourFront = this._holdArmour["front"];
	player.ship.script._armourAft = this._holdArmour["aft"];
	this._holdArmour = {};
}

//-------------------------------------------------------------------------------------------------------------
this.$grabLocalData = function $grabLocalData() {
	var sc = worldScripts.ShipConfiguration_Core;
	var ps = player.ship.script;
	ps._frontArmourType = sc.$equipmentItemInUse("frontarmour", player.ship);
	if (ps._frontArmourType != "") {
		var info = EquipmentInfo.infoForKey(ps._frontArmourType);
		ps._frontDamagePoint = info.scriptInfo.damage_point;
		ps._frontArmourStrength = info.scriptInfo.armour_strength;
	}
	ps._aftArmourType = sc.$equipmentItemInUse("aftarmour", player.ship);
	if (ps._aftArmourType != "") {
		var info = EquipmentInfo.infoForKey(ps._aftArmourType);
		ps._aftDamagePoint = info.scriptInfo.damage_point;
		ps._aftArmourStrength = info.scriptInfo.armour_strength;
	}

	// grab a local reference to the armour arrays, so we don't have to keep two lists, but don't need to keep references to the other module
	this._armourList = sc._armour;
	this._frontArmourList = sc._frontarmour;
	this._aftArmourList = sc._aftarmour;
	this._altArmour = sc._altArmour;
}