"use strict";
this.name = "GalCopBB_RangeFinder_MFD";
this.author = "phkb";
this.copyright = "2017 phkb";
this.description = "Controls the Range Finder MFD";
this.license = "CC BY-NC-SA 4.0";

// idea: include rock hermits as separate scan item

this._mode = -1; // 0 = Escape capsules, 1 = Black boxes, 2 = asteroids, 3 = ships, 4 = communication relays
this._maxRange = 0; // maximum scan distance for the range finder (returned from script_info data)
this._maxDirectionRange = 0; // maximum distance for giving directional pointers to targets (returned from script_info data)
this._running = false;
this._scanTimer = null; // timer of the scan
this._scanTime = 2; // number of seconds it will take for a scan
this._energyTimer = null; // timer to deduct energy (if heat is not being applied)
this._pointer = -1; // pointer to which scan indicators are in use
this._heatAmount = 0.93; // amount of heat generated when used with ShipConfiguration OXP
this._energyAmount = 3; // amount of energy to deduct each quarter second
this._lastScan = ""; // results of the last scan
this._lastScanData = {};
this._next_uniqueID = 0;
this._scInstalled = false; // flag to indicate that ShipConfig is installed
this._heatSettings = {
	200000: 0.88,
	500000: 0.94
}; // heat buildup settings
this._energySettings = { // energy drain settings
	"type1": {
		200000: 2.0,
		500000: 2.7
	}, // no EEU or NEU
	"type2": {
		200000: 2.7,
		500000: 3.5
	}, // EEU only
	"type3": {
		200000: 3.2,
		500000: 4
	}, // NEU
};

this._firstCol = 1;
this._secondCol = 6;
this._thirdCol = 8;

//-------------------------------------------------------------------------------------------------------------
this.startUpComplete = function () {
	var h = worldScripts.hudselector;
	if (h) h.$HUDSelectorAddMFD(this.name, "RangeFinderMFD");

	this._lastScan = expandDescription("[gcm_rangefinder_nodata]");

	if (worldScripts.ShipConfiguration_Core) this._scInstalled = true;

	this._maxRange = this.$getScannerRange();
	this._maxDirectionRange = this.$getScannerDirectionalRange();
	if (this._maxRange > 0) {
		player.ship.setMultiFunctionText("RangeFinderMFD", expandDescription("[gcm_rangefinder_header]") + "\n  " + this._lastScan, false);
	}
	this._heatAmount = this._heatSettings[this._maxRange];
	this._energyAmount = this.$getEnergySetting();

	var chrs = ["+", "-", "X", "x", "O", "<", ">", "^", "v", "A"];
	var first = 0;
	for (var i = 0; i < chrs.length; i++) {
		var wid = defaultFont.measureString(chrs[i]);
		if (wid > first) first = wid;
	}
	this._firstCol = first + 0.3;
	this._thirdCol = 15 - (this._secondCol + this._firstCol);
}

//-------------------------------------------------------------------------------------------------------------
this.playerWillEnterWitchspace = function () {
	if (this._scanTimer && this._scanTimer.isRunning) {
		if (this._scInstalled) {
			player.ship.script.thirdPartyHeat -= this._heatAmount;
			if (player.ship.script.thirdPartyHeat < 0) player.ship.script.thirdPartyHeat = 0;
		}
		this._scanTimer.stop();
		this._scanTimer = null;
	}
	if (this._energyTimer && this._energyTimer.isRunning) {
		this._energyTimer.stop();
		this._energyTimer = null;
	}
	this._lastScan = expandDescription("[gcm_rangefinder_nodata]");
	this._lastScanData = {};
	this._next_uniqueID = 0;
}

//-------------------------------------------------------------------------------------------------------------
this.shipWillLaunchFromStation = function (station) {
	this._lastScan = expandDescription("[gcm_rangefinder_nodata]");
}

//-------------------------------------------------------------------------------------------------------------
this.shipDied = function (whom, why) {
	if (this._scanTimer && this._scanTimer.isRunning) {
		this._scanTimer.stop();
		this._scanTimer = null;
	}
	if (this._energyTimer && this._energyTimer.isRunning) {
		this._energyTimer.stop();
		this._energyTimer = null;
	}
}

//-------------------------------------------------------------------------------------------------------------
this.equipmentDamaged = function (equipmentKey) {
	if (equipmentKey === "EQ_GCM_RANGE_FINDER_MFD" || equipmentKey === "EQ_GCM_RANGE_FINDER_EXT_REMOVAL") {
		if (this._scanTimer && this._scanTimer.isRunning === true) {
			this._scanTimer.stop();
			this._scanTimer = null;
			if (this._scInstalled) player.ship.script.thirdPartyHeat -= this._heatAmount;
		}
		if (this._energyTimer && this._energyTimer.isRunning) {
			this._energyTimer.stop();
			this._energyTimer = null;
		}
		this._maxRange = this.$getScannerRange();
		this._maxDirectionRange = this.$getScannerDirectionalRange();
		this._heatAmount = this._heatSettings[this._maxRange];
		this._energyAmount = this.$getEnergySetting();
	}
}

