"use strict";
this.name = "GalCopBB_DataCache";
this.author = "phkb";
this.copyright = "2017 phkb";
this.description = "Control code for data cache entities (missions 31/41/71)";
this.license = "CC BY-NC-SA 4.0";

this._maxLocations = 0;
this._dataCache = null; // holding object, containing data cache that is about to transfer data to the player
this._dataCacheTimer = null; // timer to control data transfer to player
this._dataDelivery = 0; // counter that controls the percentage of data transferred
this._dataReceived = 0; // counter that controls the percentage of data received by player
this._selfDestructShip = null; // data-cache entity that will be destroyed by self-destruct timer
this._selfDestructTimer = null; // data-cache self-destruct timer
this._selfDestructCounter = 0; // data-cache self-destruct counter
this._selfDestructMessage = expandDescription("[gcm_self_destruct]").split('|');
this._defenceTimer = null; // timer to control when the data cache will detonate after being attacked
this._cacheBeaconTimer = null; // timer to control when the data cache waypoint timer will disengage
this._setData = [];
this._usageCount = 0;

//-------------------------------------------------------------------------------------------------------------
this.startUp = function () {
	var gcm = worldScripts.GalCopBB_Missions;
	// add these mission types into the main control
	gcm._availableMissionTypes.push(31);
	// type 41 is being added through galcopbb_delivery.js file
	// type 71 is being added through galcopbb_solaractivity.js file

	// position 7 is not used in any of these mission types, so limit the possibilities 
	this._maxLocations = gcm._positions.length - 1;

	if (missionVariables.GalCopBBMissions_CommsRelaySwitchCount) this._usageCount = missionVariables.GalCopBBMissions_CommsRelaySwitchCount;
}

//-------------------------------------------------------------------------------------------------------------
this.playerWillSaveGame = function () {
	missionVariables.GalCopBBMissions_CommsRelaySwitchCount = this._usageCount;
}

