// Storage for the options that the server gives us. This will define our filters.
let options = {};
// Normally load user settings from local storage, unless embedded mode is in use
let useLocalStorage = true;

// Save settings to local storage. Suppressed if "use local storage" is false.
function saveSettings() {
    if (useLocalStorage) {
        // Find all storeable UI elements, store a key of "element id:property name" mapped to the value of that
        // property. For a checkbox, that's the "checked" property.
        $(".storeable-checkbox").each(function () {
            localStorage.setItem("#" + $(this)[0].id + ":checked", JSON.stringify($(this)[0].checked));
        });
        $(".storeable-select").each(function () {
            localStorage.setItem("#" + $(this)[0].id + ":value", JSON.stringify($(this)[0].value));
        });
        $(".storeable-text").each(function () {
            localStorage.setItem("#" + $(this)[0].id + ":value", JSON.stringify($(this)[0].value));
        });
        // Password fields are only saved if the corresponding "remember password" checkbox is ticked.
        $(".password-field").each(function () {
            const pwKey = "#" + $(this)[0].id + ":value";
            const rememberCheckboxId = $(this).data("remember-checkbox");
            if (rememberCheckboxId && $("#" + rememberCheckboxId)[0] && $("#" + rememberCheckboxId)[0].checked) {
                localStorage.setItem(pwKey, JSON.stringify($(this)[0].value));
            } else {
                localStorage.removeItem(pwKey);
            }
        });
    }
}

// Load settings from local storage and set up the filter selectors. Suppressed if "use local storage" is false.
function loadSettings() {
    if (useLocalStorage) {
        // Find all local storage entries and push their data to the corresponding UI element
        Object.keys(localStorage).forEach(function (key) {
            if (key.startsWith("#") && key.includes(":")) {
                // Split the key back into an element ID and a property
                const split = key.split(":");
                $(split[0]).prop(split[1], JSON.parse(localStorage.getItem(key)));
            }
        });
    }
}

// Load and apply any URL params. This is used for "embedded mode" where another site can embed a version of
// Spothole and provide its own interface options rather than using the user's saved ones. These may select things
// from the various filter & display options, so this 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 occurs..
function loadURLParams() {
    let params = new URLSearchParams(document.location.search);

    // Handle embedded mode. We set a global to e.g. suppress loading/saving settings, and apply an attribute to the
    // top-level html element to use CSS selectors to remove bits of UI.
    let embedded = params.get("embedded");
    if (embedded != null && embedded === "true") {
        useLocalStorage = false;
        $("html").attr("embedded-mode", "true");
    }

    // Handle other params
    updateSelectFromParam(params, "color_scheme", "color_scheme");
    updateSelectFromParam(params, "time_zone", "time_zone"); // Only on Spots and Alerts pages
    updateSelectFromParam(params, "limit", "spots_to_fetch"); // Only on Spots page
    updateSelectFromParam(params, "limit", "alerts_to_fetch"); // Only on Alerts page
    updateSelectFromParam(params, "max_age", "max_spot_age"); // Only on Map & Bands pages
    updateFilterFromParam(params, "band", "band");
    updateFilterFromParam(params, "sig", "sig");
    updateFilterFromParam(params, "source", "source");
    updateFilterFromParam(params, "mode", "mode");
    updateFilterFromParam(params, "dx_continent", "dx_continent");
    updateFilterFromParam(params, "de_continent", "de_continent");
}

// Update an HTML select element so that its value matches the given parameter
function updateSelectFromParam(params, paramName, selectID) {
    let v = params.get(paramName);
    if (v != null) {
        $("#" + selectID).prop("value", v);
        // Extra check if this is the "color scheme" select
        if (selectID === "color_scheme") {
            setColorScheme(v);
        }
    }
}

// Update a set of HTML checkbox elements describing a filter of the given name, so that any items named in the
// parameter (as a comma-separated list) will be enabled, and all others disabled. e.g. if paramName is
// "filter-band" and the params contain "filter-band=20m,40m", and prefix is "band", then #filter-button-band-30m
// would be disabled but #filter-button-band-20m and #filter-button-band-40m would be enabled.
function updateFilterFromParam(params, paramName, filterName) {
    let v = params.get(paramName);
    if (v != null) {
        // First uncheck all options for the filter
        $(".filter-button-" + filterName).prop("checked", false);
        // Now find out which ones should be enabled
        let s = v.split(",");
        s.forEach(val => $("#filter-button-" + filterName + "-" + val).prop("checked", true));
    }
}

// For a parameter, such as dx_continent, get the query string for the current filter options.
function getQueryStringFor(parameter) {
    return parameter + "=" + encodeURIComponent(getSelectedFilterOptions(parameter));
}

// For a parameter, such as dx_continent, get the filter options that are currently selected in the UI.
function getSelectedFilterOptions(parameter) {
    return $(".filter-button-" + parameter).filter(function () {
        return this.checked;
    }).map(function () {
        return this.value;
    }).get().join(",");
}

// For a parameter, such as dx_continent, return true if all possible options are enabled. (In this case, we don't need
// to bother sending this as one of the query parameters to the API; no parameter provided implies "send everything".)
function allFilterOptionsSelected(parameter) {
    const filter = $(".filter-button-" + parameter).filter(function () {
        return !this.checked;
    }).get();
    return filter.length === 0;
}