//-------------------------------------------------------------------------------------------------------------
this.equipmentRepaired = function (equipmentKey) {
	if (equipmentKey === "EQ_GCM_RANGE_FINDER_MFD" || equipmentKey === "EQ_GCM_RANGE_FINDER_EXT_REMOVAL") {
		this._maxRange = this.$getScannerRange();
		this._maxDirectionRange = this.$getScannerDirectionalRange();
		this._heatAmount = this._heatSettings[this._maxRange];
		this._energyAmount = this.$getEnergySetting();
		this._lastScan = expandDescription("[gcm_rangefinder_nodata]");
		this._lastScanData = {};
		if (this._mode >= 0) {
			player.ship.setMultiFunctionText("RangeFinderMFD", expandDescription("[gcm_rangefinder_mode_short_" + this._mode + "]") + this._lastScan, false);
		} else {
			player.ship.setMultiFunctionText("RangeFinderMFD", expandDescription("[gcm_rangefinder_header]") + "\n  " + this._lastScan, false);
		}
	}
}

//-------------------------------------------------------------------------------------------------------------
this.equipmentRemoved = this.equipmentAdded = function (equipmentKey) {
	if (equipmentKey === "EQ_GCM_RANGE_FINDER_MFD" || equipmentKey === "EQ_GCM_RANGE_FINDER_EXTENDER") {
		this._maxRange = this.$getScannerRange();
		this._heatAmount = this._heatSettings[this._maxRange];
		this._energyAmount = this.$getEnergySetting();
		player.ship.setMultiFunctionText("RangeFinderMFD", null);
	}
}

//-------------------------------------------------------------------------------------------------------------
this.playerBoughtEquipment = function (equipmentKey) {
	if (equipmentKey === "EQ_GCM_RANGE_FINDER_REMOVAL" || equipmentKey === "EQ_GCM_RANGE_FINDER_EXT_REMOVAL") {
		var p = player.ship;
		p.removeEquipment("EQ_GCM_RANGE_FINDER_REMOVAL");
		p.removeEquipment("EQ_GCM_RANGE_FINDER_EXT_REMOVAL");
		p.removeEquipment("EQ_GCM_RANGE_FINDER_EXTENDER");
		p.removeEquipment("EQ_GCM_RANGE_FINDER_MFD");
		p.setMultiFunctionText("RangeFinderMFD", null);
		return;
	}
	this._lastScan = expandDescription("[gcm_rangefinder_nodata]");
	this._lastScanData = {};
	player.ship.setMultiFunctionText("RangeFinderMFD", expandDescription("[gcm_rangefinder_header]") + "\n  " + this._lastScan, false);
}

//-------------------------------------------------------------------------------------------------------------
this.mode = function () {
	var rf = worldScripts.GalCopBB_RangeFinder_MFD;
	if (rf._scanTimer && rf._scanTimer.isRunning) {
		player.consoleMessage(expandDescription("[gcm_rangefinder_changemodefail]"));
		return;
	}
	if (rf._maxRange === 0) return;

	rf._mode += 1;
	if (rf._mode === 6) rf._mode = 0;
	
	rf._lastScan = "";

	player.consoleMessage(expandDescription("[gcm_rangefinder_mode_description_" + rf._mode + "]"));
	player.ship.setMultiFunctionText("RangeFinderMFD", expandDescription("[gcm_rangefinder_mode_short_" + rf._mode + "]"));
	worldScripts.GalCopBB_Missions.$playSound("mode");
}

