// How often to query the server?
const REFRESH_INTERVAL_SEC = 60;

// Map layers
var markersLayer;
var geodesicsLayer;
var terminator;

// Load spots and populate the map.
function loadSpots() {
    $.getJSON('/api/v1/spots' + buildQueryString(), function(jsonData) {
        // Store data
        spots = jsonData;
        // Update map
        updateMap();
        terminator.setTime();
    });
}

// Build a query string for the API, based on the filters that the user has selected.
function buildQueryString() {
    var str =  "?";
    ["dx_continent", "de_continent", "mode", "source", "band", "sig"].forEach(fn => {
        if (!allFilterOptionsSelected(fn)) {
            str = str + getQueryStringFor(fn) + "&";
        }
    });
    str = str + "max_age=" + $("#max-spot-age option:selected").val();
    // Additional filters for the map view: No dupes, no QRT, only spots with good locations
    str = str + "&dedupe=true&allow_qrt=false&needs_good_location=true";
    return str;
}

// Update the spots map
function updateMap() {
    // Clear existing content
    markersLayer.clearLayers();
    geodesicsLayer.clearLayers();

    // Make new markers for all spots that match the filter
    spots.forEach(function (s) {
        var m = L.marker([s["dx_latitude"], s["dx_longitude"]], {icon: getIcon(s)});
        m.bindPopup(getTooltipText(s));
        markersLayer.addLayer(m);

        // Create geodesics if required
        if ($("#mapShowGeodesics")[0].checked && s["de_latitude"] != null && s["de_longitude"] != null) {
            try {
                var geodesic = L.geodesic([[s["de_latitude"], s["de_longitude"]], m.getLatLng()], {
                    color: bandToColor(s['band']),
                    wrap: false,
                    steps: 5
                });
                geodesicsLayer.addLayer(geodesic);
            } catch (e) {
                // Not sure what causes these but better to continue than to crash out
            }
        }
    });
}

// Get an icon for a spot, based on its band, using PSK Reporter colours, its program etc.
function getIcon(s) {
    return L.ExtraMarkers.icon({
        icon: sigToIcon(s["sig"], "fa-tower-cell"),
        iconColor: bandToContrastColor(s["band"]),
        markerColor: bandToColor(s["band"]),
        shape: 'circle',
        prefix: 'fa',
        svg: true
    });
}

// Tooltip text for the markers
function getTooltipText(s) {
    // Format DX call
    var dx_call = s["dx_call"];
    if (dx_call == null) {
        dx_call = "";
        dx_flag = "";
    }
    if (s["dx_ssid"] != null) {
        dx_call = dx_call + "-" + s["dx_ssid"];
    }

    // Format DX flag
    var dx_flag = "<i class='fa-solid fa-globe-africa'></i>";
    if (s["dx_flag"] && s["dx_flag"] != null && s["dx_flag"] != "") {
        dx_flag = s["dx_flag"];
    }

    // Format the frequency
    var freq_string = "Unknown"
    if (s["freq"] != null) {
        var mhz = Math.floor(s["freq"] / 1000000.0);
        var khz = Math.floor((s["freq"] - (mhz * 1000000.0)) / 1000.0);
        var hz = Math.floor(s["freq"] - (mhz * 1000000.0) - (khz * 1000.0));
        var hz_string = (hz > 0) ? hz.toFixed(0)[0] : "";
        freq_string = `<span class='freq-mhz freq-mhz-pad'>${mhz.toFixed(0)}</span><span class='freq-khz'>${khz.toFixed(0).padStart(3, '0')}</span><span class='freq-hz hideonmobile'>${hz_string}</span>`
    }

    // Format comment
    var commentText = "";
    if (s["comment"] != null) {
        commentText = escapeHtml(s["comment"]);
    }

    // Sig or fallback to source
    var sigSourceText = s["source"];
    if (s["sig"]) {
        sigSourceText = s["sig"];
    }

    // Format sig_refs
    var sig_refs = "";
    if (s["sig_refs"] != null) {
        var items = []
        for (var i = 0; i < s["sig_refs"].length; i++) {
            if (s["sig_refs"][i]["url"] != null) {
                items[i] = `<a href='${s["sig_refs"][i]["url"]}' title='${s["sig_refs"][i]["name"]}' target='_new' class='sig-ref-link'>${s["sig_refs"][i]["id"]}</a>`
            } else {
                items[i] = `${s["sig_refs"][i]["id"]}`
            }
        }
        sig_refs = items.join(", ");
    }

    // DX
    ttt = `<span class='nowrap'><span class='icon-wrapper'>${dx_flag}</span> <a href='https://www.qrz.com/db/${dx_call}' target='_blank' class="dx-link">${dx_call}</a></span><br/>`;

    // Frequency & band
    ttt += `<span class='icon-wrapper'><i class='fa-solid fa-radio markerPopupIcon'></i></span>&nbsp;${freq_string}`;
    if (s["band"] != null) {
        ttt += ` (${s["band"]})`;
    }
    // Mode
    if (s["mode"] != null) {
        ttt += ` &nbsp;&nbsp; <i class='fa-solid fa-wave-square markerPopupIcon'></i>&nbsp;${s["mode"]}`;
    }
    ttt += "<br/>";

    // Source / SIG / Ref
    ttt += `<span class='nowrap'><span class='icon-wrapper'><i class='fa-solid ${sigToIcon(s["sig"], "fa-tower-cell")}'></i></span>&nbsp;${sigSourceText} ${sig_refs}</span><br/>`;

    // Time
    ttt += `<span class='icon-wrapper'><i class='fa-solid fa-clock markerPopupIcon'></i></span>&nbsp;${moment.unix(s["time"]).fromNow()}`;

    // Comment
    if (commentText.length > 0) {
        ttt += `<br/><span class='icon-wrapper'><i class='fa-solid fa-comment markerPopupIcon'></i></span> ${commentText}`;
    }

    return ttt;
}

