import GObject from 'gi://GObject';
import St from 'gi://St';
import Gio from 'gi://Gio';
import Clutter from 'gi://Clutter';
import Shell from 'gi://Shell';
import GLib from 'gi://GLib';
import { Extension, gettext as _ } from 'resource:///org/gnome/shell/extensions/extension.js';
import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js';
import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
import * as Main from 'resource:///org/gnome/shell/ui/main.js';

const AppPinner = GObject.registerClass(
    class AppPinner extends PanelMenu.Button {

        // 1. Costruttore e inizializzazione
        _init(settings) {
            super._init(0.0, _('App Pinner'));
            this._settings = settings;
            this._destroyed = false;
            this._pendingApps = new Set();
            this._settingsHandler = [];
            this._longPressTimeoutId = null;

            this._windowTracker = Shell.WindowTracker.get_default();
            this._windowTracker.connect('tracked-windows-changed', this._updateRunningIndicators.bind(this));

            this._settingsHandler.push(
                this._settings.connect('changed::show-in-panel', () => this._updateVisibility()),
                this._settings.connect('changed::sort-alphabetically', () => this._refreshUI()),
                this._settings.connect('changed::show-pin-icon', () => this._updatePinIconVisibility())
            );

            this._appSystem = Shell.AppSystem.get_default();
            this._runningTracker = new Set();
            this._appStateChangedId = this._appSystem.connect('app-state-changed',
                () => this._updateRunningIndicators());

            // Contenitore principale
            this._mainContainer = new St.BoxLayout({
                style_class: 'panel-button',
                vertical: false,
                x_align: Clutter.ActorAlign.CENTER,
                y_align: Clutter.ActorAlign.CENTER,
                reactive: true,
                track_hover: true
            });
            this.add_child(this._mainContainer);

            // Icona del menu
            this._menuIcon = new St.Icon({
                icon_name: 'view-pin-symbolic',
                style_class: 'system-status-icon app-pinner-menu-icon',
                icon_size: 15
            });
            this._mainContainer.add_child(this._menuIcon);

            // Contenitore icone pinnate
            this._pinnedIconsBox = new St.BoxLayout({
                style_class: 'app-pinner-icons',
                vertical: false,
                x_align: Clutter.ActorAlign.CENTER,
                y_align: Clutter.ActorAlign.CENTER,
                x_expand: true,
                y_expand: true
            });
            this._mainContainer.add_child(this._pinnedIconsBox);

            this._updateIconsSpacing();

            // Connessioni impostazioni
            this._settingsHandler.push(
                this._settings.connect('changed::icon-size', () => this._refreshUI()),
                this._settings.connect('changed::spacing', () => {
                    this._updateIconsSpacing();
                    this._pinnedIconsBox.queue_relayout();
                }),
                this._settings.connect('changed::enable-labels', () => this._refreshUI()),
                this._settings.connect('changed::pinned-apps', () => this._refreshUI()),
                this._settings.connect('changed::indicator-color', () => {
                    this._updateIndicatorColor();
                    this._updateRunningIndicators();
                })
            );


            this.menu.actor.connect('button-press-event', (actor, event) => {
                const target = event.get_source();

                if (!this.menu.actor.contains(target)) {
                    this.menu.close();
                }
            });

            this._buildMenu();
            this._refreshUI();

            this._logindProxy = new Gio.DBusProxy({
                g_connection: Gio.DBus.system,
                g_interface_name: 'org.freedesktop.login1.Manager',
                g_object_path: '/org/freedesktop/login1',
                g_name: 'org.freedesktop.login1'
            });

            this._logindProxy.init_async(GLib.PRIORITY_DEFAULT, null, (proxy, res) => {
                try {
                    this._logindProxy.init_finish(res);
                    this._logindId = this._logindProxy.connectSignal('PrepareForSleep',
                        this._handleSleepSignal.bind(this));
                } catch (e) {
                    console.error('Errore inizializzazione proxy login1:', e.message);
                }
            });

            this._appStateChangedId = this._appSystem.connect('app-state-changed', () => {
                this._addTimeout(100, () => {
                    this._updateRunningIndicators();
                    return GLib.SOURCE_REMOVE;
                });
            });

            this._settingsHandler.push(
                this._settings.connect('changed::custom-links', () => this._refreshUI())
            );

            this._timeoutIds = new Set();

        }

        _updateVisibility() {
            const showInPanel = this._settings.get_boolean('show-in-panel');
            this._pinnedIconsBox.visible = showInPanel;
            this._updatePinIconVisibility();
            this._pinnedIconsBox.queue_relayout();
            this.queue_relayout();
        }

        _updatePinIconVisibility() {
            const showPinIcon = this._settings.get_boolean('show-pin-icon');
            const hasPinnedApps = this._settings.get_strv('pinned-apps').length > 0;
            const noPinnedApps = !hasPinnedApps;

            const iconSize = noPinnedApps ? 18 : 18;
            this._menuIcon.icon_size = iconSize;

            this._menuIcon.opacity = (noPinnedApps || showPinIcon) ? 255 : 0;
            this._menuIcon.reactive = (noPinnedApps || showPinIcon);
            this._menuIcon.set_width(showPinIcon ? -1 : iconSize);

            this.queue_relayout();
        }

        _addTimeout(interval, callback) {
            const sourceId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, interval, () => {
                const result = callback();
                this._timeoutIds.delete(sourceId);
                return result;
            });
            this._timeoutIds.add(sourceId);
            return sourceId;
        }

        // 2. Metodi lifecycle e gestione stato
        destroy() {
            if (this._destroyed) return;
            this._destroyed = true;

            if (this._logindId && this._logindProxy) {
                this._logindProxy.disconnect(this._logindId);
                this._logindProxy = null;
            }

            this._settingsHandler?.forEach(h => this._settings.disconnect(h));
            this._settingsHandler = null;

            this._pinnedIconsBox?.destroy();
            this._searchInput?.destroy();

            if (this._appStateChangedId) {
                this._appSystem.disconnect(this._appStateChangedId);
                this._appStateChangedId = null;
            }

            if (this._timeoutIds) {
                this._timeoutIds.forEach(id => GLib.Source.remove(id));
                this._timeoutIds.clear();
            }

            if (this._longPressTimeoutId !== null) {
                GLib.Source.remove(this._longPressTimeoutId);
                this._timeoutIds.delete(this._longPressTimeoutId);
                this._longPressTimeoutId = null;
            }

            super.destroy();
        }

        // 3. Costruzione UI principale
        _buildMenu() {
            this.menu.removeAll();

            // Barra di ricerca
            const searchEntry = new PopupMenu.PopupBaseMenuItem();
            const searchBox = new St.BoxLayout({
                vertical: false,
                style_class: 'search-box',
                x_expand: true,
                x_align: Clutter.ActorAlign.FILL
            });

            this._searchInput = new St.Entry({
                hint_text: _('Search applications...'),
                can_focus: true,
                x_expand: true
            });

            searchBox.add_child(this._searchInput);
            searchEntry.actor.add_child(searchBox);
            this.menu.addMenuItem(searchEntry);

            // Sezione risultati
            this._resultsSection = new PopupMenu.PopupMenuSection();
            this.menu.addMenuItem(this._resultsSection);

            // Sezione pinnati
            this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
            this._pinnedSection = new PopupMenu.PopupMenuSection();
            this.menu.addMenuItem(this._pinnedSection);

            this._searchInput.clutter_text.connect('text-changed', () => this._updateSearch());

            // Sezione aggiungi link
            this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
            const linkSection = new PopupMenu.PopupMenuSection();
            this.menu.addMenuItem(linkSection);

            const linkBox = new St.BoxLayout({
                vertical: false,
                style_class: 'app-pinner-link-box',
                x_expand: true,
                margin_top: 6,
                margin_bottom: 6
            });
            this._linkInput = new St.Entry({
                hint_text: _('Enter URL...'),
                can_focus: true,
                style_class: 'app-pinner-link-input'
            });

            const addLinkBtn = new St.Button({
                style_class: 'app-pinner-add-link-btn',
                label: _('Add'),
                can_focus: true,
                x_align: Clutter.ActorAlign.END
            });

            addLinkBtn.connect('clicked', () => {
                const url = this._linkInput.get_text().trim();
                if (this._isValidUrl(url)) {
                    this._pinLink(url);
                    this._linkInput.set_text('');
                    this.menu.close();
                }
            });

            linkBox.add_child(this._linkInput);
            linkBox.add_child(addLinkBtn);
            linkSection.actor.add_child(linkBox);

            this._mainContainer.reactive = true;
            this._mainContainer.connect('button-press-event', (actor, event) => {
                if (event.get_button() === Clutter.BUTTON_PRIMARY) {
                    this.menu.toggle();
                    return Clutter.EVENT_STOP;
                }
                return Clutter.EVENT_PROPAGATE;
            });

            this.menu.actor.connect('button-press-event', (actor, event) => {
                const target = event.get_source();
                if (!this.menu.actor.contains(target)) {
                    this.menu.close();
                }
            });
        }

        _addPinnedIcon(appId) {

            if (appId.startsWith('link://')) {
                const url = appId.replace('link://', '');
                return this._addLinkIcon(url);
            }


            const app = Gio.DesktopAppInfo.new(appId + '.desktop') ||
                Gio.DesktopAppInfo.new(appId);

            if (!app) {
                const altPaths = [
                    GLib.build_filenamev([GLib.get_home_dir(), '.local', 'share', 'applications', `${appId}.desktop`]),
                    `/var/lib/flatpak/exports/share/applications/${appId}.desktop`,
                    `${GLib.get_home_dir()}/.local/share/flatpak/exports/share/applications/${appId}.desktop`
                ];

                for (const path of altPaths) {
                    if (GLib.file_test(path, GLib.FileTest.EXISTS)) {
                        const altApp = Gio.DesktopAppInfo.new_from_filename(path);
                        if (altApp) {
                            app = altApp;
                            break;
                        }
                    }
                }

                if (!app) {
                    console.error(`[CRITICAL] App not found in any location: ${appId}`);
                    return;
                }
            }

            const showLabels = this._settings.get_boolean('enable-labels');

            const iconBox = new St.BoxLayout({
                vertical: true,
                style_class: 'app-pinner-icon-box',
                y_expand: true
            });

            const iconContainer = new St.Bin({
                style_class: 'app-pinner-icon-container',
                x_align: Clutter.ActorAlign.CENTER,
                y_align: Clutter.ActorAlign.CENTER,
            });


            const iconButton = new St.Button({
                child: new St.Icon({
                    gicon: app.get_icon(),
                    icon_size: this._settings.get_int('icon-size')
                }),
                style_class: 'app-pinner-icon',
                reactive: true,
                hover: true,
                track_hover: true
            });

            const runningIndicator = new St.Widget({
                style_class: 'app-pinner-running-indicator',
                visible: false,
                reactive: false,
                x_align: Clutter.ActorAlign.END,
                y_align: Clutter.ActorAlign.START,
                x_expand: false,
                y_expand: false,
                translation_x: +9,
                translation_y: -9
            });

            this._updateIndicatorColor(runningIndicator);

            this._settings.connect('changed::indicator-color',
                () => this._updateIndicatorColor(runningIndicator));


            iconContainer.add_child(iconButton);
            iconContainer.add_child(runningIndicator);

            iconBox.runningIndicator = runningIndicator;

            let pressStartTime = 0;
            this._longPressTimeoutId = null;
            let isLongPress = false;

            iconButton.connect('button-press-event', (actor, event) => {

                if (this._longPressTimeoutId !== null) {
                    GLib.Source.remove(this._longPressTimeoutId);
                    this._timeoutIds.delete(this._longPressTimeoutId);
                    this._longPressTimeoutId = null;
                }

                pressStartTime = Date.now();
                isLongPress = false;

                this._longPressTimeoutId = this._addTimeout(500, () => {
                    isLongPress = true;
                    this._animateAndMoveToEnd(appId, iconBox);
                    this._longPressTimeoutId = null;
                    return GLib.SOURCE_REMOVE;
                });

                actor.ease({
                    scale_x: 0.8,
                    scale_y: 0.8,
                    duration: 200
                });
                return Clutter.EVENT_PROPAGATE;
            });

            iconButton.connect('button-release-event', (actor, event) => {
                if (this._longPressTimeoutId !== null) {
                    GLib.Source.remove(this._longPressTimeoutId);
                    this._timeoutIds.delete(this._longPressTimeoutId);
                    this._longPressTimeoutId = null;
                }

                actor.ease({
                    scale_x: 1.0,
                    scale_y: 1.0,
                    duration: 200
                });
                return Clutter.EVENT_PROPAGATE;
            });

            iconButton.connect('clicked', () => {
                if (!isLongPress) {
                    this._launchApp(appId);
                }
                isLongPress = false;
            });

            iconBox.add_child(iconContainer);

            if (showLabels) {
                iconBox.add_child(new St.Label({
                    text: app.get_name(),
                    style_class: 'app-pinner-label'
                }));
            }

            this._pinnedIconsBox.add_child(iconBox);
            iconBox.appId = appId;

            this._updateRunningIndicator(appId, runningIndicator);

            return iconBox;
        }

        _addLinkIcon(url) {
            const appId = `link://${url}`;
            const iconSize = this._settings.get_int('icon-size');
            const showLabels = this._settings.get_boolean('enable-labels');

            const iconBox = new St.BoxLayout({
                vertical: true,
                style_class: 'app-pinner-icon-box'
            });

            const iconButton = new St.Button({
                child: new St.Icon({
                    icon_name: 'emblem-web-symbolic',
                    icon_size: iconSize,
                    style_class: 'app-pinner-link-icon'
                }),
                style_class: 'app-pinner-icon',
                reactive: true,
                hover: true,
                track_hover: true
            });

            iconButton.connect('notify::hover', () => {
                iconButton.style = iconButton.hover
                    ? 'background: rgba(255,255,255,0.1); border-radius: 6px;'
                    : '';
            });

            let pressStartTime = 0;
            this._longPressTimeoutId = null;
            let isLongPress = false;

            iconButton.connect('button-press-event', (actor, event) => {

                pressStartTime = Date.now();
                isLongPress = false;

                this._longPressTimeoutId = this._addTimeout(500, () => {
                    isLongPress = true;
                    this._animateAndMoveToEnd(appId, iconBox);
                    return GLib.SOURCE_REMOVE;
                });

                actor.ease({
                    scale_x: 0.8,
                    scale_y: 0.8,
                    duration: 200
                });

                return Clutter.EVENT_PROPAGATE;
            });

            iconButton.connect('button-release-event', (actor, event) => {
                if (this._longPressTimeoutId !== null) {
                    GLib.Source.remove(this._longPressTimeoutId);
                    this._timeoutIds.delete(this._longPressTimeoutId);
                    this._longPressTimeoutId = null;
                }

                actor.ease({
                    scale_x: 1.0,
                    scale_y: 1.0,
                    duration: 200
                });

                return Clutter.EVENT_PROPAGATE;
            });

            iconButton.connect('clicked', () => {
                if (!isLongPress) {
                    Gio.AppInfo.launch_default_for_uri(url, null);
                }
                isLongPress = false;
            });

            const container = new St.Bin();
            container.add_child(iconButton);
            iconBox.add_child(container);

            if (showLabels) {
                const label = new St.Label({
                    text: this._shortenUrl(url),
                    style_class: 'app-pinner-label',
                    x_align: Clutter.ActorAlign.CENTER
                });
                iconBox.add_child(label);
            }

            iconBox.appId = appId;
            this._pinnedIconsBox.add_child(iconBox);

            return iconBox;
        }

        _checkVisibility() {
            if (this._destroyed) return;
            this._updateVisibility();
            this._addTimeout(2000, () => {
                this._checkVisibility();
                return GLib.SOURCE_REMOVE;
            });
        }

        // 4. Gestione interazioni utente
        _launchApp(appId) {

            if (appId.startsWith('link://')) {
                const url = appId.replace('link://', '');
                Gio.AppInfo.launch_default_for_uri(url, null);
                return;
            }
            try {
                const appSystem = Shell.AppSystem.get_default();
                let app = appSystem.lookup_app(`${appId}.desktop`) || appSystem.lookup_app(appId);

                if (!app) {
                    const desktopApp = Gio.DesktopAppInfo.new(`${appId}.desktop`);
                    if (desktopApp) {
                        app = new Shell.App({ desktop_app_info: desktopApp });
                    }
                    if (!app) {
                        console.error(`[ERROR] Application ${appId} not found`);
                        return;
                    }
                }

                this._pendingApps.add(appId);

                app.activate();

                this._forceImmediateUpdate(appId);

                this._addTimeout(1500, () => {
                    this._pendingApps.delete(appId);
                    this._updateRunningIndicators();
                    this._addTimeout(500, () => {
                        this._updateRunningIndicators();
                        return GLib.SOURCE_REMOVE;
                    });
                    return GLib.SOURCE_REMOVE;
                });

            } catch (e) {
                this._pendingApps.delete(appId);
            }

            this._pendingApps.add(appId);
            this._updateRunningIndicators();
        }

        _handleSleepSignal(sender, signalName, params) {
            const [isSleeping] = params.deepUnpack();
            if (!isSleeping) {
                this._addTimeout(1000, () => {
                    this._updateVisibility();
                    this._forceFullRefresh();
                    return GLib.SOURCE_REMOVE;
                });
            }
        }

        // 5. Gestione stato applicazioni
        _updateRunningIndicators() {
            const children = this._pinnedIconsBox.get_children();

            children.forEach(iconBox => {
                const appId = iconBox.appId;
                if (!appId || appId.startsWith('link://')) return;
                this._updateRunningIndicator(appId, iconBox.runningIndicator);
            });
        }

        _updateRunningIndicator(appId, indicator) {
            let isRunning = false;

            if (this._pendingApps.has(appId)) {
                isRunning = true;
            } else {
                const app = this._findAppById(appId);
                if (app) {
                    const state = app.get_state();
                    const hasWindows = app.get_windows().length > 0;
                    const isInAppSystem = this._appSystem.get_running().some(a => a.get_id() === app.get_id());

                    isRunning = state === Shell.AppState.RUNNING || hasWindows || isInAppSystem;

                }
            }

            if (indicator.visible !== isRunning) {
                indicator.visible = isRunning;

                if (isRunning) {
                    indicator.ease({
                        scale_x: 1.2,
                        scale_y: 1.2,
                        opacity: 200,
                        duration: 200,
                        mode: Clutter.AnimationMode.EASE_OUT_QUAD,
                        onComplete: () => {
                            indicator.ease({
                                scale_x: 1.0,
                                scale_y: 1.0,
                                opacity: 255,
                                duration: 300
                            });
                        }
                    });
                } else {
                    indicator.set_scale(1.0, 1.0);
                    indicator.opacity = 255;
                }
            }

            if (!isRunning && this._pendingApps.has(appId)) {
                this._pendingApps.delete(appId);
            }
        }

        // 6. Gestione impostazioni e aggiornamenti UI
        _refreshUI() {
            if (this._destroyed) return;

            const schema = this._settings.settings_schema;
            const key = schema.get_key('icon-size');
            const range = key.get_range();
            const [minSize, maxIconSize] = range.deep_unpack();

            let pinnedApps = this._settings.get_strv('pinned-apps');

            if (this._settings.get_boolean('sort-alphabetically') && pinnedApps.length > 0) {
                const sortedApps = [...pinnedApps].sort((a, b) => {
                    const nameA = this._getAppOrLinkName(a).toLowerCase();
                    const nameB = this._getAppOrLinkName(b).toLowerCase();
                    return nameA.localeCompare(nameB);
                });

                if (!arraysEqual(pinnedApps, sortedApps)) {
                    const handler = this._settings.connect('changed::pinned-apps', () => { });
                    this._settings.set_strv('pinned-apps', sortedApps);
                    this._settings.disconnect(handler);
                    return;
                }
            }

            const spacing = this._settings.get_int('spacing');
            const totalItems = pinnedApps.length;

            const containerWidth = (maxIconSize * totalItems) + (spacing * Math.max(0, totalItems - 1));

            this._mainContainer.set_style(`
                min-width: ${containerWidth}px;
                padding: 0 ${spacing}px;
            `);

            this._pinnedIconsBox.destroy_all_children();

            pinnedApps.forEach(appId => {
                const app = Gio.DesktopAppInfo.new(`${appId}.desktop`) || Gio.DesktopAppInfo.new(appId);
                if (app) {
                    this._addPinnedIcon(appId);
                } else if (appId.startsWith('link://')) {
                    this._addPinnedIcon(appId);
                }
            });

            this._pinnedSection.box.destroy_all_children();
            pinnedApps.forEach(appId => this._addMenuPinnedItem(appId));

            this._updatePinIconVisibility();
        }

        _updateIconsSpacing() {
            const spacing = Math.min(20, Math.max(0, this._settings.get_int('spacing')));
            this._pinnedIconsBox.set_style(`spacing: ${spacing}px;`);
        }

        // 7. Gestione elementi pinnati
        _pinApp(app) {

            const current = this._settings.get_strv('pinned-apps');
            if (current.length >= 10) {
                this._showMaxItemsError();
                return;
            }

            const rawAppId = app.get_id();
            const appId = rawAppId.replace(/\.desktop$/i, '');

            if (!current.includes(appId)) {
                const updated = [...current, appId];
                this._settings.set_strv('pinned-apps', updated);
            }
        }

        _unpinApp(appId) {
            if (appId.startsWith('link://')) {
                const url = appId.replace('link://', '');
                const links = this._settings.get_strv('custom-links')
                    .filter(l => l !== url);
                this._settings.set_strv('custom-links', links);
            }

            const startupApps = this._settings.get_strv('startup-apps')
                .filter(id => id !== appId);
            this._settings.set_strv('startup-apps', startupApps);

            const pinnedApps = this._settings.get_strv('pinned-apps')
                .filter(id => id !== appId);
            this._settings.set_strv('pinned-apps', pinnedApps);
        }

        _isPinned(appId) {
            const cleanAppId = this._sanitizeAppId(appId);
            return this._settings.get_strv('pinned-apps').includes(cleanAppId);
        }

        // 8. Funzioni helper e utilità
        _isValidUrl(url) {
            try {
                new URL(url);
                return true;
            } catch {
                return url.startsWith('http') || url.startsWith('ftp');
            }
        }

        _sortApps(a, b, query) {
            const aName = a.get_name().toLowerCase();
            const bName = b.get_name().toLowerCase();

            if (aName === query) return -1;
            if (bName === query) return 1;

            const aStart = aName.startsWith(query);
            const bStart = bName.startsWith(query);
            if (aStart !== bStart) return aStart ? -1 : 1;

            return aName.length - bName.length;
        }

        _handleWakeupEvent() {
            this._addTimeout(2000, () => {
                this._updateRunningIndicators();
                this._refreshUI();
                return GLib.SOURCE_REMOVE;
            });
        }

        _pinLink(url) {

            const pinnedApps = this._settings.get_strv('pinned-apps');
            if (pinnedApps.length >= 10) {
                this._showMaxItemsError();
                return;
            }

            if (!url) return;
            if (!url.includes('://')) url = `http://${url}`;

            const links = this._settings.get_strv('custom-links');

            if (!links.includes(url)) {
                const newLinks = [...links, url];
                const newPinnedApps = [...pinnedApps, `link://${url}`];

                this._settings.set_strv('custom-links', newLinks);
                this._settings.set_strv('pinned-apps', newPinnedApps);
                this._refreshUI();
            }
        }

        async _updateSearch() {
            this._resultsSection.box.destroy_all_children();
            const query = this._searchInput.get_text().trim().toLowerCase();
            if (!query) return;

            const iconSize = this._settings.get_int('icon-size');
            const appSys = Shell.AppSystem.get_default();

            const results = appSys.get_installed()
                .filter(app => {
                    const appId = this._sanitizeAppId(app.get_id());
                    return !this._isPinned(appId) &&
                        this._matchQuery(query, app.get_name().toLowerCase());
                })
                .sort((a, b) => this._sortApps(a, b, query))
                .slice(0, 10);

            if (results.length === 0) {
                this._resultsSection.box.add_child(
                    new PopupMenu.PopupMenuItem(_('No results found'))
                );
                return;
            }

            results.forEach(app => {
                const item = new PopupMenu.PopupMenuItem(app.get_name());
                const icon = new St.Icon({
                    gicon: app.get_icon(),
                    icon_size: iconSize
                });
                item.insert_child_at_index(icon, 0);

                if (this._settings.get_strv('pinned-apps').length >= 10) {
                    item.setSensitive(false);
                    item.label.text = _("Max items reached - unpin something first");
                }

                item.connect('activate', () => {
                    this._pinApp(app);

                    this._searchInput.set_text('');
                    this._updateSearch();

                    this.menu.close();

                });

                this._resultsSection.box.add_child(item);
            });

            this.menu.actor.show_all();
            this.menu.actor.queue_redraw();

        }

        _sanitizeAppId(appId) {
            const sanitized = appId
                .replace(/\.desktop$/i, '')
                .replace(/^application:\/\//i, '');
            return sanitized;
        }

        _matchQuery(query, appName) {
            const keywords = appName.split(/[\s-]/);
            return appName.includes(query) ||
                keywords.some(k => k.startsWith(query)) ||
                this._fuzzyMatch(query, appName);
        }

        _fuzzyMatch(pattern, str) {
            let patternIndex = 0;
            for (const char of str.toLowerCase()) {
                if (char === pattern[patternIndex]) {
                    if (++patternIndex === pattern.length) return true;
                }
            }
            return false;
        }

        _getIndicatorForApp(appId) {
            const iconBox = this._pinnedIconsBox.get_children().find(b => b.appId === appId);
            return iconBox ? iconBox.runningIndicator : null;
        }

        _forceImmediateUpdate(appId) {
            const indicator = this._getIndicatorForApp(appId);
            if (indicator) {
                indicator.visible = true;
                indicator.opacity = 0;
                indicator.set_scale(0.5, 0.5);
                indicator.ease({
                    opacity: 255,
                    scale_x: 1,
                    scale_y: 1,
                    duration: 300,
                    mode: Clutter.AnimationMode.EASE_OUT_QUAD
                });
            }
            this._updateRunningIndicators();
        }

        _shortenUrl(url) {
            try {
                const parsed = new URL(url);
                return parsed.hostname.replace(/^www\./i, '');
            } catch {
                return url.substring(0, 15) + '...';
            }
        }

        _forceFullRefresh() {
            this._runningTracker = new Set(
                this._appSystem.get_running().map(app => this._sanitizeAppId(app.get_id()))
            );

            this._pinnedIconsBox.get_children().forEach(iconBox => {
                this._updateRunningIndicator(iconBox.appId, iconBox.runningIndicator);
            });

            this._addTimeout(1000, () => {
                this._appSystem = Shell.AppSystem.get_default();
                this._updateRunningIndicators();
                return GLib.SOURCE_REMOVE;
            });
        }

        _findAppById(appId) {
            let app = this._appSystem.lookup_app(appId) ||
                this._appSystem.lookup_app(`${appId}.desktop`);

            if (!app) {
                const gioApp = Gio.DesktopAppInfo.new(`${appId}.desktop`) ||
                    Gio.DesktopAppInfo.new(appId);

                if (gioApp) {
                    app = this._appSystem.lookup_app(gioApp.get_id());
                }
            }

            return app;
        }

        _refreshUIWithEffect() {
            this._pinnedIconsBox.get_children().forEach((child, index) => {
                child.ease({
                    opacity: 0,
                    scale_x: 0.5,
                    scale_y: 0.5,
                    duration: 300,
                    delay: index * 30,
                    onComplete: () => child.destroy()
                });
            });

            this._addTimeout(300, () => {
                this._settings.get_strv('pinned-apps').forEach(appId =>
                    this._addPinnedIcon(appId)
                );
                return GLib.SOURCE_REMOVE;
            });
        }

        _animateAndMoveToEnd(appId, iconBox) {
            const currentApps = this._settings.get_strv('pinned-apps');
            const currentIndex = currentApps.indexOf(appId);

            if (currentIndex === currentApps.length - 1) {
                iconBox.ease({
                    scale_x: 1.2,
                    scale_y: 1.2,
                    duration: 200,
                    mode: Clutter.AnimationMode.EASE_OUT_QUAD,
                    onComplete: () => {
                        iconBox.ease({
                            scale_x: 1.0,
                            scale_y: 1.0,
                            duration: 300,
                            mode: Clutter.AnimationMode.EASE_OUT_BOUNCE
                        });
                    }
                });
                return;
            }

            iconBox.ease({
                scale_x: 1.2,
                scale_y: 1.2,
                opacity: 0,
                duration: 300,
                mode: Clutter.AnimationMode.EASE_OUT_QUAD,
                onComplete: () => {
                    const updatedApps = this._settings.get_strv('pinned-apps');
                    const newIndex = updatedApps.indexOf(appId);

                    if (newIndex === updatedApps.length - 1) return;

                    const newOrder = [
                        ...updatedApps.slice(0, newIndex),
                        ...updatedApps.slice(newIndex + 1),
                        updatedApps[newIndex]
                    ];

                    this._settings.set_strv('pinned-apps', newOrder);
                }
            });
        }

        _updateIndicatorColor(indicator) {
            const color = this._settings.get_string('indicator-color');
            indicator.set_style(`background-color: ${color};`);
        }

        _getUrlName(url) {
            try {
                if (!url.match(/^[a-zA-Z]+:\/\//)) {
                    url = 'http://' + url;
                }

                const parsed = new URL(url);
                let hostname = parsed.hostname;

                hostname = hostname.replace(/^www\./i, '');

                const specialCases = {
                    'youtube.com': 'YouTube',
                    'google.com': 'Google',
                    'github.com': 'GitHub',
                };

                return specialCases[hostname.toLowerCase()] ||
                    hostname.split('.')[0].charAt(0).toUpperCase() +
                    hostname.split('.')[0].slice(1);
            } catch (e) {
                return url.replace(/^https?:\/\//, '')
                    .replace(/^www\./i, '')
                    .split('/')[0];
            }
        }

        _getAppOrLinkName(appId) {
            if (appId.startsWith('link://')) {
                return this._getUrlName(appId.replace('link://', ''));
            } else {
                const app = Gio.DesktopAppInfo.new(`${appId}.desktop`) || Gio.DesktopAppInfo.new(appId);
                return app ? app.get_name() : appId;
            }
        }


        _addMenuPinnedItem(appId) {
            if (appId.startsWith('link://')) {
                const url = appId.replace('link://', '');
                const item = new PopupMenu.PopupMenuItem(url);

                const icon = new St.Icon({
                    icon_name: 'emblem-web-symbolic',
                    icon_size: this._settings.get_int('icon-size'),
                    style_class: 'app-pinner-link-icon'
                });
                item.insert_child_at_index(icon, 0);

                const spacer = new St.BoxLayout({ x_expand: true });
                item.actor.add_child(spacer);

                const removeBtn = new St.Button({
                    child: new St.Label({ text: '×' }),
                    style_class: 'app-pinner-remove-btn'
                });

                item.connect('activate', () => {
                    Gio.AppInfo.launch_default_for_uri(url, null);
                    this.menu.close();
                });

                removeBtn.connect('clicked', () => {
                    this._unpinApp(`link://${url}`);
                });

                item.add_child(removeBtn);
                this._pinnedSection.box.add_child(item);
            } else {
                const app = Gio.DesktopAppInfo.new(`${appId}.desktop`);
                if (!app) {
                    console.error(`Applicazione non trovata nel menu: ${appId}`);
                    return;
                }

                const item = new PopupMenu.PopupMenuItem(app.get_name());
                const icon = new St.Icon({
                    gicon: app.get_icon(),
                    icon_size: this._settings.get_int('icon-size')
                });
                item.insert_child_at_index(icon, 0);

                const spacer = new St.BoxLayout({ x_expand: true });
                item.actor.add_child(spacer);

                const removeBtn = new St.Button({
                    child: new St.Label({
                        text: '×',
                        style_class: 'app-pinner-remove-label'
                    }),
                    style_class: 'app-pinner-remove-btn'
                });

                removeBtn.connect('button-press-event', (actor, event) => {
                    this._unpinApp(appId);
                    this.menu.close();
                    this.menu.open();
                    return Clutter.EVENT_STOP;
                });

                item.connect('activate', () => {
                    this._launchApp(appId);
                    this.menu.close();
                });

                item.add_child(removeBtn);
                this._pinnedSection.box.add_child(item);
            }
        }
    });

function arraysEqual(a, b) {
    return a.length === b.length && a.every((v, i) => v === b[i]);
}

export default class AppPinnerExtension extends Extension {

    constructor(metadata) {
        super(metadata);
        this._timeoutIds = new Set();
        this._settingsHandler = [];
    }

    _addTimeout(interval, callback) {
        const sourceId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, interval, () => {
            const result = callback();
            this._timeoutIds.delete(sourceId);
            return result;
        });
        this._timeoutIds.add(sourceId);
        return sourceId;
    }

    _dbusImpl = null;

    // 1. Lifecycle dell'estensione
    enable() {

        this._settingsHandler = [];

        this._settings = this.getSettings();

        this._validateSettings();

        this._settingsHandler.push(
            this._settings.connect('changed::show-in-panel', () => {
                if (this._indicator) {
                    this._indicator._updateVisibility();
                }
            }),
            this._settings.connect('changed::show-pin-icon', () => {
                if (this._indicator) {
                    this._indicator._updatePinIconVisibility();
                }
            })
        );

        this._sessionConnection = Gio.DBus.session;
        this._sessionWatcher = this._sessionConnection.watch_name(
            'org.gnome.Shell',
            Gio.BusNameWatcherFlags.NONE,
            () => { },
            () => this.disable()
        );

        const dbusInterface = `
        <node>
            <interface name="org.gnome.Shell.Extensions.AppPinner">
                <method name="LaunchPosition">
                    <arg type="u" name="position" direction="in"/>
                </method>
            </interface>
        </node>`;

        if (!this._dbusImpl) {
            try {
                this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(dbusInterface, this);
                this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell/Extensions/AppPinner');
            } catch (e) {
                console.error('Errore registrazione D-Bus:', e.message);
            }
        }

        if (!this._settings.settings_schema.get_key('show-in-panel')) {
            this._settings.set_boolean('show-in-panel', true);
        }

        this._positionHandler = this._settings.connect(
            'changed::position-in-panel',
            () => this._safeRecreateIndicator()
        );

        this._keybindings = [];
        for (let i = 1; i <= 10; i++) {
            const shortcut = this._settings.get_string(`shortcut-${i}`);
            if (shortcut) {
                this._addKeybinding(i);
            }
        }

        this._shortcutHandlers = [];
        for (let i = 1; i <= 10; i++) {
            const handlerId = this._settings.connect(`changed::shortcut-${i}`, () => this._updateKeybinding(i));
            this._shortcutHandlers.push(handlerId);
        }

        this._safeRecreateIndicator();

        this._settings.connect('changed::startup-apps', () => this._syncAutostart());
        this._syncAutostart();

        this._startupChangedId = this._settings.connect(
            'changed::startup-apps',
            () => {
                this._addTimeout(500, () => {
                    console.log("[DEBUG] Startup apps changed!");
                    this._syncAutostart();
                    return GLib.SOURCE_REMOVE;
                });
            }
        );

        this._settings.connect('changed::pinned-apps', () => {
            this._cleanOrphanedStartupApps();
            this._syncAutostart();
        });

        this._settings.connect('changed::startup-apps', () => this._syncAutostart());

        this._cleanOrphanedStartupApps();
        this._syncAutostart();

        this._settings.connect('changed::pinned-apps', () => {
            this._cleanOrphanedStartupApps();
            this._syncAutostart();
        });

        this._addTimeout(2000, () => {
            this._syncAutostart();
            return GLib.SOURCE_REMOVE;
        });

        this._indicator._checkVisibility();

    }

    disable() {

        if (this._sessionWatcherId) {
            this._sessionConnection.unwatch_name(this._sessionWatcherId);
            this._sessionWatcherId = null;
        }

        if (this._dbusImpl) {
            try {
                this._dbusImpl.unexport();
            } catch (e) {
                console.error('DBus unexport error:', e);
            }
            this._dbusImpl = null;
        }


        for (let i = 1; i <= 10; i++) {
            this._removeKeybinding(i);
        }

        const mediaKeysSettings = new Gio.Settings({
            schema_id: 'org.gnome.settings-daemon.plugins.media-keys'
        });

        const currentPaths = mediaKeysSettings.get_strv('custom-keybindings');
        const newPaths = currentPaths.filter(p => !p.includes('app-pinner-'));
        mediaKeysSettings.set_strv('custom-keybindings', newPaths);

        if (this._positionHandler) {
            this._settings.disconnect(this._positionHandler);
            this._positionHandler = null;
        }

        this._keybindings.forEach(key => Main.wm.removeKeybinding(key));
        this._keybindings = [];

        if (this._indicator) {
            const parent = this._indicator.get_parent();
            if (parent) {
                parent.remove_child(this._indicator);
            }
            this._indicator.destroy();
            this._indicator = null;
        }

        if (this._timeoutIds) {
            this._timeoutIds.forEach(id => GLib.Source.remove(id));
            this._timeoutIds.clear();
        }

        this._settings.disconnect(this._shortcutHandler);

        const autostartDir = GLib.build_filenamev([GLib.get_user_config_dir(), 'autostart']);
        const dir = Gio.File.new_for_path(autostartDir);

        const enumerator = dir.enumerate_children('standard::name', Gio.FileQueryInfoFlags.NONE, null);
        let fileInfo;
        while ((fileInfo = enumerator.next_file(null)) !== null) {
            const name = fileInfo.get_name();
            if (name.startsWith('app-pinner-')) {
                GLib.unlink(`${autostartDir}/${name}`);
            }
        }

        if (this._startupChangedId) {
            this._settings.disconnect(this._startupChangedId);
        }

        try {
            const enumerator = dir.enumerate_children('standard::name', Gio.FileQueryInfoFlags.NONE, null);
            let fileInfo;
            while ((fileInfo = enumerator.next_file(null)) !== null) {
                const name = fileInfo.get_name();
                if (name.startsWith('app-pinner-')) {
                    GLib.unlink(`${autostartDir}/${name}`);
                }
            }
        } catch (e) {
            console.error('Errore pulizia autostart:', e);
        }

        if (this._settingsHandler) {
            this._settingsHandler.forEach(handler => {
                if (handler) {
                    this._settings.disconnect(handler);
                }
            });
            this._settingsHandler = null;
        }

        if (this._indicator && this._indicator._checkVisibility) {
            GLib.Source.remove(this._indicator._checkVisibilityTimeout);
        }

        this._settings = null
    }

    _updateKeybinding(position) {
        const shortcut = this._settings.get_string(`shortcut-${position}`);
        if (shortcut) {
            this._addKeybinding(position);
        } else {
            this._removeKeybinding(position);
        }
    }

    // 2. Gestione D-Bus e keybindings
    _addKeybinding(position) {
        const key = `shortcut-${position}`;
        const shortcut = this._settings.get_string(key);

        if (!shortcut) return;

        const customPath = `/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/app-pinner-${position}/`;

        const customSettings = new Gio.Settings({
            schema_id: 'org.gnome.settings-daemon.plugins.media-keys.custom-keybinding',
            path: customPath
        });

        customSettings.set_string('name', `App Pinner Position ${position}`);
        customSettings.set_string('command', `dbus-send --session --type=method_call --dest=org.gnome.Shell /org/gnome/Shell/Extensions/AppPinner org.gnome.Shell.Extensions.AppPinner.LaunchPosition uint32:${position}`);
        customSettings.set_string('binding', shortcut);

        const mediaKeysSettings = new Gio.Settings({
            schema_id: 'org.gnome.settings-daemon.plugins.media-keys'
        });

        const currentPaths = mediaKeysSettings.get_strv('custom-keybindings');
        if (!currentPaths.includes(customPath)) {
            mediaKeysSettings.set_strv('custom-keybindings', [...currentPaths, customPath]);
        }
    }

    LaunchPosition(position) {
        this._launchAppByPosition(position);
    }

    _launchAppByPosition(position) {

        const apps = this._settings.get_strv('pinned-apps');

        if (apps.length === 0) return;

        const index = position - 1;
        if (index < 0 || index >= apps.length) return;

        const appId = apps[index];

        if (appId.startsWith('link://')) {
            if (!this._indicator) return;

            this._indicator._launchApp(appId);
            return;
        }

        const appSys = Shell.AppSystem.get_default();
        let app = appSys.lookup_app(`${appId}.desktop`) || appSys.lookup_app(appId);

        if (!app) {
            const gioApp = Gio.DesktopAppInfo.new(`${appId}.desktop`) || Gio.DesktopAppInfo.new(appId);

            if (!gioApp) {
                console.error(`[ERROR] App ${appId} non trovata in nessun formato`);
                return;
            }
        } else {
        }

        this._indicator._launchApp(appId);
    }

    // 3. Gestione autostart
    _syncAutostart() {
        const autostartDir = GLib.build_filenamev([GLib.get_user_config_dir(), 'autostart']);
        const startupApps = this._settings.get_strv('startup-apps');

        if (!GLib.file_test(autostartDir, GLib.FileTest.IS_DIR)) {
            GLib.mkdir_with_parents(autostartDir, 0o755);
        }

        const validFiles = new Map();

        startupApps.forEach(entry => {
            if (entry.startsWith('link://')) {
                const url = entry.replace('link://', '');
                const sanitized = this._sanitizeForFilename(url);
                const fileName = `app-pinner-link-${sanitized}.desktop`;
                validFiles.set(fileName, true);

                const content = [
                    '[Desktop Entry]',
                    'Type=Application',
                    `Name=Link ${url}`,
                    `Exec=xdg-open "${url}"`,
                    'Icon=emblem-web-symbolic',
                    'X-GNOME-Autostart-enabled=true',
                    'X-GNOME-Autostart-Delay=2',
                    'NoDisplay=false',
                    ''
                ].join('\n');

                try {
                    GLib.file_set_contents(`${autostartDir}/${fileName}`, content);
                    GLib.chmod(`${autostartDir}/${fileName}`, 0o644);
                } catch (e) {
                    console.error(`Errore scrittura link ${url}:`, e.message);
                }
            } else {
                const appInfo = this._getAppInfo(entry);
                if (!appInfo) {
                    console.error(`[ERROR] App non trovata: ${entry}`);
                    return;
                }

                const desktopId = appInfo.get_id() || `${entry}.desktop`;
                const sanitizedId = this._sanitizeForFilename(desktopId);
                const fileName = `app-pinner-${sanitizedId}.desktop`;
                validFiles.set(fileName, true);

                const execCommand = appInfo.get_string('Exec');
                if (!execCommand) {
                    console.error(`[ERROR] Comando non trovato per ${entry}`);
                    return;
                }

                const desktopContent = [
                    '[Desktop Entry]',
                    'Type=Application',
                    `Name=${appInfo.get_name()} (App Pinner)`,
                    `Exec=${execCommand}`,
                    'X-GNOME-Autostart-enabled=true',
                    'X-GNOME-Autostart-Delay=2',
                    'Icon=' + (appInfo.get_icon() || ''),
                    'Comment=Avviato automaticamente da App Pinner',
                    'NoDisplay=false',
                    ''
                ].join('\n');

                try {
                    GLib.file_set_contents(`${autostartDir}/${fileName}`, desktopContent);
                    GLib.chmod(`${autostartDir}/${fileName}`, 0o644);
                } catch (e) {
                    console.error(`Errore scrittura app ${entry}:`, e.message);
                }
            }
        });

        const dir = Gio.File.new_for_path(autostartDir);
        try {
            const enumerator = dir.enumerate_children('standard::name', Gio.FileQueryInfoFlags.NONE, null);
            let fileInfo;
            while ((fileInfo = enumerator.next_file(null))) {
                const fileName = fileInfo.get_name();
                if (fileName.startsWith('app-pinner-') && !validFiles.has(fileName)) {
                    GLib.unlink(`${autostartDir}/${fileName}`);
                }
            }
        } catch (e) {
            console.error('Errore durante la pulizia dei file:', e.message);
        }
    }

    // 4. Funzioni helper e validazione
    _validateSettings() {
        const currentPos = this._settings.get_string('position-in-panel');
        if (!['left', 'right'].includes(currentPos)) {
            this._settings.set_string('position-in-panel', 'right');
        }
    }

    _getAppInfo(appId) {
        console.log(`[DEBUG] Getting app info for: ${appId}`);

        const appSys = Shell.AppSystem.get_default();
        const allApps = appSys.get_installed();
        const foundApp = allApps.find(app =>
            app.get_id().toLowerCase() === appId.toLowerCase()
        );

        if (foundApp) {
            return Gio.DesktopAppInfo.new(foundApp.get_id());
        }

        const flatpakPaths = [
            '/var/lib/flatpak/exports/share/applications/',
            `${GLib.get_home_dir()}/.local/share/flatpak/exports/share/applications/`
        ];

        for (const path of flatpakPaths) {
            const fullPath = `${path}${appId}.desktop`;
            if (GLib.file_test(fullPath, GLib.FileTest.EXISTS)) {
                return Gio.DesktopAppInfo.new_from_filename(fullPath);
            }
        }

        const dataDirs = GLib.get_system_data_dirs();
        for (const dataDir of dataDirs) {
            const appPath = `${dataDir}/applications/${appId}.desktop`;
            if (GLib.file_test(appPath, GLib.FileTest.EXISTS)) {
                return Gio.DesktopAppInfo.new_from_filename(appPath);
            }
        }

        return null;
    }

    _sanitizeForFilename(appId) {
        return appId
            .replace(/\.desktop$/i, '')
            .replace(/[^a-zA-Z0-9-]/g, '_')
            .replace(/_+/g, '_')
            .substring(0, 50);
    }

    _cleanOrphanedStartupApps() {
        const pinnedApps = this._settings.get_strv('pinned-apps');
        const customLinks = this._settings.get_strv('custom-links');
        const startupApps = this._settings.get_strv('startup-apps');

        const validEntries = new Set([
            ...pinnedApps,
            ...customLinks.map(url => `link://${url}`)
        ]);

        const cleaned = startupApps.filter(appId =>
            validEntries.has(appId) ||
            customLinks.includes(appId.replace('link://', ''))
        );

        if (!arraysEqual(cleaned, startupApps)) {
            this._settings.set_strv('startup-apps', cleaned);
        }
    }

    _removeKeybinding(position) {
        const customPath = `/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/app-pinner-${position}/`;

        const mediaKeysSettings = new Gio.Settings({
            schema_id: 'org.gnome.settings-daemon.plugins.media-keys'
        });

        const currentPaths = mediaKeysSettings.get_strv('custom-keybindings');
        if (currentPaths.includes(customPath)) {
            const newPaths = currentPaths.filter(p => p !== customPath);
            mediaKeysSettings.set_strv('custom-keybindings', newPaths);

            const customSettings = new Gio.Settings({
                schema_id: 'org.gnome.settings-daemon.plugins.media-keys.custom-keybinding',
                path: customPath
            });
            customSettings.reset('name');
            customSettings.reset('command');
            customSettings.reset('binding');
        }
    }

    _showMaxItemsError() {
        Main.notifyError(
            _("Maximum items reached"),
            _("You can pin up to 10 items maximum")
        );
    }

    _validateAccelerator(accelerator) {
        if (!accelerator) return false;

        try {
            const [key, mods] = Clutter.accelerator_parse(accelerator);
            return key !== 0;
        } catch (e) {
            return false;
        }
    }

    _safeRecreateIndicator() {
        const iconSize = this._settings.get_int('icon-size');
        const spacing = this._settings.get_int('spacing');
        const labels = this._settings.get_boolean('enable-labels');

        this._recreateIndicator();

        this._settings.set_int('icon-size', iconSize);
        this._settings.set_int('spacing', spacing);
        this._settings.set_boolean('enable-labels', labels);
    }

    _recreateIndicator() {
        if (this._indicator) {
            this._indicator.destroy();
            this._indicator = null;
        }

        this._indicator = new AppPinner(this._settings);
        const position = this._settings.get_string('position-in-panel');

        if (this._indicator.get_parent()) {
            this._indicator.get_parent().remove_child(this._indicator);
        }

        switch (position) {
            case 'left':
                Main.panel._leftBox.insert_child_at_index(this._indicator, 0);
                break;

            case 'center':
                if (Main.panel._centerBox) {
                    Main.panel._centerBox.add_child(this._indicator);
                } else {
                    Main.panel._centerBox = new St.BoxLayout();
                    Main.panel._centerBox.x_align = Clutter.ActorAlign.CENTER;
                    Main.panel.insert_child_at_index(Main.panel._centerBox, 1);
                    Main.panel._centerBox.add_child(this._indicator);
                }
                break;

            case 'right':
                const dateMenu = Main.panel.statusArea?.dateMenu;
                let targetIndex = 0;
                if (dateMenu?.actor) {
                    const children = Main.panel._rightBox.get_children();
                    const dateMenuIndex = children.indexOf(dateMenu.actor);
                    targetIndex = dateMenuIndex !== -1 ? dateMenuIndex + 1 : 0;
                }
                Main.panel._rightBox.insert_child_at_index(this._indicator, targetIndex);
                break;
        }

        this._indicator.set_style_class_name(
            `app-pinner-position-${position}`
        );

        Main.panel.queue_relayout();
        Main.panel.menuManager.addMenu(this._indicator.menu);
    }
}