"use strict";
this.name = "Lib_SystemMap";
this.author = "phkb";
this.copyright = "2026 phkb";
this.description = "Adds a system map, accessible from the F7 screen.";
this.licence = "CC BY-NC-SA 4.0";

this._exitScreen = "GUI_SCREEN_SYSTEM_DATA";
this._custom = [];
this._hint = false;
this._distanceUnits = ["[systemmap_distance_ou]", "[systemmap_distance_km]", "[systemmap_distance_m]", "[systemmap_distance_cz]"];
this._unitSelected = 1;

this._libSettings = {
    Name: this.name,
    Display: expandDescription("[systemmap_display]"),
    Alias: expandDescription("[systemmap_alias]"),
    Alive: "_libSettings",
    SInt: {
        S0: { Name: "_unitSelected", Def: 1, Min: 0, Max: 3, Desc: expandDescription("[systemmap_units]") },
        Info: expandDescription("[systemmap_sint_info]")
    },
};

//-------------------------------------------------------------------------------------------------------------
this.$addCustomMarker = function (obj) {
    if (!obj.ent && !obj.pos) {
        log(this.name, "No entity (obj.ent) or position (obj.pos) specified. Unable to add marker.");
        return;
    }
    if (!obj.map || obj.map == "") obj.map = "map_station.png";
    if (obj.hasOwnProperty("col") && isNaN(obj.col)) {
        log(this.name, "Invalid color (obj.col) value. Must be an integer between 0 and 12.");
        return;
    }
    obj._updated = false;
    if (obj.hasOwnProperty("displayName")) {
        obj._orig_displayName = obj.displayName;
        obj._updated = true;
    }
    this._custom.push(obj);
}

//-------------------------------------------------------------------------------------------------------------
this.startUpComplete = function () {
    if (oolite.compareVersion("1.91") <= 0) {
        setExtraGuiScreenKeys(this.name, {
            guiScreen: "GUI_SCREEN_SYSTEM_DATA",
            registerKeys: { "systemkey": [{ key: "s" }] },
            callback: this.$localSystemMap.bind(this)
        });

        if (worldScripts.ContextualHelp) {
            worldScripts.ContextualHelp.$addHelpTextToGuiScreen(this.name, "GUI_SCREEN_SYSTEM_DATA", "\n" + expandDescription("[systemmap_helper]"));
        }
    } else {
        this.$initInterface(player.ship.dockedStation);
        this._exitScreen = "GUI_SCREEN_INTERFACES";
    }

    if (missionVariables.SystemMap_Units) this._unitSelected = parseInt(missionVariables.SystemMap_Units);
    // register our settings
    worldScripts.Lib_Config._registerSet(this._libSettings);

    // if the tianve pulsar isn't installed, delete the function that would otherwise run every jump
    if (!worldScripts["oolite-tianve"]) {
        delete this.shipExitedWitchspace;
    }
}

//-------------------------------------------------------------------------------------------------------------
this.playerWillSaveGame = function () {
    missionVariables.SystemMap_Units = this._unitSelected;
}

//-------------------------------------------------------------------------------------------------------------
this.shipWillEnterWitchspace = function () {
    this._custom.length = 0;
}

//-------------------------------------------------------------------------------------------------------------
this.shipExitedWitchspace = function () {
    // add in the tianve pulsar, if installed
    if (galaxyNumber == 0 && system.ID == 246 && worldScripts["oolite-tianve"]) {
        var ve = system.allVisualEffects;
        for (var i = 0; i < ve.length; i++) {
            if (ve[i].dataKey == "tianve_pulsar_flashers") {
                this.$addCustomMarker({ map: "lib_starmap_i8.png", pos: ve[i].position, col: 6, displayName: expandDescription("[systemmap_pulsar]") + this.$getDistance(ve[i].position) });
                break;
            }
        }
    }
}

//-------------------------------------------------------------------------------------------------------------
this.shipDockedWithStation = function (station) {
    if (oolite.compareVersion("1.91") > 0) {
        this.$initInterface(station);
    }
}

//-------------------------------------------------------------------------------------------------------------
this.guiScreenChanged = function (to, from) {
    if (guiScreen == "GUI_SCREEN_SYSTEM_DATA" && oolite.compareVersion("1.91") <= 0 && !this._hint) {
        this._hint = true;
        player.consoleMessage(expandDescription("[systemmap_helper]"))
    }
}

