/*******************************************************************************
 * 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/>.
 * 
 * Original Author: Gopi Sankar Karmegam
 ******************************************************************************/
/* jshint moz:true */

const GLib = imports.gi.GLib;
const GObject = imports.gi.GObject;
const Gio = imports.gi.Gio;
const Gtk = imports.gi.Gtk;

const ExtensionUtils = imports.misc.extensionUtils;
const Me = ExtensionUtils.getCurrentExtension();
const Lib = Me.imports.convenience;
const _d = Lib._log;
const SignalManager = Lib.SignalManager;

const Gettext = imports.gettext;
Gettext.bindtextdomain("sound-output-device-chooser", Me.path + "/locale");
const _ = Gettext.gettext;

var SETTINGS_SCHEMA = "org.gnome.shell.extensions.sound-output-device-chooser";
var HIDE_ON_SINGLE_DEVICE = "hide-on-single-device";
var HIDE_MENU_ICONS = "hide-menu-icons";
var SHOW_PROFILES = "show-profiles";
var PORT_SETTINGS = "ports-settings";
var SHOW_INPUT_SLIDER = "show-input-slider";
var SHOW_INPUT_DEVICES = "show-input-devices";
var SHOW_OUTPUT_DEVICES = "show-output-devices";
var ENABLE_LOG = "enable-log";
var NEW_PROFILE_ID = "new-profile-indentification";
var EXPAND_VOL_MENU = "expand-volume-menu";

var ICON_THEME = "icon-theme";
var ICON_THEME_COLORED = "colored";
var ICON_THEME_MONOCHROME = "monochrome";
var ICON_THEME_NONE = "none";

const PORT_SETTINGS_VERSION = 2;

function init() { }

function getPortsFromSettings(_settings) {
    let obj = JSON.parse(_settings.get_string(PORT_SETTINGS));
    let currentSettingsVersion = PORT_SETTINGS_VERSION;
    if (Array.isArray(obj)) {
        currentSettingsVersion = 1;
    }
    if (currentSettingsVersion < PORT_SETTINGS_VERSION) {
        obj = migratePortSettings(currentSettingsVersion, obj, _settings);
    }
    return obj.ports;
}

function setPortsSettings(ports, _settings) {
    let settingsObj = { "version": PORT_SETTINGS_VERSION };
    settingsObj.ports = ports;
    //_d(JSON.stringify(settingsObj));
    _settings.set_string(PORT_SETTINGS, JSON.stringify(settingsObj));
    return settingsObj;
}

function getPortDisplayName(port) {
    const card = Lib.getCardByName(port.card_name);
    const description = card && card.card_description
    return `${port.human_name} - ${description}`;
}

function migratePortSettings(currVersion, currSettings, _settings) {
    let ports = [];
    switch (currVersion) {
        case 1:
            let _lPorts = Lib.getPorts(true).slice();
            for (let port of currSettings) {
                for (var i = 0; i < _lPorts.length; i++) {
                    let _lPort = _lPorts[i];
                    if (port.human_name == _lPort.human_name && port.name == _lPort.name) {
                        port.card_name = _lPort.card_name;
                        port.display_name = getPortDisplayName(_lPort);
                        _lPorts.splice(i, 1);
                        ports.push(port);
                        break;
                    }
                }
            }
            break;
    }
    return setPortsSettings(ports, _settings);
}