//-------------------------------------------------------------------------------------------------------------
this.systemWillPopulate = function () {
	this._setData.length = 0;
	var list = worldScripts.GalCopBB_Missions.$getListOfMissions(true, [31, 41]);

	if (list.length > 0) {
		// loop through all active missions and see if any need to be set up for this system
		for (var i = 0; i < list.length; i++) {

			// *** type 31/41 - get data cache
			if ((list[i].data.missionType === 31 || (list[i].data.missionType === 41 && list[i].data.stage === 0)) &&
				list[i].destination === system.ID &&
				list[i].data.quantity === 0 &&
				list[i].data.destroyedQuantity === 0 &&
				list[i].expiry > clock.adjustedSeconds) {
				if (list[i].data.hasOwnProperty("spawned") === false) list[i].data.spawned = false;

				// based on danger level, add some pirate ships around the comms relay
				var goonCount = Math.floor(Math.random() * (8 - system.info.government)) + 2;
				this._setData.push({
					missionType: 3141,
					trueMissionType: list[i].data.missionType,
					missionID: list[i].ID,
					source: list[i].source,
					goons: goonCount,
					quantity: 0,
					target: list[i].data.targetQuantity,
					repeat: list[i].data.spawned
				});
				list[i].data.spawned = true;

				// add comms relay with populator
				system.setPopulator("gcm-info-cache-" + list[i].ID, {
					callback: function (pos) {
						var g = worldScripts.GalCopBB_Missions;
						var gns = worldScripts.GalCopBB_GoonSquads;
						var dc = worldScripts.GalCopBB_DataCache;
						var cr = null;
						var checkShips = null;
						for (var j = 0; j <= 5; j++) {
							checkShips = system.addShips("commsrelay", 1, pos, 1000);
							if (checkShips) cr = checkShips[0];
							if (cr) break;
						}
						if (cr) {
							var missData = dc.$getMissionData(3141);
							cr.setScript("oolite-default-ship-script.js");
							if (missData.repeat === false) {
								cr.beaconCode = "D";
								cr.beaconLabel = expandDescription("[gcm_comms_relay]");
							}

							cr.script._missionID = missData.missionID;
							cr.script._passcode = expandDescription("[gcm_passcode]");

							// add our defense mechanism to the cache
							if (cr.script.shipBeingAttacked) cr.script.$gcm_hold_shipBeingAttacked = cr.script.shipBeingAttacked;
							cr.script.shipBeingAttacked = dc.$gcm_cache_shipBeingAttacked;
							if (cr.script.shipBeingAttackedByCloaked) cr.script.$gcm_hold_shipBeingAttackedByCloaked = cr.script.shipBeingAttackedByCloaked;
							cr.script.shipBeingAttackedByCloaked = dc.$gcm_cache_shipBeingAttackedByCloaked;
							if (cr.script.shipAttackedWithMissile) cr.script.$gcm_hold_shipAttackedWithMissile = cr.script.shipAttackedWithMissile;
							cr.script.shipAttackedWithMissile = dc.$gcm_cache_shipAttackedWithMissile;

							// add our shipDied event to the relay
							if (cr.script.shipDied) cr.script.$gcm_hold_shipDied = cr.script.shipDied;
							cr.script.shipDied = g.$gcm_entity_shipDied;

							// set up broadcast comms interface
							var bcc = worldScripts.BroadcastCommsMFD;
							if (bcc.$checkMessageExists("gcm_transmit_seccode") === false) {
								bcc.$createMessage({
									messageName: "gcm_transmit_seccode",
									callbackFunction: dc.$transmitSecurityCode.bind(dc),
									displayText: expandDescription("[gcm_transmit_securitycode]"),
									messageText: expandDescription("[gcm_transmitting_securitycode]"),
									ship: cr,
									transmissionType: "target",
									deleteOnTransmit: true,
									delayCallback: 5,
									hideOnConditionRed: true
								});
							}

							if (missData.repeat === false && (dc._cacheBeaconTimer == null || dc._cacheBeaconTimer.isRunning === false)) {
								dc._cacheBeaconTimer = new Timer(dc, dc.$endDataCacheBeacon, Math.floor(Math.random() * 30) + 120, 0);
							}

							gns.$createGoonSquad(missData.goons, cr.position, player.ship.scannerRange * 0.5);

							if (g._debug) g.$setWaypoint(pos, [0, 0, 0, 0], "Z", "Debug position (" + missData.trueMissionType + ")", missData.trueMissionType);

						} else {
							log("galcopBB_data_cache", "!!ERROR: Data cache not spawned!");
						}
					},
					location: "INNER_SYSTEM_OFFPLANE",
					locationSeed: list[i].ID
				});
			}
		}
	}
}

//-------------------------------------------------------------------------------------------------------------
this.shipWillEnterWitchspace = function (cause, destination) {
	this.$stopTimers();
}

//-------------------------------------------------------------------------------------------------------------
this.shipDied = function (whom, why) {
	this.$stopTimers();
}

//-------------------------------------------------------------------------------------------------------------
this.playerBoughtEquipment = function (equipmentKey) {
	if (equipmentKey === "EQ_GCM_COMMS_RELAY_SWITCH_RECHARGE") {
		player.ship.removeEquipment(equipmentKey);
		worldScripts.GalCopBB_DataCache._usageCount = 0;
	}
	if (equipmentKey === "EQ_GCM_COMMS_RELAY_SWITCH_REMOVE") {
		player.ship.removeEquipment(equipmentKey);
		player.ship.removeEquipment("EQ_GCM_COMMS_RELAY_SWITCH");
	}
}

//-------------------------------------------------------------------------------------------------------------
this.mode = function () {

}