//-------------------------------------------------------------------------------------------------------------
this.$initInterface = function $initInterface(station) {
    station.setInterface(this.name, {
        title: expandDescription("[systemmap_title]"),
        category: expandDescription("[systemmap_category]"),
        summary: expandDescription("[systemmap_summary]"),
        callback: this.$localSystemMap.bind(this)
    });
}

//-------------------------------------------------------------------------------------------------------------
this.$localSystemMap = function () {
    var p = player.ship;
    if (!p.docked) return; // only while docked

    if (system.isInterstellarSpace) {
        // inform player you can't access screen in interstellar space
        mission.runScreen({
            title: expandDescription("[systemmap_title]"),
            message: expandDescription("[systemmap_interstellar]"),
            exitScreen: this._exitScreen
        });
        return;
    }

    // make sure the planetary compass names have been run.
    if (worldScripts["planetaryCompass_worldScript.js"]) {
        worldScripts["planetaryCompass_worldScript.js"].$setUpNames();
    }

    // if xenon installed and active, pick the appropriate background
    var xenon = null;
    if (worldScripts.XenonUI) {
        var found = true;
        var lib = worldScripts.Lib_GUI;
        if (lib.$cur != "XenonUI") found = false;
        if (found) {
            var xui = worldScripts.XenonUI;
            xenon = { name: "xenon_" + xui._ratio + (xui._amber ? "_amber" : "") + ".png", height: 546 };
        }
    }

    var stn_found = false;
    if (p.docked && p.dockedStation.isMainStation) stn_found = true;

    // create array and add the sun
    var name = "";

    //if (system)
    var sini = [{ map: "lib_starmap_0.png", ent: system.sun, col: 12, displayName: this.$getName(system.sun) + this.$getDistance(system.sun.position) }];

    // add in the witchpoint
    var wp = system.shipsWithRole("buoy-witchpoint")[0];
    if (wp) {
        sini.push({ map: "lib_starmap_2.png", ent: wp, displayName: expandDescription("[systemmap_witchpoint]") + this.$getDistance(wp.position) });
    }

    // add in the players location
    if (p.docked && p.dockedStation.hasRole("planetFall2_surface")) {
        sini.push({ map: "map_point.png", pos: p.dockedStation.linkedVE.position, col: 1, displayName: expandDescription("[systemmap_yourlocation]") });
    } else {
        sini.push({ map: "map_point.png", ent: p, col: 1, displayName: expandDescription("[systemmap_yourlocation]") })
    }

    // add the main planet
    var pf = this.$planetHasLandingSpots(system.mainPlanet);
    if (system.mainPlanet.isGasGiant || system.mainPlanet.solarGasGiant) {
        sini.push({ map: "map_planet_1.png", ent: system.mainPlanet, col: 4, displayName: this.$getName(system.mainPlanet) + this.$getDistance(system.mainPlanet.position) });
    } else {
        sini.push({ map: "map_planet_" + (pf ? "pf" : "") + "1.png", ent: system.mainPlanet, col: 2, displayName: this.$getName(system.mainPlanet) + this.$getDistance(system.mainPlanet.position) });
    }

    // add all the planets and moons (except for the main planet)
    function plfn(entity) { return entity.isPlanet && !entity.isMainPlanet; }

    var pl = system.filteredEntities(this, plfn, system.sun);
    var p_count = 2;
    for (var i = 0; i < pl.length; i++) {
        pf = this.$planetHasLandingSpots(pl[i]);
        if (!pl[i].hasAtmosphere) {
            sini.push({ map: "map_moon" + (pf ? "_pf" : "") + ".png", ent: pl[i], col: 10, displayName: this.$getName(pl[i]) + this.$getDistance(pl[i].position) });
        } else {
            if (p_count > 9) {
                if (pl[i].solarGasGiant || pl[i].isGasGiant) {
                    sini.push({ map: "map_planet.png", ent: pl[i], col: 4, displayName: this.$getName(pl[i]) + this.$getDistance(pl[i].position) });
                } else {
                    sini.push({ map: "map_planet" + (pf ? "_pf" : "") + ".png", ent: pl[i], col: 2, displayName: this.$getName(pl[i]) + this.$getDistance(pl[i].position) });
                }
            } else {
                if (pl[i].solarGasGiant || pl[i].isGasGiant) {
                    sini.push({ map: "map_planet_" + p_count + ".png", ent: pl[i], col: 4, displayName: this.$getName(pl[i]) + this.$getDistance(pl[i].position) });
                } else {
                    sini.push({ map: "map_planet_" + (pf ? "pf" : "") + p_count + ".png", ent: pl[i], col: 2, displayName: this.$getName(pl[i]) + this.$getDistance(pl[i].position) });
                }
            }
            p_count += 1;
        }
    }

    // add the main station
    sini.push({ map: "map_station.png", ent: system.mainStation, col: 3, displayName: this.$getName(system.mainStation) + this.$getDistance(system.mainStation.position) });

    // add all the main stations (except for the actual main station)
    function stfn(entity) {
        return entity.isShip &&
            entity.hasNPCTraffic && // only stations with traffic
            (entity.scanClass != "CLASS_ROCK" || entity.beaconCode != null) && // only rock hermits with a beacon
            (["hunter", "neutral", "private"].indexOf(entity.allegiance) == -1 || entity.beaconCode != null) && // only hunter/neutral/private stations with a beacon          
            !entity.hasRole("planetFall2_surface") && // not planetfall locations
            !entity.isMainStation && // not the main station
            entity.maxSpeed == 0; // must not be moving
    }

    var stns = system.filteredEntities(this, stfn, system.mainPlanet);
    for (var i = 0; i < stns.length; i++) {
        if (p.docked && stns[i] == p.dockedStation) stn_found = true;
        sini.push({ map: "map_station.png", ent: stns[i], displayName: this.$getName(stns[i]) + this.$getDistance(stns[i].position) });
    }

    // make sure the players current station is on the map.
    if (stn_found == false) {
        if (p.docked && !p.dockedStation.hasRole("planetFall2_surface")) {
            sini.push({ map: "map_station.png", ent: p.dockedStation, col: 5, displayName: this.$getName(p.dockedStation) });
        }
    }

    // add any custom items
    if (this._custom.length > 0) {
        for (var i = 0; i < this._custom.length; i++) {
            var pos = p.position;
            if (this._custom[i].hasOwnProperty("ent")) pos = this._custom[i].ent.position;
            if (this._custom[i].hasOwnProperty("pos")) pos = this._custom[i].pos;

            if (this._custom[i]._updated == false) {
                this._custom[i].displayName = this.$getName(this._custom[i].ent) + this.$getDistance(pos);
            } else {
                this._custom[i].displayName = this._custom[i]._orig_displayName + this.$getDistance(pos);
            }
            sini.push(this._custom[i]);
        }
    }

    worldScripts.Lib_Starmap._start({
        ini: sini,
        title: expandDescription("[systemmap_screen_title]"),
        exit: this._exitScreen,
        viewControls: true,
        background: xenon,
        exitText: expandDescription("[systemmap_close]")
    });
}

