"use strict";
this.name = "GalCopBB_Earthquake";
this.author = "phkb";
this.copyright = "2017 phkb";
this.description = "Controls all earthquake-system mission settings (missions 100-106)";
this.license = "CC BY-NC-SA 4.0";

/*
	100-110 Earthquake system missions
	*100 - Use scan equipment (seismic resonance scanner) to scan planet in 3-4 locations
		player must get in low enough orbit to perform scan successfully
		have some way of informing the player where the scan points are - waypoints, must be within 2km of waypoints
	*101 - transport injured people to hi-techlevel system for treatment
	*102 - destroy a number of asteroids around the planet. The gravitational effect of the asteroids means instability on the surface
	103 - collect and deliver special equipment
	*104 - take seismic data to hi-techlevel system for data analysis
*/

this._debug = false;
this._earthquakeSystems = []; // systems where there are earthquakes
this._scanTimer = null; // timer object used when scanning
this._scanPosition = 0; // current position of the scan
this._outOfRangeCount = 0; // counts number of times scan attempted to start but was out of range
this._energy = null; // timer to deduct energy
this._energyReduction = 3; // amount of energy to deduct each quarter second
this._waypoints = [];

//-------------------------------------------------------------------------------------------------------------
this.startUp = function () {
	var gcm = worldScripts.GalCopBB_Missions;
	// add these mission types into the main control
	var list = [100, 101, 102, 104];
	gcm._availableMissionTypes = gcm._availableMissionTypes.concat(list);
	this._debug = gcm._debug;

	// exclude some equipment items from being sold/stored by ShipConfig
	var sc = worldScripts.ShipConfiguration_Core;
	if (sc) {
		sc._noSell.push("EQ_GCM_SEISMIC_SCANNER");
	}
}

//-------------------------------------------------------------------------------------------------------------
this.startUpComplete = function () {
	this._earthquakeSystems = this.$findEarthquakeSystems();
	worldScripts.BulletinBoardSystem.$registerBBEvent("GalCopBB_Earthquake", "$custom_shipWillDockWithStation", "shipWillDockWithStation_start");
}

//-------------------------------------------------------------------------------------------------------------
this.shipWillLaunchFromStation = function (station) {
	var gcm = worldScripts.GalCopBB_Missions;
	var seq = ["pwp", "-pwp", "psp", "-psp"];
	var list = gcm.$getListOfMissions(true, [100, 102]);
	if (list.length === 0) return;
	for (var i = 0; i < list.length; i++) {
		if (list[i].data.missionType === 100 &&
			list[i].destination === system.ID &&
			list[i].expiry > clock.adjustedSeconds) {
			// create waypoints
			this._waypoints.length = 0;
			for (var j = 1; j <= 4; j++) {
				var typ = seq[j - 1];
				var mult = 1;
				if (typ.indexOf("-") >= 0) {
					mult = -1;
					typ = typ.replace("-", "");
				}
				var pos = Vector3D(0, 0, 1.03 * mult).fromCoordinateSystem(typ);
				system.setWaypoint(this.name + "_" + j, pos, system.mainPlanet.orientation, {
					size: 50,
					beaconCode: j.toString(),
					beaconLabel: expandDescription("[gcm_scan_point]", { index: j })
				});
				this._waypoints.push(pos);
			}
			break;
		}
		if (list[i].data.missionType === 102 &&
			list[i].destination === system.ID &&
			list[i].expiry > clock.adjustedSeconds) {
			var checkShips = null;
			for (var j = 0; j < ((list[i].data.targetQuantity - list[i].data.destroyedQuantity) - list[i].data.quantity); j++) {
				for (var k = 1; k <= 5; k++) {
					checkShips = system.addShips("asteroid", 1, system.mainPlanet.position.add(new Vector3D.randomDirection(system.mainPlanet.radius * (1 + (Math.random() * 0.4) + 0.2))), 1)
					if (checkShips) break;
				}
				if (checkShips) {
					var aster = checkShips[0];
					aster.setScript("oolite-default-ship-script.js");
					aster.script.shipDied = this.$aster_shipDied;
					aster.script._missionID = list[i].ID;
				}
			}
		}
	}
}