//-------------------------------------------------------------------------------------------------------------
this.activated = function () {
	var dc = worldScripts.GalCopBB_DataCache;
	var beacons = system.shipsWithRole("commsrelay");
	if (!beacons || beacons.length === 0) {
		player.consoleMessage(expandDescription("[gcm_no_relays_found]"), 5);
		return;
	}
	if (dc._cacheBeaconTimer != null && dc._cacheBeaconTimer.isRunning === true) {
		player.consoleMessage(expandDescription("[gcm_relay_beacon_active]"), 5);
		return;
	}
	if (dc._usageCount === 3) {
		player.consoleMessage(expandDescription("[gcm_relay_switch_inactive]"), 5)
		return;
	}
	var bcn = beacons[0]; // there should only ever be one
	if (bcn.beaconCode === "D") {
		// this shouldn't happen, because the beacon code should only be present when the timer is running
		// it's on already, so we'll just pretend nothing happened...
	} else {
		dc._usageCount += 1;
		bcn.beaconCode = "D";
		bcn.beaconLabel = expandDescription("[gcm_comms_relay]");
		var bb = worldScripts.BulletinBoardSystem;
		var item = bb.$getItem(bcn.script._missionID);
		item.payment = Math.floor(item.payment * 0.95);
		dc._cacheBeaconTimer = new Timer(dc, dc.$endDataCacheBeacon, 30, 0);
		player.consoleMessage(expandDescription("[gcm_relay_beacon_engaged]"), 5);
		//if (dc._usageCount === 3) {
		//dc._usageCount = 0;
		//player.ship.removeEquipment("EQ_GCM_COMMS_RELAY_SWITCH");
		//}
	}
}

//-------------------------------------------------------------------------------------------------------------
this.$gcm_cache_shipBeingAttacked = function $gcm_cache_shipBeingAttacked(attacker) {
	if (this.ship.script.$gcm_hold_shipBeingAttacked) this.ship.script.$gcm_cache_shipBeingAttacked(attacker);
	worldScripts.GalCopBB_DataCache.$cacheExplosion(this.ship);
}

//-------------------------------------------------------------------------------------------------------------
this.$gcm_cache_shipBeingAttackedByCloaked = function $gcm_cache_shipBeingAttackedByCloaked() {
	if (this.ship.script.$gcm_hold_shipBeingAttackedByCloaked) this.ship.script.$gcm_cache_shipBeingAttackedByCloaked();
	worldScripts.GalCopBB_DataCache.$cacheExplosion(this.ship);
}

//-------------------------------------------------------------------------------------------------------------
this.$gcm_cache_shipAttackedWithMissile = function $gcm_cache_shipAttackedWithMissile(missile, whom) {
	if (this.ship.script.$gcm_hold_shipAttackedWithMissile) this.ship.script.$gcm_cache_shipAttackedWithMissile(missile, whom);
	worldScripts.GalCopBB_DataCache.$cacheExplosion(this.ship);
}

//-------------------------------------------------------------------------------------------------------------
this.$cacheExplosion = function $cacheExplosion(ship) {
	this.ship.commsMessage(expandDescription("[gcm_relay_under_attack]"));
	this._dataCache = ship;
	if (this._dataCacheTimer && this._dataCacheTimer.isRunning) this._dataCacheTimer.stop();
	this._defenceTimer = new Timer(this, this.$detonateCache, 3, 0);
}

//-------------------------------------------------------------------------------------------------------------
this.$detonateCache = function $detonateCache() {
	this._dataCache.becomeCascadeExplosion();
}

//-------------------------------------------------------------------------------------------------------------
// gets mission specific data for the populator routines
// this works on a first in/first out basis - if there are multiple missions of the same type being populated, the mission specific data would
// get pushed in to the setData array in order, and then this routine pulls that data out in the same order
// that's the theory, anyway!
this.$getMissionData = function $getMissionData(missionType) {
	for (var i = 0; i < this._setData.length; i++) {
		if (this._setData[i].missionType === missionType) {
			var result = {
				missionID: this._setData[i].missionID,
				trueMissionType: (this._setData[i].trueMissionType ? this._setData[i].trueMissionType : missionType),
				source: this._setData[i].source,
				goons: this._setData[i].goons,
				quantity: this._setData[i].quantity,
				target: this._setData[i].target,
				repeat: this._setData[i].repeat
			};
			this._setData.splice(i, 1);
			return result;
		}
	}
	return null;
}

