import Gio from 'gi://Gio';
import * as Resources from '../helpers/resource_helpers.js';
import * as Log from './log.js';
import { PanelHelper } from '../helpers/panel_helper.js';
import { LabelHelper } from '../helpers/label_helper.js';

export class DBus {
    labels;
    gpuModeToggle;
    proxy;
    sgfxVendor = '';
    sgfxLastState = 6;
    sgfxPowerState = 5;
    sgfxSupportedGPUs = [];
    connected = false;

    constructor(gpuModeToggle) {
        this.gpuModeToggle = gpuModeToggle;
        this.proxy = new Gio.DBusProxy.makeProxyWrapper(Resources.File.DBus('org-supergfxctl-gfx-5', this.gpuModeToggle._exPath))(Gio.DBus.system, 'org.supergfxctl.Daemon', '/org/supergfxctl/Gfx');
    }
    async initialize() {

        try {
            await this.initLabels();
            if (this.labels.v51 || this.labels.v52)
                this.sgfxLastState = 7;
            const sup = await this.proxy.SupportedAsync();
            if (Array.isArray(sup) && Array.isArray(sup[0])) {
                this.sgfxSupportedGPUs = sup[0].map((n) => parseInt(n) || 0);
                this.connected = sup[0].length === this.sgfxSupportedGPUs.length;
            }
            if (this.connected) {
                await this.vendor();
                await this.gfxMode();
                await this.gpuPower();
                this.connectNotifySignal();
                this.connectPowerSignal();

                try {
                    const lastState = this.lastState;
                    const lastPState = this.lastPowerState;
                    const lVar = `${this.labels.get(1, lastState)} (${this.labels.get(2, lastPState)}})`;
                    const gpuIcon = this.labels.getGpuIcon(0, lastState, 3, lastPState);
                    if (this.labels.gsVersion >= 44)
                        this.gpuModeToggle.title = lVar;
                    else
                        this.gpuModeToggle.label = lVar;
                    this.gpuModeToggle.gicon = gpuIcon;
                    this.gpuModeToggle.menu.setHeader(gpuIcon, `${this.labels.get(1, lastState)} (${this.labels.get(2, lastPState)})`);
                }
                catch {
                    PanelHelper.notify('DBus: Could not establish connection to supergfxctl!', 'gpu-integrated-active', 4, 3);
                    Log.error('DBus: initializing interface: failed after making the connection!');
                }
            }
        }
        catch {
            Log.error('DBus: initializing interface: aborted!');
        }
    }
    async initLabels() {

        try {
            this.labels = new LabelHelper((await this.proxy.VersionAsync()).toString().split('.'));
        }
        catch {
            Log.error('DBus: initializing labels: get current version failed!');
        }
    }

    connectNotifySignal() {

        try {
            this.proxy.connectSignal('NotifyAction', (_proxy = null, _name, value) => {
                let newMode = parseInt(this.proxy.ModeSync());
                let details = `has changed.`;
                let iconName = 'supergfxctl-gex';
                let switchable = true;

                switch (this.labels.getAction(value)) {
                    case 2:
                        details = `You must switch to Integrated mode before switching to VFIO.`;
                        switchable = false;
                        break;
                    case 4:
                        details = `changed to ${this.labels.get(0, newMode)}`;
                        break;
                    default:
                        const action = this.labels.getAction(value);
                        details = `changed. Please save your work and ${action === 1 ? 'reboot' : 'logout'} to apply the changes.`;
                        iconName =
                            action === 1
                                ? 'system-reboot-symbolic'
                                : 'system-log-out-symbolic';
                        break;
                }
                if (switchable && newMode !== this.sgfxLastState) {
                    this.sgfxLastState = newMode;
                    this.gpuModeToggle.refresh();
                }
                PanelHelper.notify(details, iconName, this.labels.getAction(value));
            });
        }
        catch {
            Log.error(`DBus: connecting signal: Error, no live updates of GPU modes!`);
        }
    }

    connectPowerSignal() {

        try {
            this.proxy.connectSignal('NotifyGfxStatus', (_proxy = null, _name, value) => {
                if (this.sgfxPowerState !== value[0]) {
                    this.sgfxPowerState = value[0];
                    if (this.sgfxPowerState === 0 && this.sgfxLastState === 1) {

                        try {
                            let mode = parseInt(this.proxy.ModeSync());
                            if (this.labels.is(0, mode, 2))
                                PanelHelper.notify(`Your dedicated GPU turned on while you are on the integrated mode. This should not happen. It could be that another application rescanned your PCI bus. Rebooting is advised.`, 'dialog-error', 1);
                            else if (this.sgfxLastState !== mode)
                                this.sgfxLastState = mode;
                        }
                        catch {
                            Log.error('DBus: getting mode: failed!');
                        }
                    }
                    this.gpuModeToggle.refresh();
                }
            });
        }
        catch (error) {
            Log.error(`DBus: connecting signal: Error, no live updates of power modes!`, error);
        }
    }
    async vendor() {

        try {
            this.sgfxVendor = await this.proxy.VendorAsync();
        }
        catch {
            Log.error('DBus: get current vendor: failed!');
        }
        return this.sgfxVendor;
    }
    async gfxMode(mode = -1) {

        try {
            if (mode >= 0) {

                try {
                    await this.proxy.SetModeAsync(mode);
                }
                catch (e) {
                    Log.error('DBus: switching mode: failed!');
                    PanelHelper.notify(e.toString(), 'dialog-error');
                }
            }
            this.sgfxLastState = parseInt(await this.proxy.ModeAsync());
        }
        catch {
            Log.error('DBus: get current mode: failed!');
        }
        return this.sgfxLastState;
    }
    async gpuPower() {

        try {
            this.sgfxPowerState = parseInt((await this.proxy.PowerAsync()).toString().trim());
        }
        catch {
            Log.error('DBus: getting power mode: failed!');
            this.sgfxPowerState = 5;
        }
        return this.sgfxPowerState;
    }

    get supported() {
        if (this.connected) {
            if (this.sgfxSupportedGPUs.length === 1 &&
                ((this.labelHelper.v50 && this.sgfxSupportedGPUs[0] === 4) ||
                    ((this.labelHelper.v51 || this.labelHelper.v52) &&
                        this.sgfxSupportedGPUs[0] === 5))) {
                return [0].concat(this.sgfxSupportedGPUs);
            }
            return this.sgfxSupportedGPUs;
        }
        return [];
    }

    get lastState() {
        return this.connected
            ? this.sgfxLastState
            : 6 + (this.labels.v51 || this.labels.v52 ? 1 : 0);
    }

    get lastPowerState() {
        return this.connected ? this.sgfxPowerState : 5;
    }

    get labelHelper() {
        return this.labels;
    }
}