//-------------------------------------------------------------------------------------------------------------
this.$custom_shipWillDockWithStation = function $custom_shipWillDockWithStation(station) {
	var gcm = worldScripts.GalCopBB_Missions;
	if (gcm._simulator === true) return;
	var bb = worldScripts.BulletinBoardSystem;
	var list = gcm.$getListOfMissions(true, [101, 104]);
	for (var i = 0; i < list.length; i++) {
		if (list[i].data.missionType === 101 && station.isMainStation && list[i].destination === system.ID) {
			if (player.ship.equipmentStatus("EQ_GCM_PATIENT_TRANSPORT") === "EQUIPMENT_OK" && list[i].expiry >= clock.adjustedSeconds) {
				list[i].arrivalReportText = expandDescription("[missionType101_arrivalReport_complete]", {
					payment: formatCredits(list[i].payment, false, true)
				});
			} else {
				list[i].arrivalReportText = expandDescription("[missionType101_arrivalReport_late]");
			}

			list[i].data.quantity = 1;
			bb.$updateBBMissionPercentage(list[i].ID, 1);

			gcm.$logMissionData(list[i].ID);
		}
		if (list[i].data.missionType === 104 && station.isMainStation && list[i].destination === system.ID) {
			if (list[i].expiry >= clock.adjustedSeconds) {
				if (player.ship.equipmentStatus("EQ_GCM_UNPROCESSED_SCAN_DATA") === "EQUIPMENT_OK") {
					list[i].arrivalReportText = expandDescription("[missionType104_arrivalReport_complete]", {
						payment: formatCredits(list[i].payment, false, true)
					});
				} else {
					list[i].arrivalReportText = expandDescription("[missionType104_arrivalReport_corrupt]", {
						payment: formatCredits(list[i].payment, false, true)
					});
				}
			} else {
				list[i].arrivalReportText = expandDescription("[missionType104_arrivalReport_late]");
			}

			list[i].data.quantity = 1;
			bb.$updateBBMissionPercentage(list[i].ID, 1);

			gcm.$logMissionData(list[i].ID);
		}
	}
}

//-------------------------------------------------------------------------------------------------------------
this.equipmentDamaged = function (equipmentKey) {
	if (equipmentKey === "EQ_GCM_UNPROCESSED_SCAN_DATA") {
		var list = worldScripts.GalCopBB_Missions.$getListOfMissions(true, 104);
		for (var i = 0; i < list.length; i++) {
			if (list[i].data.destroyedQuantity === 0) {
				list[i].data.destroyedQuantity = 1;
				list[i].payment /= 2;
				break;
			}
		}
	}
}

//-------------------------------------------------------------------------------------------------------------
this.$aster_shipDied = function $aster_shipDied(whom, why) {
	//log(this.name, "whom " + whom + ", why " + why);
	var gcm = worldScripts.GalCopBB_Missions;
	var bb = worldScripts.BulletinBoardSystem;
	var item = bb.$getItem(this.ship.script._missionID);
	if (whom.isPlayer) {
		item.data.quantity += 1;
		bb.$updateBBMissionPercentage(item.ID, item.data.quantity / item.data.targetQuantity);

		gcm.$logMissionData(item.ID);
		player.consoleMessage(expandDescription("[goal_updated]"));
	} else {
		item.data.destroyedQuantity += 1;
		bb.$updateBBMissionPercentage(item.ID, item.data.quantity / item.data.targetQuantity);
		gcm.$logMissionData(item.ID);
		player.consoleMessage(expandDescription("[goal_updated]"));
	}
}

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

//-------------------------------------------------------------------------------------------------------------
this.activated = function () {
	worldScripts.GalCopBB_Earthquake.$startScanner();
}