//-------------------------------------------------------------------------------------------------------------
this.$stopTimers = function $stopTimers() {
	if (this._dataCacheTimer && this._dataCacheTimer.isRunning) this._dataCacheTimer.stop();
	delete this._dataCacheTimer;
	if (this._selfDestructTimer && this._selfDestructTimer.isRunning) this._selfDestructTimer.stop();
	delete this._selfDestructTimer;
	if (this._defenceTimer && this._defenceTimer.isRunning) this._defenceTimer.stop();
	delete this._defenceTimer;
}

//-------------------------------------------------------------------------------------------------------------
// works the time delay between transmitting the security code and receiving the data
this.$checkRangeToDataCache = function $checkRangeToDataCache() {
	if (this._dataCache.isValid === false) {
		this._dataCacheTimer.stop();
		return;
	}
	if (player.ship.position.distanceTo(this._dataCache) < player.ship.scannerRange) {
		this._dataReceived += 1;
		player.consoleMessage("Data transfer " + (this._dataReceived * 10) + "% complete.");
		if (this._dataReceived >= 10) {
			this._dataReceived = 0;
			this._dataCacheTimer.stop();
			delete this._dataCacheTimer;
			this.$dataCacheComplete();
			// remove the beacon, if it's been added
			if (this._dataCache.beaconCode == "M") {
				this._dataCache.beaconCode = "";
				this._dataCache.beaconLabel = "";
			}
		}
	} else {
		// reset the data cache BCC setup
		this._dataCacheTimer.stop();
		delete this._dataCacheTimer;
		player.consoleMessage(expandDescription("[gcm_relay_out_of_range]"));
		var item = this._bb.$getItem(this._dataCache.script._missionID);
		// check the mission type
		var msgKey = "";
		if (item.data.missionType === 31) msgKey = "gcm_transmit_seccode";
		if (item.data.missionType === 71) {
			msgKey = "gcm_sm_transmit_seccode_1";
			if (this._dataCache.position.distanceTo(system.mainPlanet) > sun.position.distanceTo(system.mainPlanet)) msgKey = "gcm_sm_transmit_seccode_2";
		}
		if (msgKey != "") {
			// set up broadcast comms interface
			var bcc = worldScripts.BroadcastCommsMFD;
			if (bcc.$checkMessageExists(msgKey) === false) {
				bcc.$createMessage({
					messageName: msgKey,
					callbackFunction: this.$transmitSecurityCode.bind(this),
					displayText: expandDescription("[gcm_transmit_securitycode]"),
					messageText: expandDescription("[gcm_transmitting_securitycode]"),
					ship: this._dataCache,
					transmissionType: "target",
					deleteOnTransmit: true,
					delayCallback: 5,
					hideOnConditionRed: true
				});
			}
		}
		this._dataCache = null;
	}
}

//-------------------------------------------------------------------------------------------------------------
// completes the data cache collection mission
this.$dataCacheComplete = function $dataCacheComplete() {
	if (this._dataCache.isInSpace) {

		this._dataCache.commsMessage(expandDescription("[gcm_relay_transfer_complete]"), player.ship);
		var missID = this._dataCache.script._missionID;
		var bb = worldScripts.BulletinBoardSystem;
		var item = bb.$getItem(missID);
		item.data.quantity += 1;
		// the mission can now be completed 
		bb.$updateBBMissionPercentage(missID, item.data.quantity / item.data.targetQuantity);

		worldScripts.GalCopBB_Missions.$logMissionData(item.ID);
		player.consoleMessage(expandDescription("[goal_updated]"));

		// set up self-destruct timer (mission type 31 only)
		if (item.data.missionType === 31) {
			this._selfDestructCounter = 0;
			this._selfDestructTimer = new Timer(this, this.$selfDestruct, 10, 1);
		} else {
			this._dataCache = null;
		}

		var id = worldScripts.GalCopBB_Missions.$createSecondaryMission(missID);
		if (id !== -1) {
			// we set the pending mission orig id here, although missions received via a comms relay will be auto accepted
			worldScripts.GalCopBB_Missions._pendingMissionOrigID = missID;
		}
	}
}

