"use strict";

import Gtk from "gi://Gtk";
import GObject from "gi://GObject";
import GLib from "gi://GLib";
import type Gio from "gi://Gio";

import { ExtensionPreferences } from "resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js";

import PrefsBoxOrderItemRow from "./PrefsBoxOrderItemRow.js";
import PrefsBoxOrderListEmptyPlaceholder from "./PrefsBoxOrderListEmptyPlaceholder.js";

export default class PrefsBoxOrderListBox extends Gtk.ListBox {
    static {
        GObject.registerClass({
            GTypeName: "PrefsBoxOrderListBox",
            Template: GLib.uri_resolve_relative(import.meta.url, "../ui/prefs-box-order-list-box.ui", GLib.UriFlags.NONE),
            Properties: {
                BoxOrder: GObject.ParamSpec.string(
                    "box-order",
                    "Box Order",
                    "The box order this PrefsBoxOrderListBox is associated with.",
                    GObject.ParamFlags.READWRITE,
                    ""
                ),
            },
            Signals: {
                "row-move": {
                    param_types: [PrefsBoxOrderItemRow.$gtype, GObject.TYPE_STRING],
                },
            },
        }, this);
    }

    _boxOrder!: string;
    #settings: Gio.Settings;
    #rowSignalHandlerIds = new Map<PrefsBoxOrderItemRow, number[]>();

    /**
     * @param {Object} params
     */
    constructor(params = {}) {
        super(params);

        // Load the settings.
        this.#settings = ExtensionPreferences.lookupByURL(import.meta.url)!.getSettings();

        // Add a placeholder widget for the case, where no GtkListBoxRows are
        // present.
        this.set_placeholder(new PrefsBoxOrderListEmptyPlaceholder());
    }

    get boxOrder(): string {
        return this._boxOrder;
    }

    set boxOrder(value: string) {
        this._boxOrder = value;

        // Get the actual box order for the given box order name from settings.
        const boxOrder = this.#settings.get_strv(this._boxOrder);
        // Populate this GtkListBox with GtkListBoxRows for the items of the
        // given configured box order.
        for (const item of boxOrder) {
            const row = new PrefsBoxOrderItemRow({}, item);
            this.insertRow(row, -1);
        }

        this.determineRowMoveActionEnable();
        this.notify("box-order");
    }

    /**
     * Inserts the given PrefsBoxOrderItemRow to this list box at the given
     * position.
     * Also handles stuff like connecting signals.
     */
    insertRow(row: PrefsBoxOrderItemRow, position: number): void {
        this.insert(row, position);

        const signalHandlerIds: number[] = [];
        signalHandlerIds.push(row.connect("move", (row, direction) => {
            this.emit("row-move", row, direction);
        }));

        this.#rowSignalHandlerIds.set(row, signalHandlerIds);
    }

    /**
     * Removes the given PrefsBoxOrderItemRow from this list box.
     * Also handles stuff like disconnecting signals.
     */
    removeRow(row: PrefsBoxOrderItemRow): void {
        const signalHandlerIds = this.#rowSignalHandlerIds.get(row) ?? [];

        for (const id of signalHandlerIds) {
            row.disconnect(id);
        }

        this.remove(row);
    }

    /**
     * Saves the box order represented by `this` (and its
     * `PrefsBoxOrderItemRows`) to settings.
     */
    saveBoxOrderToSettings(): void {
        let currentBoxOrder: string[] = [];
        for (let potentialPrefsBoxOrderItemRow of this) {
            // Only process PrefsBoxOrderItemRows.
            if (!(potentialPrefsBoxOrderItemRow instanceof PrefsBoxOrderItemRow)) {
                continue;
            }

            const item = potentialPrefsBoxOrderItemRow.item;
            currentBoxOrder.push(item);
        }
        this.#settings.set_strv(this.boxOrder, currentBoxOrder);
    }

    /**
     * Determines whether or not each move action of each PrefsBoxOrderItemRow
     * should be enabled or disabled.
     */
    determineRowMoveActionEnable(): void {
        for (let potentialPrefsBoxOrderItemRow of this) {
            // Only process PrefsBoxOrderItemRows.
            if (!(potentialPrefsBoxOrderItemRow instanceof PrefsBoxOrderItemRow)) {
                continue;
            }

            const row = potentialPrefsBoxOrderItemRow;

            // If the current row is the topmost row in the topmost list box,
            // then disable the move-up action.
            if (row.get_index() === 0 && this.boxOrder === "left-box-order") {
                row.action_set_enabled("row.move-up", false);
            } else { // Else enable it.
                row.action_set_enabled("row.move-up", true);
            }

            // If the current row is the bottommost row in the bottommost list
            // box, then disable the move-down action.
            const rowNextSibling = row.get_next_sibling();
            if ((rowNextSibling instanceof PrefsBoxOrderListEmptyPlaceholder || rowNextSibling === null) && this.boxOrder === "right-box-order") {
                row.action_set_enabled("row.move-down", false);
            } else { // Else enable it.
                row.action_set_enabled("row.move-down", true);
            }
        }
    }
}
