// SPDX-FileCopyrightText: GSConnect Developers https://github.com/GSConnect
//
// SPDX-License-Identifier: GPL-2.0-or-later

import GLib from 'gi://GLib';
import Gio from 'gi://Gio';
import GObject from 'gi://GObject';
import Gtk from 'gi://Gtk';

import system from 'system';

import Config from '../../config.js';


/*
 * Issue Header
 */
const ISSUE_HEADER = `
GSConnect: ${Config.PACKAGE_VERSION} (${Config.IS_USER ? 'user' : 'system'})
GJS:       ${system.version}
Session:   ${GLib.getenv('XDG_SESSION_TYPE')}
OS:        ${GLib.get_os_info('PRETTY_NAME')}
`;


/**
 * A dialog for selecting a device
 */
export const DeviceChooser = GObject.registerClass({
    GTypeName: 'GSConnectServiceDeviceChooser',
    Properties: {
        'action-name': GObject.ParamSpec.string(
            'action-name',
            'Action Name',
            'The name of the associated action, like "sendFile"',
            GObject.ParamFlags.READWRITE,
            null
        ),
        'action-target': GObject.param_spec_variant(
            'action-target',
            'Action Target',
            'The parameter for action invocations',
            new GLib.VariantType('*'),
            null,
            GObject.ParamFlags.READWRITE
        ),
    },
    Template: 'resource:///org/gnome/Shell/Extensions/GSConnect/ui/service-device-chooser.ui',
    Children: ['device-list', 'cancel-button', 'select-button'],
}, class DeviceChooser extends Gtk.Dialog {

    _init(params = {}) {
        super._init({
            use_header_bar: true,
            application: Gio.Application.get_default(),
        });
        this.set_keep_above(true);

        // HeaderBar
        this.get_header_bar().subtitle = params.title;

        // Dialog Action
        this.action_name = params.action_name;
        this.action_target = params.action_target;

        // Device List
        this.device_list.set_sort_func(this._sortDevices);

        this._devicesChangedId = this.application.settings.connect(
            'changed::devices',
            this._onDevicesChanged.bind(this)
        );
        this._onDevicesChanged();
    }

    vfunc_response(response_id) {
        if (response_id === Gtk.ResponseType.OK) {
            try {
                const device = this.device_list.get_selected_row().device;
                device.activate_action(this.action_name, this.action_target);
            } catch (e) {
                logError(e);
            }
        }

        this.destroy();
    }

    get action_name() {
        if (this._action_name === undefined)
            this._action_name = null;

        return this._action_name;
    }

    set action_name(name) {
        this._action_name = name;
    }

    get action_target() {
        if (this._action_target === undefined)
            this._action_target = null;

        return this._action_target;
    }

    set action_target(variant) {
        this._action_target = variant;
    }

    _onDeviceActivated(box, row) {
        this.response(Gtk.ResponseType.OK);
    }

    _onDeviceSelected(box) {
        this.set_response_sensitive(
            Gtk.ResponseType.OK,
            (box.get_selected_row())
        );
    }

    _onDevicesChanged() {
        // Collect known devices
        const devices = {};

        for (const [id, device] of this.application.manager.devices.entries())
            devices[id] = device;

        // Prune device rows
        this.device_list.foreach(row => {
            if (!devices.hasOwnProperty(row.name))
                row.destroy();
            else
                delete devices[row.name];
        });

        // Add new devices
        for (const device of Object.values(devices)) {
            const action = device.lookup_action(this.action_name);

            if (action === null)
                continue;

            const row = new Gtk.ListBoxRow({
                visible: action.enabled,
            });
            row.set_name(device.id);
            row.device = device;

            action.bind_property(
                'enabled',
                row,
                'visible',
                Gio.SettingsBindFlags.DEFAULT
            );

            const grid = new Gtk.Grid({
                column_spacing: 12,
                margin: 6,
                visible: true,
            });
            row.add(grid);

            const icon = new Gtk.Image({
                icon_name: device.icon_name,
                pixel_size: 32,
                visible: true,
            });
            grid.attach(icon, 0, 0, 1, 1);

            const name = new Gtk.Label({
                label: device.name,
                halign: Gtk.Align.START,
                hexpand: true,
                visible: true,
            });
            grid.attach(name, 1, 0, 1, 1);

            this.device_list.add(row);
        }

        if (this.device_list.get_selected_row() === null)
            this.device_list.select_row(this.device_list.get_row_at_index(0));
    }

    _sortDevices(row1, row2) {
        return row1.device.name.localeCompare(row2.device.name);
    }
});


/**
 * A dialog for reporting an error.
 */
export const ErrorDialog = GObject.registerClass({
    GTypeName: 'GSConnectServiceErrorDialog',
    Template: 'resource:///org/gnome/Shell/Extensions/GSConnect/ui/service-error-dialog.ui',
    Children: [
        'error-stack',
        'expander-arrow',
        'gesture',
        'report-button',
        'revealer',
    ],
}, class ErrorDialog extends Gtk.Window {

    _init(error) {
        super._init({
            application: Gio.Application.get_default(),
            title: `GSConnect: ${error.name}`,
        });
        this.set_keep_above(true);

        this.error = error;
        this.error_stack.buffer.text = `${error.message}\n\n${error.stack}`;
        this.gesture.connect('released', this._onReleased.bind(this));
    }

    _onClicked(button) {
        if (this.report_button === button) {
            const uri = this._buildUri(this.error.message, this.error.stack);
            Gio.AppInfo.launch_default_for_uri_async(uri, null, null, null);
        }

        this.destroy();
    }

    _onReleased(gesture, n_press) {
        if (n_press === 1)
            this.revealer.reveal_child = !this.revealer.reveal_child;
    }

    _onRevealChild(revealer, pspec) {
        this.expander_arrow.icon_name = this.revealer.reveal_child
            ? 'pan-down-symbolic'
            : 'pan-end-symbolic';
    }

    _buildUri(message, stack) {
        const body = `\`\`\`${ISSUE_HEADER}\n${stack}\n\`\`\``;
        const titleQuery = encodeURIComponent(message).replace('%20', '+');
        const bodyQuery = encodeURIComponent(body).replace('%20', '+');
        const uri = `${Config.PACKAGE_BUGREPORT}?title=${titleQuery}&body=${bodyQuery}`;

        // Reasonable URI length limit
        if (uri.length > 2000)
            return uri.substr(0, 2000);

        return uri;
    }
});