//-------------------------------------------------------------------------------------------------------------
// response after player has transmitted security code to a comms relay
this.$transmitSecurityCode = function $transmitSecurityCode() {
	var bb = worldScripts.BulletinBoardSystem;
	var targets = system.shipsWithPrimaryRole("commsrelay");
	for (var i = 0; i < targets.length; i++) {
		if (player.ship.position.distanceTo(targets[i]) < player.ship.scannerRange) {
			targets[i].commsMessage(expandDescription("[gcm_transmit_code]"), player.ship);

			var item = bb.$getItem(targets[i].script._missionID);
			if (item.data.missionType === 31 || item.data.missionType === 71) {
				// start a timer to control data transfer to player
				this._dataCache = targets[i];
				this._dataCacheTimer = new Timer(this, this.$checkRangeToDataCache.bind(this), 2, 2);
			} else if (item.data.missionType === 41) {
				this._dataCache = targets[i];
				// work out new destination
				var dta = worldScripts.GalCopBB_Delivery.$missionType41Updated_Values(targets[i], targets[i].script._missionID);
				// tell player if something went wrong
				if (dta === false) {
					// !! Argh! error ... 
					targets[i].commsMessage(expandDescription("[gcm_relay_data_corrupted]"), player.ship);
					// turn off penalty so player doesn't get dinged for something not his fault
					item.penalty = 0;
				}
				// start the self-destruct
				this._selfDestructCounter = 0;
				this._selfDestructTimer = new Timer(this, this.$selfDestruct, 10, 1);

			}
		}
	}
}

//-------------------------------------------------------------------------------------------------------------
// does the self destruct sequence on a data cache
this.$selfDestruct = function $selfDestruct() {
	if (this._selfDestructCounter <= (this._selfDestructMessage.length - 1)) {
		this._dataCache.commsMessage(this._selfDestructMessage[this._selfDestructCounter], player.ship);
		this._selfDestructCounter += 1;
	} else {
		this._selfDestructTimer.stop();
		delete this._dataCache.script.shipDied;
		this._dataCache.explode();
	}
}

//-------------------------------------------------------------------------------------------------------------
// turns off the data cache beacon (it's only available for a short period of time)
this.$endDataCacheBeacon = function $endDataCacheBeacon() {
	var targets = system.shipsWithPrimaryRole("commsrelay");
	if (targets.length > 0) {
		for (var i = 0; i < targets.length; i++) {
			targets[i].beaconCode = "";
			targets[i].beaconLabel = "";
		}
		player.consoleMessage(expandDescription("[gcm_relay_lost_contact]"));
	}
}

//-------------------------------------------------------------------------------------------------------------
this.$acceptedMission = function $acceptedMission(missID) {
	var bb = worldScripts.BulletinBoardSystem;
	var item = bb.$getItem(missID);
	var gcm = worldScripts.GalCopBB_Missions;

	if (this._debug) log(this.name, "accepted mission id = " + missID);

	if (!item) {
		log(this.name, "!!ERROR: BB returned null value from $getItem on mission ID " + missID);
		return;
	}
	gcm.$updateLastMissionDate(item.source, item.data.missionType);

	if (item.data.missionType === 31) {
		player.ship.awardEquipment("EQ_GCM_COMMS_RELAY_SWITCH");
		worldScripts.GalCopBB_DataCache._usageCount = 0;
	}
}