// Generate a filter card with inline checkboxes plus All/None links.
function generateMultiToggleFilterCard(elementID, filterQuery, options) {
    const $row = $('<div>');
    options.forEach(o => {
        $row.append(`<div class="form-check form-check-inline"><input type="checkbox" class="form-check-input filter-button-${filterQuery} storeable-checkbox" id="filter-button-${filterQuery}-${o}" value="${o}" autocomplete="off" onClick="filtersUpdated()" checked><label class="form-check-label" for="filter-button-${filterQuery}-${o}">${o}</label></div>`);
    });
    $(elementID).append($row);
    $(elementID).append(`<div class="mt-1"><a href="#" onclick="toggleFilterButtons('${filterQuery}', true); return false;">All</a> &nbsp; <a href="#" onclick="toggleFilterButtons('${filterQuery}', false); return false;">None</a></div>`);
}

// Method called when "All" or "None" is clicked
function toggleFilterButtons(filterQuery, state) {
    $(".filter-button-" + filterQuery).each(function () {
        $(this).prop('checked', state);
    });
    filtersUpdated();
}

// When the "use local time" field is changed, reload the table and save settings
function timeZoneUpdated() {
    updateTable();
    saveSettings();
}

// When one of the column toggle checkboxes are changed, reload the table and save settings
function columnsUpdated() {
    updateTable();
    saveSettings();
}

// Function to set the colour scheme based on the state of the UI select box
function setColorSchemeFromUI() {
    let theme = $("#color_scheme option:selected").val();
    if (theme !== "") {
        setColorScheme(theme);
        saveSettings();
    }
}

// Function to set the color scheme. Supported values: "dark", "light", "auto"
function setColorScheme(mode) {
    let effectiveModeDark = mode === "dark";
    if (mode === "auto") {
        effectiveModeDark = window.matchMedia('(prefers-color_scheme: dark)').matches
    }
    $("html").attr("data-bs-theme", effectiveModeDark ? "dark" : "light");
    const metaThemeColor = document.querySelector("meta[name=theme-color]");
    metaThemeColor.setAttribute("content", effectiveModeDark ? "black" : "white");
    const metaAppleStatusBarStyle = document.querySelector("meta[name=apple-mobile-web-app-status-bar-style]");
    metaAppleStatusBarStyle.setAttribute("content", effectiveModeDark ? "black-translucent" : "white-translucent");
}

// Startup function to determine whether to use light or dark mode, or leave as auto
function usePreferredTheme() {
    // Work out if we have ever explicitly saved the value of our select box. If so, we set our colour scheme now based
    // on that. If not, we let the select stay with nothing selected, so that the server sets it to whatever the
    // server's default is when the options call is retrieved.
    let val = localStorage.getItem("#color_scheme:value");
    if (val != null) {
        setColorScheme(JSON.parse(val));
    }
}

// Sets up a listener on the OS light-dark theme change. If the Spothole user theme is set to Auto, the UI will be
// updated, otherwise if the Spothole user theme is forced to light or dark, that preference will remain.
function listenForOSThemeChange() {
    window.matchMedia('(prefers-color_scheme: dark)').addEventListener('change', () => {
        setColorScheme($("#color_scheme option:selected").val());
    });
}

// Panel toggle functions
const PANELS = [
    {area: "#filters-area", button: "#filters-button"},
    {area: "#display-area", button: "#display-button"},
    {area: "#data-area", button: "#data-button"},
];

// Toggle a panel open or closed. If opening, all other visible panels are closed first.
// areaId is the jQuery selector for the panel's content area, e.g. "#filters-area".
function togglePanel(areaId) {
    if (!$(areaId).is(":visible")) {
        PANELS.forEach(p => {
            if (p.area !== areaId && $(p.area).is(":visible")) {
                $(p.area).hide();
                $(p.button).button("toggle");
            }
        });
    }
    $(areaId).toggle();
}

// Close a panel and deactivate its toggle button.
function closePanel(areaId) {
    const panel = PANELS.find(p => p.area === areaId);
    if (panel) {
        $(panel.button).button("toggle");
    }
    $(areaId).hide();
}

function toggleFiltersPanel() {
    togglePanel("#filters-area");
}

function closeFiltersPanel() {
    closePanel("#filters-area");
}

function toggleDisplayPanel() {
    togglePanel("#display-area");
}

function closeDisplayPanel() {
    closePanel("#display-area");
}

function toggleDataPanel() {
    togglePanel("#data-area");
}

function closeDataPanel() {
    closePanel("#data-area");
}

// Build a headers object containing any QRZ.com / HamQTH credentials the user has supplied,
// provided the corresponding "enabled" checkbox is ticked.
function getCredentialHeaders() {
    const headers = {};
    if ($("#qrz_enabled")[0] && $("#qrz_enabled")[0].checked) {
        const qrzUsername = $("#qrz-username").val();
        const qrzPassword = $("#qrz-password").val();
        if (qrzUsername) headers["X-QRZ-Username"] = qrzUsername;
        if (qrzPassword) headers["X-QRZ-Password"] = qrzPassword;
    }
    if ($("#hamqth_enabled")[0] && $("#hamqth_enabled")[0].checked) {
        const hamqthUsername = $("#hamqth-username").val();
        const hamqthPassword = $("#hamqth-password").val();
        if (hamqthUsername) headers["X-HamQTH-Username"] = hamqthUsername;
        if (hamqthPassword) headers["X-HamQTH-Password"] = hamqthPassword;
    }
    return headers;
}


// Startup
$(document).ready(function () {
    usePreferredTheme();
    listenForOSThemeChange();
});