//-------------------------------------------------------------------------------------------------------------
this.$getDistance = function (pos) {
    var dist = player.ship.position.distanceTo(pos);
    if (Math.round(dist) == 0) return "";
    return expandDescription(this._distanceUnits[this._unitSelected], { dist: dist });
}

//-------------------------------------------------------------------------------------------------------------
this.$getName = function (entity) {
    if (entity.isPlanet && !entity.isMainPlanet) {
        var beacon = this.$checkForBeacon(entity);
        if (beacon && beacon.beaconLabel && beacon.beaconLabel != "") {
            return beacon.beaconLabel;
        }
    }
    if (entity.displayName && entity.displayName != "") {
        return entity.displayName;
    } else if (entity.name && entity.name != "") {
        return entity.name;
    }
    return "";
}

//-------------------------------------------------------------------------------------------------------------
this.$checkForBeacon = function (entity) {
    var beacons = this.$findEntitiesWithBeacons(entity);
    if (beacons.length == 0) return null;
    return beacons[0];
}

//-------------------------------------------------------------------------------------------------------------
this.$findEntitiesWithBeacons = function (entity) {
    function _beacons(ent) {
        return !(ent.isPlayer) &&
            (ent.beaconCode != "" && ent.beaconCode != null) &&
            (ent.displayName != "Jump Marker") &&
            (ent.AI != "EscortDeck_AI.plist");
    }
    return system.filteredEntities(this, _beacons, entity, 1000);
}

//-------------------------------------------------------------------------------------------------------------
this.$planetHasLandingSpots = function (planet) {
    if (!worldScripts.PlanetFall2 && !worldScripts.PlanetFall) return false;
    if (worldScripts.PlanetFall) {
        return !(planet.hasOwnProperty("PFNoLand") || planet.hasOwnProperty("solarGasGiant") || planet.hasOwnProperty("isGasGiant"));
    }
    return (worldScripts.PlanetFall2.$getPlanetLandingSites(planet).length > 0);
}
