/*
    Copyright 2025 Roman Lefler

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
import Adw from "gi://Adw";
import Gtk from "gi://Gtk";
import { Location } from "../location.js";
import { UserInputError } from "../errors.js";
import { gettext as _g } from "resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js";
/**
 * Shows a dialog to create or edit a location.
 * @param parent Parent window for dialog
 * @param loc Location to edit, or undefined to create a blank one
 * @returns The new location, or null if canceled. Rejects if inputs are invalid.
 */
export async function editLocation(parent, loc) {
    const dialog = new Gtk.Window({
        transient_for: parent,
        title: loc ? _g("Edit %s").format(loc.getName()) : _g("New Location"),
        modal: true,
        width_request: parent.get_width() * 0.75,
        height_request: parent.get_height() * 0.75
    });
    const page = new Adw.PreferencesPage();
    const group = new Adw.PreferencesGroup();
    const nameRow = new Adw.EntryRow({
        title: _g("Name"),
        text: loc?.getRawName() ?? ""
    });
    nameRow.connect("changed", w => {
        if (w.text === "") {
            w.add_css_class("error");
            save.set_sensitive(false);
        }
        else {
            w.remove_css_class("error");
            save.set_sensitive(true);
        }
    });
    // TODO: There should be a radio with Here vs. Coords
    // TODO: More localized coordinates; right now positive/negative with space separator is adequate
    // (see https://i18n.leifgehrmann.com/geo-coordinates/)
    let coordsText;
    if (!loc)
        coordsText = "40.7 -73.97";
    else if (loc.isHere())
        coordsText = "here";
    else {
        const latLon = await loc.latLon();
        coordsText = `${latLon.lat} ${latLon.lon}`;
    }
    const coordsRow = new Adw.EntryRow({
        // This is done kind of goofy since Coordinates is already translated
        title: _g("%s (e.g. \"%s\")").format(_g("Coordinates"), "40.7 -73.97"),
        text: coordsText
    });
    coordsRow.connect("changed", w => {
        const txt = w.text;
        const coords = parseCoords(txt);
        const valid = txt === "here" || coords !== null;
        if (valid) {
            w.remove_css_class("error");
            save.set_sensitive(true);
        }
        else {
            w.add_css_class("error");
            save.set_sensitive(false);
        }
    });
    group.add(nameRow);
    group.add(coordsRow);
    const save = new Gtk.Button({
        child: new Adw.ButtonContent({
            icon_name: "document-save-symbolic",
            label: _g("Save")
        }),
        css_classes: ["suggested-action"]
    });
    if (nameRow.text === "")
        save.set_sensitive(false);
    group.add(save);
    page.add(group);
    dialog.set_child(page);
    dialog.set_default_widget(save);
    nameRow.connect("entry-activated", () => {
        save.emit("clicked");
    });
    coordsRow.connect("entry-activated", () => {
        save.emit("clicked");
    });
    const prom = new Promise((resolve, reject) => {
        save.connect("clicked", () => {
            const name = nameRow.text;
            const coords = coordsRow.text;
            if (coords === "here") {
                resolve(Location.newHere(name || undefined));
            }
            else {
                const parsedCoords = parseCoords(coords);
                if (parsedCoords) {
                    if (!name) {
                        reject(new UserInputError(_g("Name is required.")));
                    }
                    else
                        resolve(Location.newCoords(name, parsedCoords[0], parsedCoords[1]));
                }
                else {
                    reject(new UserInputError(_g("Invalid coordinates entry.")));
                }
            }
            dialog.close();
        });
        dialog.connect("close-request", () => {
            dialog.destroy();
            resolve(null);
        });
    });
    dialog.show();
    return prom;
}
function isDecimalNum(s) {
    return /^-?(\d+\.?\d*|\.\d+)$/.test(s);
}
function parseCoords(s) {
    // e.g. replaces 5,43 with 5.43 for l10n
    s = s.replace(/(\d)(,)(\d)/g, "$1.$2");
    // If someone puts a comma separator, replace it with a space
    s = s.replace(/^\s*(-?[0-9.]+)\s*,\s*(-?[0-9.]+)\s*$/, "$1 $2");
    // Disallow garbage at end (e.g. Deg W)
    if (!s.match(/^\s*-?[0-9.]+\s+-?[0-9.]+\s*$/))
        return null;
    const split = s.split(" ");
    if (split.length !== 2)
        return null;
    for (const s of split)
        if (!isDecimalNum(s.trim()))
            return null;
    // Parse float handles trimming
    const lat = parseFloat(split[0]);
    const lon = parseFloat(split[1]);
    if (isNaN(lat) || isNaN(lon))
        return null;
    else
        return [lat, lon];
}