//-------------------------------------------------------------------------------------------------------------
this.$startScanner = function $startScanner() {
	if (this._scanTimer && this._scanTimer.isRunning) {
		player.consoleMessage(expandDescription("[gcm_scan_stopped]"));
		worldScripts.GalCopBB_Missions.$playSound("stop");
		this.$stopTimer();
		return;
	}
	player.consoleMessage(expandDescription("[gcm_scan_started]"));
	this._scanTimer = new Timer(this, this.$performScan, 1, 1);
	if (this._energy && this._energy.isRunning) this._energy.stop();
	this._energy = new Timer(this, this.$depleteEnergy, 0.25, 0.25);
	worldScripts.GalCopBB_Missions.$playSound("activate");
}

//-------------------------------------------------------------------------------------------------------------
this.$performScan = function $performScan() {
	var p = player.ship;
	if (p.equipmentStatus("EQ_GCM_SEISMIC_SCANNER") === "EQUIPMENT_DAMAGED") {
		player.consoleMessage(expandDescription("[gcm_scanner_damaged]"));
		this.$stopTimer();
		return;
	}
	if (this.$isPlayerWithinRange() === false) {
		this._outOfRangeCount += 1;
		if (this._outOfRangeCount === 10) this._outOfRangeCount = 0;
		if (this._outOfRangeCount === 1) player.consoleMessage(expandDescription("[gcm_scan_out_of_range]"));
		return;
	}
	this._outOfRangeCount = 0;
	this._scanPosition += 1;
	if (parseInt(this._scanPosition / 5) === this._scanPosition / 5) {
		player.consoleMessage(expandDescription("[gcm_scan_progress]", { complete: parseInt((this._scanPosition / 60) * 100) }));
	}
	// check for complete
	if (this._scanPosition >= 60) {
		this._scanPosition = 0;
		this.$stopTimer();
		var gcm = worldScripts.GalCopBB_Missions;
		var bb = worldScripts.BulletinBoardSystem;
		var list = gcm.$getListOfMissions(true, 100);
		for (var i = 0; i < list.length; i++) {
			if (system.ID === list[i].source &&
				list[i].expiry > clock.adjustedSeconds) {

				// remove the waypoint we've finished with
				for (var j = 0; j < this._waypoints.length; j++) {
					if (player.ship.position.distanceTo(this._waypoints[j]) < 1000) {
						system.setWaypoint(this.name + "_" + (j + 1));
						this._waypoints[j] = system.sun.position;
						break;
					}
				}

				list[i].data.quantity += 1;
				bb.$updateBBMissionPercentage(list[i].ID, list[i].data.quantity / list[i].data.targetQuantity);

				gcm.$logMissionData(list[i].ID);
				player.consoleMessage(expandDescription("[goal_updated]"));
				break;
			}
		}
	}
}

//-------------------------------------------------------------------------------------------------------------
this.$depleteEnergy = function $depleteEnergy() {
	var p = player.ship;
	p.energy -= this._energyReduction;
	if (p.energy < 64) {
		this.$stopTimer()
		player.consoleMessage(expandDescription("[gcm_insufficient_energy]"));
	}
}

//-------------------------------------------------------------------------------------------------------------
// returns true if player is within 2km of one of the waypoints, otherwise false
this.$isPlayerWithinRange = function $isPlayerWithinRange() {
	var inRange = false;
	for (var i = 0; i < this._waypoints.length; i++) {
		if (player.ship.position.distanceTo(this._waypoints[i]) < 1000) inRange = true;
	}
	return inRange;
}

//-------------------------------------------------------------------------------------------------------------
this.$stopTimer = function $stopTimer() {
	if (this._scanTimer && this._scanTimer.isRunning) {
		this._scanTimer.stop();
	}
	delete this._scanTimer;
	if (this._energy && this._energy.isRunning) {
		this._energy.stop();
	}
	delete this._energy;
}

//-------------------------------------------------------------------------------------------------------------
this.$removeAllWaypoints = function () {
	// remove any waypoints still left
	for (var i = 0; i < this._waypoints.length; i++) {
		system.setWaypoint(this.name + "_" + (i + 1));
		this._waypoints[i] = system.sun.position;
	}
}