//-------------------------------------------------------------------------------------------------------------
this.activated = function () {
	var rf = worldScripts.GalCopBB_RangeFinder_MFD;
	if (rf._running == true) {
		rf._running = false;
		rf._scanTimer.stop();
		rf._scanTimer = null;
		if (rf._energyTimer && rf._energyTimer.isRunning) {
			rf._energyTimer.stop();
			rf._energyTimer = null;
		}
		if (rf._scInstalled) {
			player.ship.script.thirdPartyHeat -= rf._heatAmount;
			if (player.ship.script.thirdPartyHeat < 0) player.ship.script.thirdPartyHeat = 0;
		}
		player.ship.setMultiFunctionText("RangeFinderMFD", expandDescription("[gcm_rangefinder_mode_short_" + rf._mode + "][gcm_rangefinder_terminated]") + rf._lastScan, false);
		worldScripts.GalCopBB_Missions.$playSound("stop");
		return;
	}
	if (rf._mode === -1) rf._mode = 0;
	if (rf._maxRange === 0) return;
	rf._pointer = -1;
	rf._running = true;

	if (rf._energyTimer && rf._energyTimer.isRunning) rf._energyTimer.stop();
	rf._energyTimer = new Timer(rf, rf.$depleteEnergy, 0.25, 0.25);
	if (rf._scInstalled) player.ship.script.thirdPartyHeat += rf._heatAmount;

	rf._scanTimer = new Timer(rf, rf.$performScan, rf._scanTime, rf._scanTime);
	player.ship.setMultiFunctionText("RangeFinderMFD", expandDescription("[gcm_rangefinder_mode_short_" + rf._mode + "][gcm_rangefinder_active_initial]"), false);
	worldScripts.GalCopBB_Missions.$playSound("activate");
}

//-------------------------------------------------------------------------------------------------------------
this.$performScan = function $performScan() {
	function compare(a, b) {
		return a.dist > b.dist;
	}

	function gcm_findships(entity) {
		return (entity.isShip && entity.isStation === false && !entity.isRock && entity.isPlayer === false && !entity.hasRole("escape-capsule") && entity.dataKey != "telescopemarker" && !entity.isCargo);
	}

	function gcm_findcapsules(entity) {
		return (entity.isShip && entity.hasRole("escape-capsule"));
	}

	function gcm_findblackboxes(entity) {
		// looking for black boxes or ships that have a black box
		return (entity.isShip && entity.shipClassName.toLowerCase().indexOf("black box") >= 0 || entity.primaryRole === "gcm_derelict");
	}

	function gcm_findasteroids(entity) {
		return (entity.isShip && entity.isRock === true && entity.isBoulder === false && entity.dataKey != "telescopemarker");
	}

	function gcm_findcommsrelays(entity) {
		return (entity.isShip && entity.primaryRole === "commsrelay");
	}

	function gcm_findcargo(entity) {
		return (entity.isCargo && entity.hasRole("cargopod"));
	}

	this._pointer += 1;
	if (this._pointer === 3) this._pointer = 0;
	var leftside = "";
	var rightside = "";

	switch (this._pointer) {
		case 0:
			leftside = "..|";
			rightside = "|..";
			break;
		case 1:
			leftside = ".|.";
			rightside = ".|.";
			break;
		case 2:
			leftside = "|..";
			rightside = "..|";
			break;
	}
	var text = expandDescription("[gcm_rangefinder_mode_short_" + this._mode + "]") + leftside + expandDescription("[gcm_rangefinder_active]") + rightside + "\n";
	switch (this._mode) {
		case 0:
			var targets = system.filteredEntities(this, gcm_findcapsules);
			break;
		case 1:
			var targets = system.filteredEntities(this, gcm_findblackboxes);
			break;
		case 2:
			var targets = system.filteredEntities(this, gcm_findasteroids);
			break;
		case 3:
			var targets = system.filteredEntities(this, gcm_findships);
			break;
		case 4:
			var targets = system.filteredEntities(this, gcm_findcommsrelays);
			break;
		case 5:
			var targets = system.filteredEntities(this, gcm_findcargo);
			break;
	}

	this._lastScan = "";
	if (targets && targets.length > 0) {
		var list = [];
		for (var i = 0; i < targets.length; i++) {
			var d = player.ship.position.distanceTo(targets[i]);
			if (d < this._maxRange) {
				// occasionally skip items that are on the edge of scanner range
				if ((this._maxRange - d) < (this._maxRange / 5) && Math.random() > ((this._maxRange - d) / (this._maxRange / 5))) continue;
				// get the last distance for this item
				if (targets[i].script.hasOwnProperty("_uniqueID") === false) {
					targets[i].script._uniqueID = this._next_uniqueID;
					this._next_uniqueID += 1;
				}
				var last = this._lastScanData[targets[i].script._uniqueID];
				if (!last) last = 0;
				list.push({
					tgt: targets[i],
					dist: d,
					range: (d < this._maxDirectionRange ? this.$getPointer(targets[i]) : (d > last ? "+" : "-"))
				});
				this._lastScanData[targets[i].script._uniqueID] = d;
			}
		}
		if (list.length > 0) {
			list.sort(compare);
			for (var i = 0; i < list.length; i++) {
				if (i >= 7) break;
				this._lastScan += this.$padTextRight(list[i].range, this._firstCol) +
					this.$padTextRight((list[i].dist / 1000).toFixed(2) + "kms", this._secondCol) +
					(this._mode === 3 ? this.$padTextRight(list[i].tgt.shipClassName, this._thirdCol) : "") + // displays the class name (eg "Python") of the ship detected
					"\n";
			}
		} else {
			this._lastScan += expandDescription("[gcm_rangefinder_empty]");
		}
	} else {
		this._lastScan += expandDescription("[gcm_rangefinder_empty]");
	}
	text += this._lastScan;

	player.ship.setMultiFunctionText("RangeFinderMFD", text, false);
}