const SDCSettingsWidget = new GObject.Class({
    Name: "SDC.Prefs.Widget",
    GTypeName: "SDCSettingsWidget",
    Extends: Gtk.Box,

    _init: function(params) {
        this.parent(params);
        this.orientation = Gtk.Orientation.VERTICAL;
        this.spacing = 0;
        let uiFileSuffix = "";

        if (Gtk.get_major_version() >= "4") {
            uiFileSuffix = "40";
            this.__addFn = this.append;
            this.__showFn = this.show;
        }
        else {
            this.__addFn = x => this.pack_start(x, true, true, 0);
            this.__showFn = this.show_all;
        }
        // creates the settings
        this._settings = Lib.getSettings(SETTINGS_SCHEMA);

        Lib.setLog(this._settings.get_boolean(ENABLE_LOG));

        // creates the ui builder and add the main resource file
        let uiFilePath = Me.path + "/ui/prefs-dialog" +uiFileSuffix +".glade";
        let builder = new Gtk.Builder();
        builder.set_translation_domain("sound-output-device-chooser");

        if (builder.add_from_file(uiFilePath) == 0) {
            _d("JS LOG: could not load the ui file: %s".format(uiFilePath));
            let label = new Gtk.Label({
                label: _("Could not load the preferences UI file"),
                vexpand: true
            });
            this.__addFn(label);            
        } else {
            _d("JS LOG:_UI file receive and load: " + uiFilePath);

            let mainContainer = builder.get_object("main-container");

            this.__addFn(mainContainer);
            
            this._signalManager = new SignalManager();

            let showProfileSwitch = builder.get_object("show-profile");
            let volMenuSwitch = builder.get_object(EXPAND_VOL_MENU);
            let singleDeviceSwitch = builder.get_object("single-device");
            let showInputSliderSwitch = builder.get_object("show-input-slider");
            let showInputDevicesSwitch = builder.get_object("show-input-devices");
            let showOutputDevicesSwitch = builder.get_object("show-output-devices");
            let hideMenuIconsSwitch = builder.get_object("hide-menu-icons");
            let iconThemeCombo = builder.get_object("icon-theme");
            let logSwitch = builder.get_object("enable-log");
            let newProfileIdSwitch = builder.get_object("new-profile-identification");

            this._settings.bind(HIDE_ON_SINGLE_DEVICE, singleDeviceSwitch, "active", Gio.SettingsBindFlags.DEFAULT);
            this._settings.bind(SHOW_PROFILES, showProfileSwitch, "active", Gio.SettingsBindFlags.DEFAULT);
            this._settings.bind(EXPAND_VOL_MENU, volMenuSwitch, "active", Gio.SettingsBindFlags.DEFAULT);
            this._settings.bind(SHOW_INPUT_SLIDER, showInputSliderSwitch, "active", Gio.SettingsBindFlags.DEFAULT);
            this._settings.bind(SHOW_INPUT_DEVICES, showInputDevicesSwitch, "active", Gio.SettingsBindFlags.DEFAULT);
            this._settings.bind(SHOW_OUTPUT_DEVICES, showOutputDevicesSwitch, "active", Gio.SettingsBindFlags.DEFAULT);
            this._settings.bind(HIDE_MENU_ICONS, hideMenuIconsSwitch, "active", Gio.SettingsBindFlags.DEFAULT);
            this._settings.bind(ENABLE_LOG, logSwitch, "active", Gio.SettingsBindFlags.DEFAULT);
            this._settings.bind(NEW_PROFILE_ID, newProfileIdSwitch, "active", Gio.SettingsBindFlags.DEFAULT);
            this._settings.bind(ICON_THEME, iconThemeCombo, "active-id", Gio.SettingsBindFlags.DEFAULT);

            //Show always is not working always, hidden in the UI directly
            let showAlwaysToggleRender = builder.get_object("ShowAlwaysToggleRender");
            let hideAlwaysToggleRender = builder.get_object("HideAlwaysToggleRender");
            let showActiveToggleRender = builder.get_object("ShowActiveToggleRender");

            this._signalManager.addSignal(showAlwaysToggleRender, "toggled", this._showAlwaysToggleRenderCallback.bind(this));
            this._signalManager.addSignal(hideAlwaysToggleRender, "toggled", this._hideAlwaysToggleRenderCallback.bind(this));
            this._signalManager.addSignal(showActiveToggleRender, "toggled", this._showActiveToggleRenderCallback.bind(this));

            this._portsStore = builder.get_object("ports-store");

            this._populatePorts();
            this._restorePortsFromSettings();
        }
    },

    _populatePorts: function() {
        let ports = Lib.getPorts(true);
        ports.sort((a, b) => (b.direction.localeCompare(a.direction)) || getPortDisplayName(a).localeCompare(getPortDisplayName(b))).forEach(port => {
            this._portsStore.set(this._portsStore.append(), [0, 1, 2, 3, 4, 5, 6, 7, 8], [port.human_name, false, false, true, port.name, 3, port.card_name, getPortDisplayName(port), port.direction]);
        });
    },

    _showAlwaysToggleRenderCallback: function(widget, path) {
        this._toggleCallback(widget, path, 1, [2, 3]);
    },

    _hideAlwaysToggleRenderCallback: function(widget, path) {
        this._toggleCallback(widget, path, 2, [1, 3]);
    },

    _showActiveToggleRenderCallback: function(widget, path) {
        this._toggleCallback(widget, path, 3, [1, 2]);
    },

    _toggleCallback: function(widget, path, activeCol, inactiveCols) {
        let active = !widget.active;
        if (!active) {
            return;
        }
        let [success, iter] = this._portsStore.get_iter_from_string(path);
        if (!success) {
            return;
        }
        /*Dont support non-pci cards for show always*/
        let card_name = this._portsStore.get_value(iter, 6);
        if (!/\.pci-/.exec(card_name) && activeCol == 1) {
            this._toggleCallback(widget, path, 3, [1, 2]);
            return;
        }
        this._portsStore.set_value(iter, activeCol, active);
        this._portsStore.set_value(iter, 5, activeCol);
        for (let col of inactiveCols) {
            this._portsStore.set_value(iter, col, !active);
        }
        this._commitSettings();
    },

    _commitSettings: function() {
        let ports = [];
        let [success, iter] = this._portsStore.get_iter_first();
        while (iter && success) {
            if (!this._portsStore.get_value(iter, 3)) {
                let display_option = this._portsStore.get_value(iter, 5);
                if (display_option != 3) {//Dont store default value
                    ports.push({
                        human_name: this._portsStore.get_value(iter, 0),
                        name: this._portsStore.get_value(iter, 4),
                        display_option: display_option,
                        card_name: this._portsStore.get_value(iter, 6),
                        display_name: this._portsStore.get_value(iter, 7)
                    });
                }
            }
            success = this._portsStore.iter_next(iter);
        }
        setPortsSettings(ports, this._settings);
    },

    _restorePortsFromSettings: function() {
        let ports = getPortsFromSettings(this._settings);

        let found;
        for (let port of ports) {
            found = false;
            if (!port || !port.human_name || !port.name) {
                continue;
            }

            let [success, iter] = this._portsStore.get_iter_first();

            while (iter && success) {
                let human_name = this._portsStore.get_value(iter, 0);
                let name = this._portsStore.get_value(iter, 4);
                let card_name = this._portsStore.get_value(iter, 6);

                if (port.name == name && port.human_name == human_name && port.card_name == card_name) {
                    this._portsStore.set_value(iter, 3, false);
                    this._portsStore.set_value(iter, port.display_option, true);
                    this._portsStore.set_value(iter, 5, port.display_option);
                    found = true;
                    break;
                }
                success = this._portsStore.iter_next(iter);
            }

            if (!found) {
                iter = this._portsStore.append();
                this._portsStore.set(iter, [0, 1, 2, 3, 4, 5, 6, 7, 8],
                    [port.human_name, false, false, false, port.name, port.display_option, port.card_name, port.display_name, ""]);
                this._portsStore.set_value(iter, port.display_option, true);
            }
        }
    }
});


function buildPrefsWidget() {
    let _settingsWidget = new SDCSettingsWidget();
    _settingsWidget.__showFn();

    return _settingsWidget;
}