//-------------------------------------------------------------------------------------------------------------
// returns a list of sysInfos where earthquakes are a problem
this.$findEarthquakeSystems = function $findEarthquakeSystems() {
	return SystemInfo.filteredSystems(this, function (other) {
		return (other.description.indexOf("earthquake") >= 0 && other.techlevel < 11);
	});
}


//-------------------------------------------------------------------------------------------------------------
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);
	gcm.$updateGeneralSettings(item);

	// give the player the seismic scanner
	if (item.data.missionType === 100) {
		player.ship.awardEquipment("EQ_GCM_SEISMIC_SCANNER");
	}
	// give player patient transport for mission type 51
	if (item.data.missionType === 101) {
		var eq = EquipmentInfo.infoForKey("EQ_GCM_PATIENT_TRANSPORT");
		if (player.ship.cargoSpaceAvailable < eq.requiredCargoSpace) this.$freeCargoSpace(eq.requiredCargoSpace, "");
		player.ship.awardEquipment("EQ_GCM_PATIENT_TRANSPORT");
	}
	// give player the unprocessed data
	if (item.data.missionType === 104) {
		player.ship.awardEquipment("EQ_GCM_UNPROCESSED_SCAN_DATA");
	}

}

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

	// update mission history
	gcm.$updateSuccessHistoryReputation(item);

	var sbm = worldScripts.Smugglers_BlackMarket;

	// *** type 100 - seismic scanner
	if (item.data.missionType === 100) {
		p.removeEquipment("EQ_GCM_SEISMIC_SCANNER");
		if (sbm) sbm.$removeSaleItem("EQ_GCM_SEISMIC_SCANNER:" + missID);
	}
	// *** type 101 - patient transport 
	if (item.data.missionType === 101) {
		// if the player has a bounty, doing these missions will reduce it
		if (player.ship.bounty > 0) {
			for (var i = 0; i < 5; i++) {
				if (player.ship.bounty > 0) player.ship.setBounty(player.ship.bounty - 1, expandDescription("[gcm_community_service]"));
			}
			player.ship.consoleMessage(expandDescription("[gcm_bounty_reduced]"));
		}
		// just in case
		p.removeEquipment("EQ_GCM_PATIENT_TRANSPORT");
	}
	// *** type 104 - seismic scan data
	if (item.data.missionType === 104) {
		p.removeEquipment("EQ_GCM_UNPROCESSED_SCAN_DATA");
	}
}

//-------------------------------------------------------------------------------------------------------------
this.$confirmCompleted = function $confirmCompleted(missID) {
	var p = player.ship;
	var result = "";
	var bb = worldScripts.BulletinBoardSystem;
	var item = bb.$getItem(missID);
	if (item) {
		// *** type 101 - patient transport
		if (item.data.missionType === 101) {
			var chk = p.equipmentStatus("EQ_GCM_PATIENT_TRANSPORT");
			if (chk !== "EQUIPMENT_OK" && chk !== "EQUIPMENT_DAMAGED") {
				result += (result === "" ? "" : "\n") + expandDescription("[gcm_patient_transport_not_found]");
			}
			if (chk === "EQUIPMENT_DAMAGED") {
				result += (result === "" ? "" : "\n") + expandDescription("[gcm_patient_transport_damaged]");
			}
		}
		if (item.data.missionType === 104) {
			var chk = p.equipmentStatus("EQ_GCM_UNPROCESSED_SCAN_DATA")
			if (chk !== "EQUIPMENT_OK" && chk !== "EQUIPMENT_DAMAGED") result += (result === "" ? "" : "\n") + expandDescription("[gcm_scan_data_not_found]");
		}
	}
	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;

	this.$removeAllWaypoints();

	// adjust reputation only when the terminatePenalty flag is set to true 
	if (item.data.terminatePenalty === true) {
		// update mission history
		gcm.$updateFailedHistoryReputation(item);
	}

	// *** type 100 - seismic scanner
	if (item.data.missionType === 100) {
		var eqsts = player.ship.equipmentStatus("EQ_GCM_SEISMIC_SCANNER");
		if (eqsts === "EQUIPMENT_OK" || eqsts === "EQUIPMENT_DAMAGED") {
			player.ship.removeEquipment("EQ_GCM_SEISMIC_SCANNER");
			if (sbm) sbm.$removeSaleItem("EQ_GCM_SEISMIC_SCANNER:" + missID);
		}
	}
	// *** type 101 - patient transport
	if (item.data.missionType === 101) {
		player.ship.removeEquipment("EQ_GCM_PATIENT_TRANSPORT");
	}
	// *** type 104 - seismic data transfer
	if (item.data.missionType === 104) {
		player.ship.removeEquipment("EQ_GCM_UNPROCESSED_SCAN_DATA");
	}
}