//-------------------------------------------------------------------------------------------------------------
this.$getPointer = function(ent) {
	var p = player.ship;
	var pointer = "";
	if (p && p.vectorForward) {
		var f_dev = p.vectorForward.angleTo(ent.position.subtract(p));
		var r_dev = p.vectorRight.angleTo(ent.position.subtract(p));
		var u_dev = p.vectorUp.angleTo(ent.position.subtract(p));
		var s = " ";
		// > 1.56 means oppposite side (3.12 exact opposite)
		// so, f_dev < 0.01, r_dev = 1.57, u_dev = 1.57  -- aligned
		if (f_dev < 0.25) {
			if (f_dev < 0.25) s = "O";
			if (f_dev < 0.12) s = "o";
			if (f_dev < 0.05) s = "X";
		}
		if (f_dev >= 0.25) {
			if (r_dev > 1.69) {
				s = "<";
			}
			if (u_dev < 1.45) {
				s = "^";
			} else if(u_dev > 1.69) {
				s = "v";
			}
			if (r_dev < 1.45) {
				s = ">";
			}
			if (f_dev > 1.57) s= "A";
		}
		pointer = s;
	}
	return pointer;
}

//-------------------------------------------------------------------------------------------------------------
// appends space to currentText to the specified length in 'em'
this.$padTextRight = function $padTextRight(currentText, desiredLength, leftSwitch) {
	if (currentText == null) currentText = "";
	var hairSpace = String.fromCharCode(31);
	var ellip = "…";
	var currentLength = defaultFont.measureString(currentText);
	var hairSpaceLength = defaultFont.measureString(hairSpace);
	// calculate number needed to fill remaining length
	var padsNeeded = Math.floor((desiredLength - currentLength) / hairSpaceLength);
	if (padsNeeded < 1) {
		// text is too long for column, so start pulling characters off
		var tmp = currentText;
		do {
			tmp = tmp.substring(0, tmp.length - 2) + ellip;
			if (tmp === ellip) break;
		} while (defaultFont.measureString(tmp) > desiredLength);
		currentLength = defaultFont.measureString(tmp);
		padsNeeded = Math.floor((desiredLength - currentLength) / hairSpaceLength);
		currentText = tmp;
	}
	// quick way of generating a repeated string of that number
	if (!leftSwitch || leftSwitch === false) {
		return currentText + new Array(padsNeeded).join(hairSpace);
	} else {
		return new Array(padsNeeded).join(hairSpace) + currentText;
	}
}

//-------------------------------------------------------------------------------------------------------------
// returns the current scan limit for the range finder
this.$getScannerRange = function $getScannerRange() {
	var eq1 = "EQ_GCM_RANGE_FINDER_MFD";
	var eq2 = "EQ_GCM_RANGE_FINDER_EXTENDER";
	if (player.ship.equipmentStatus(eq2) === "EQUIPMENT_OK") return parseInt(EquipmentInfo.infoForKey(eq2).scriptInfo.scan_range) * 1000;
	if (player.ship.equipmentStatus(eq1) === "EQUIPMENT_OK") return parseInt(EquipmentInfo.infoForKey(eq1).scriptInfo.scan_range) * 1000;
	return 0;
}

//-------------------------------------------------------------------------------------------------------------
this.$getScannerDirectionalRange = function $getScannerDirectionalRange() {
	var eq1 = "EQ_GCM_RANGE_FINDER_MFD";
	var eq2 = "EQ_GCM_RANGE_FINDER_EXTENDER";
	if (player.ship.equipmentStatus(eq2) === "EQUIPMENT_OK") return parseInt(EquipmentInfo.infoForKey(eq2).scriptInfo.directional_range) * 1000;
	if (player.ship.equipmentStatus(eq1) === "EQUIPMENT_OK") return parseInt(EquipmentInfo.infoForKey(eq1).scriptInfo.directional_range) * 1000;
	return 0;
}

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

//-------------------------------------------------------------------------------------------------------------
this.$getEnergySetting = function $getEnergySetting() {
	var typ = "type1";
	if (player.ship.equipmentStatus("EQ_ENERGY_UNIT") === "EQUIPMENT_OK") typ = "type2";
	if (player.ship.equipmentStatus("EQ_NAVAL_ENERGY_UNIT") === "EQUIPMENT_OK") typ = "type3";
	return this._energySettings[typ][this._maxRange];
}