// Load server options. Once a successful callback is made from this, we then query spots and set up the timer to query
// spots repeatedly.
function loadOptions() {
    $.getJSON('/api/v1/options', function(jsonData) {
        // Store options
        options = jsonData;

        // First pass loading settings, so we can load the band colour scheme before the filters that need to use it
        loadSettings();
        setColorScheme($("#color-scheme option:selected").val());
        setBandColorScheme($("#band-color-scheme option:selected").val());

        // Add CSS for band toggle buttons
        addBandToggleColourCSS(options["bands"]);

        // Populate the filters panel
        generateBandsMultiToggleFilterCard(options["bands"]);
        generateSIGsMultiToggleFilterCard(options["sigs"]);
        generateMultiToggleFilterCard("#dx-continent-options", "dx_continent", options["continents"]);
        generateMultiToggleFilterCard("#de-continent-options", "de_continent", options["continents"]);
        generateModesMultiToggleFilterCard(options["modes"]);
        generateSourcesMultiToggleFilterCard(options["spot_sources"], spotProvidersEnabledByDefault);

        // Load URL params. These may select things from the various filter & display options, so the function needs
        // to be called after these are set up, but if the URL params ask for "embedded mode", this will suppress
        // loading settings, so this needs to be called before that.
        loadURLParams();

        // Load settings from settings storage now all the controls are available
        loadSettings();

        // Load spots and set up the timer
        loadSpots();
        setInterval(loadSpots, REFRESH_INTERVAL_SEC * 1000);
    });
}

// Method called when any display property is changed to reload the map and persist the display settings.
function displayUpdated() {
    updateMap();
    saveSettings();
}

// Set up the map
function setUpMap() {
    // Create map
    map = L.map('map', {
        zoomControl: false,
        minZoom: 2,
        maxZoom: 12
    });

    // Add basemap
    backgroundTileLayer = L.tileLayer.provider("OpenStreetMap.Mapnik", {
        opacity: 1,
        edgeBufferTiles: 1
    });
    backgroundTileLayer.addTo(map);
    backgroundTileLayer.bringToBack();

    // Add marker layer
    markersLayer = new L.LayerGroup();
    markersLayer.addTo(map);

    // Add geodesic layer
    geodesicsLayer = new L.LayerGroup();
    geodesicsLayer.addTo(map);

    // Add terminator/greyline
    terminator = L.terminator({
        interactive: false
    });
    terminator.setStyle({fillColor: '#00000050'});
    terminator.addTo(map);

    // Display a default view.
    map.setView([30, 0], 3);
}

// Startup
$(document).ready(function() {
    // Hide the extra things that need to be hidden on this page
    $(".hideonmap").hide();
    // Set up map
    setUpMap();
    // Call loadOptions(), this will then trigger loading spots and setting up timers.
    loadOptions();
    // Prevent mouse scroll and touch actions in the popup menus being passed through to the map
    L.DomEvent.disableScrollPropagation(document.getElementById('settingsButtonRowMap'));
    L.DomEvent.disableClickPropagation(document.getElementById('settingsButtonRowMap'));
});