//-------------------------------------------------------------------------------------------------------------
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;
	}

	this.$removeAllWaypoints();

	var item = bb.$getItem(missID);
	gcm.$updateFailedHistoryReputation(item);

	// *** type 100 - seismic scanner
	if (item.data.missionType === 100) {
		var eqsts = player.ship.equipmentStatus("EQ_GCM_SEISMIC_SCANNER");
		if (eqsts === "EQUIPMENT_OK" || eqsts === "EQUIPMENT_DAMAGED") {
			eq = "EQ_GCM_SEISMIC_SCANNER";
		}
	}
	if (item.data.missionType === 101) {
		// we'll just remove this one
		player.ship.removeEquipment("EQ_GCM_PATIENT_TRANSPORT");
		eq = "";
	}

	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 worldScripts.GalCopBB_Missions.$missionAvailability(missID, missType, origSysID);
}

//-------------------------------------------------------------------------------------------------------------
// 100
this.$missionType100_Values = function $missionType100_Values(workTime, routeTime, routeDistance, destSysInfo) {
	var missValues = worldScripts.GalCopBB_CoreMissionValues;
	var result = {};
	result["quantity"] = 4;
	result["price"] = 100 + parseInt((8 - destSysInfo.government) * (Math.random() * 50));
	result["expiry"] = clock.adjustedSeconds + (3600 * 1.5); // 1.5 hours to complete
	result["penalty"] = result.price / 4;
	return result;
}

//-------------------------------------------------------------------------------------------------------------
// 101 - emergency patient transport
this.$missionType101_Values = function $missionType101_Values(workTime, routeTime, routeDistance, destSysInfo) {
	var missValues = worldScripts.GalCopBB_CoreMissionValues;
	var result = {};
	result["quantity"] = 1;
	result["price"] = parseInt((parseInt(Math.random() * 50) + 75) / 10) * 10 + (7 - destSysInfo.government) * 20 + missValues.$calcDistanceBonus(routeDistance, 10);
	result["expiry"] = clock.adjustedSeconds + routeTime + 600; // we want the time to be tight for the patient transport -- transit time + 10 mins
	result["penalty"] = result.price / 2;
	return result;
}

//-------------------------------------------------------------------------------------------------------------
// 102
this.$missionType102_Values = function $missionType102_Values(workTime, routeTime, routeDistance, destSysInfo) {
	var missValues = worldScripts.GalCopBB_CoreMissionValues;
	var result = {};
	result["quantity"] = Math.floor(Math.random() * 20) + 20;
	result["price"] = result.quantity * 8 + (Math.floor(Math.random() * 40) + 20);
	result["expiry"] = clock.adjustedSeconds + (3600 * 3); // 3 hours to complete
	result["penalty"] = 0;
	return result;
}

//-------------------------------------------------------------------------------------------------------------
// 104
this.$missionType104_Values = function $missionType104_Values(workTime, routeTime, routeDistance, destSysInfo) {
	var missValues = worldScripts.GalCopBB_CoreMissionValues;
	var result = {};
	result["quantity"] = 1;
	result["price"] = 500 + parseInt((8 - destSysInfo.government) * (Math.random() * 90)) + missValues.$calcDistanceBonus(routeDistance, 10);
	result["expiry"] = clock.adjustedSeconds + routeTime + workTime;
	result["penalty"] = result.price / 2;
	return result;
}