//-------------------------------------------------------------------------------------------------------------
this.$completedMission = function $completedMission(missID) {
	var p = player.ship;
	var bb = worldScripts.BulletinBoardSystem;
	var item = bb.$getItem(missID);
	var gcm = worldScripts.GalCopBB_Missions;
	var sbm = worldScripts.Smugglers_BlackMarket;

	// update mission history
	gcm.$updateSuccessHistoryReputation(item);
	// remove any data-type black market sales
	if (item.data.missionType === 31) {
		if (sbm) sbm.$removeSaleItem("DTA_DATA_PACKAGE:" + missID);
	}
}

//-------------------------------------------------------------------------------------------------------------
this.$confirmCompleted = function $confirmCompleted(missID) {
	var p = player.ship;
	var result = "";
	var bb = worldScripts.BulletinBoardSystem;
	var item = bb.$getItem(missID);
	if (item) {
		// get data from data cache
		if (item.data.missionType === 31) {
			if (item.data.targetQuantity === 0) {
				result += (result === "" ? "" : "\n") + expandDescription("[gcm_relay_data_collected]");
			}
		}
	}
	return result;
}

//-------------------------------------------------------------------------------------------------------------
this.$terminateMission = function $terminateMission(missID) {
	var bb = worldScripts.BulletinBoardSystem;
	var item = bb.$getItem(missID);
	var sbm = worldScripts.Smugglers_BlackMarket;
	var gcm = worldScripts.GalCopBB_Missions;

	// adjust reputation only when the terminatePenalty flag is set to true 
	if (item.data.terminatePenalty === true) {
		// update mission history
		gcm.$updateFailedHistoryReputation(item);
	}
	// remove any data-type black market sales
	if (item.data.missionType === 31) {
		if (sbm) sbm.$removeSaleItem("DTA_DATA_PACKAGE:" + missID);
	}
}

//-------------------------------------------------------------------------------------------------------------
this.$failedMission = function $failedMission(missID) {
	var bb = worldScripts.BulletinBoardSystem;
	var sbm = worldScripts.Smugglers_BlackMarket;
	var gcm = worldScripts.GalCopBB_Missions;
	var eq = "";

	if (!sbm) {
		// if there's no blackmarket option, just remove the equipment
		this.$terminateMission(missID);
		return;
	}

	var item = bb.$getItem(missID);
	// update mission history
	gcm.$updateFailedHistoryReputation(item);

	if (item.data.missionType === 31 && item.data.quantity === 1 && item.data.targetQuantity > 0) eq = "DTA_DATA_PACKAGE";

	if (eq != "") gcm._equipmentFromFailedMissions.push({
		missionType: item.data.missionType,
		source: item.source,
		equip: eq,
		date: clock.adjustedSeconds
	});
}

//-------------------------------------------------------------------------------------------------------------
this.$updateManifestEntry = function $updateManifestEntry(missID) {
	var gcm = worldScripts.GalCopBB_Missions;
	gcm.$updateManifestEntry(missID);
}

//-------------------------------------------------------------------------------------------------------------
this.$missionAvailability = function $missionAvailability(missID, missType, origSysID) {
	return "";
}

//-------------------------------------------------------------------------------------------------------------
// 31 - data cache
this.$missionType31_Values = function $missionType31_Values(workTime, routeTime, routeDistance, destSysInfo) {
	var result = {};
	// pick a location
	result["locationType"] = Math.floor(Math.random() * this._maxLocations);
	result["quantity"] = 1;
	result["price"] = parseInt((parseInt(Math.random() * 40) + 20) / 10) * 10 + (7 - destSysInfo.government) * 20 +
		worldScripts.GalCopBB_CoreMissionValues.$calcPlayerBonus(200) // plus a possible bonus price, based on player score 
		+
		worldScripts.GalCopBB_CoreMissionValues.$calcDistanceBonus(routeDistance, 20); // plus a distance bonus
	result["expiry"] = clock.adjustedSeconds + routeTime + workTime; // transit time + 1 hour to complete
	result["penalty"] = parseInt(result.price / 2);
	return result;
}