/*
Copyright (C) 2014  spin83

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 2
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, visit https://www.gnu.org/licenses/.
*/

const { Clutter, Gio } = imports.gi;

const Main = imports.ui.main;
var { ANIMATION_TIME } = imports.ui.overview;

const Config = imports.misc.config;
const GNOME_SHELL_VERSION = Config.PACKAGE_VERSION.split('.');

const ExtensionUtils = imports.misc.extensionUtils;
const MultiMonitors = ExtensionUtils.getCurrentExtension();
const Convenience = MultiMonitors.imports.convenience;

const MMLayout = MultiMonitors.imports.mmlayout;
const MMOverview = MultiMonitors.imports.mmoverview;
const MMIndicator = MultiMonitors.imports.indicator;

const OVERRIDE_SCHEMA = 'org.gnome.shell.overrides';
const MUTTER_SCHEMA = 'org.gnome.mutter';
const WORKSPACES_ONLY_ON_PRIMARY_ID = 'workspaces-only-on-primary';

const SHOW_INDICATOR_ID = 'show-indicator';
const THUMBNAILS_SLIDER_POSITION_ID = 'thumbnails-slider-position';

function copyClass (s, d) {
//    global.log(s.name +" > "+ d.name);
    if (!s) throw Error(`copyClass s undefined for d ${d.name}`)
    let propertyNames = Reflect.ownKeys(s.prototype);
    for (let pName of propertyNames.values()) {

//        global.log(" ) "+pName.toString());
        if (typeof pName === "symbol") continue;
        if (d.prototype.hasOwnProperty(pName)) continue;
        if (pName === "prototype") continue;
        if (pName === "constructor") continue;
//        global.log(pName);
        let pDesc = Reflect.getOwnPropertyDescriptor(s.prototype, pName);
//        global.log(typeof pDesc);
        if (typeof pDesc !== 'object') continue;
        Reflect.defineProperty(d.prototype, pName, pDesc);
    }
};

function gnomeShellVersion() {
    return GNOME_SHELL_VERSION;
}

class MultiMonitorsAddOn {

    constructor() {
        this._settings = Convenience.getSettings();
        this._ov_settings = new Gio.Settings({ schema: OVERRIDE_SCHEMA });
        this._mu_settings = new Gio.Settings({ schema: MUTTER_SCHEMA });

        this.mmIndicator = null;
        Main.mmOverview = null;
        Main.mmLayoutManager = null;

        this._mmMonitors = 0;
        this.syncWorkspacesActualGeometry = null;
    }

    _showIndicator() {
		if(this._settings.get_boolean(SHOW_INDICATOR_ID)) {
			if(!this.mmIndicator) {
				this.mmIndicator = Main.panel.addToStatusArea('MultiMonitorsAddOn', new MMIndicator.MultiMonitorsIndicator());
			}
		}
		else {
			this._hideIndicator();
		}
    }

    _hideIndicator() {
		if(this.mmIndicator) {
			this.mmIndicator.destroy();
			this.mmIndicator = null;
		}
    }

    _showThumbnailsSlider() {
		if (this._settings.get_string(THUMBNAILS_SLIDER_POSITION_ID) === 'none') {
			this._hideThumbnailsSlider();
			return;
		}

		if(this._ov_settings.get_boolean(WORKSPACES_ONLY_ON_PRIMARY_ID))
			this._ov_settings.set_boolean(WORKSPACES_ONLY_ON_PRIMARY_ID, false);
		if(this._mu_settings.get_boolean(WORKSPACES_ONLY_ON_PRIMARY_ID))
			this._mu_settings.set_boolean(WORKSPACES_ONLY_ON_PRIMARY_ID, false);

		if (Main.mmOverview)
			return;

		Main.mmOverview = [];
		for (let idx = 0; idx < Main.layoutManager.monitors.length; idx++) {
			if (idx != Main.layoutManager.primaryIndex) {
				Main.mmOverview[idx] = new MMOverview.MultiMonitorsOverview(idx);
			}
		}

		this.syncWorkspacesActualGeometry = Main.overview.searchController._workspacesDisplay._syncWorkspacesActualGeometry;
		Main.overview.searchController._workspacesDisplay._syncWorkspacesActualGeometry = function() {
			if (this._inWindowFade)
				return;

			const primaryView = this._getPrimaryView();
			if (primaryView) {
				primaryView.ease({
					...this._actualGeometry,
					duration: Main.overview.animationInProgress ? ANIMATION_TIME : 0,
					mode: Clutter.AnimationMode.EASE_OUT_QUAD,
				});
			}

			for (let idx = 0; idx < Main.mmOverview.length; idx++) {
				if (!Main.mmOverview[idx])
					continue;
				if (!Main.mmOverview[idx]._overview)
					continue;
				const mmView = Main.mmOverview[idx]._overview._controls._workspacesViews;
				if (!mmView)
					continue;

				const mmGeometry = Main.mmOverview[idx].getWorkspacesActualGeometry();
				mmView.ease({
					...mmGeometry,
					duration: Main.overview.animationInProgress ? ANIMATION_TIME : 0,
					mode: Clutter.AnimationMode.EASE_OUT_QUAD,
				});
			}
		}
    }

	_hideThumbnailsSlider() {
        if (!Main.mmOverview)
            return;

        for (let idx = 0; idx < Main.mmOverview.length; idx++) {
            if (Main.mmOverview[idx])
                Main.mmOverview[idx].destroy();
        }
        Main.mmOverview = null;
        Main.overview.searchController._workspacesDisplay._syncWorkspacesActualGeometry = this.syncWorkspacesActualGeometry;
    }

    _relayout() {
		if(this._mmMonitors!=Main.layoutManager.monitors.length){
			this._mmMonitors = Main.layoutManager.monitors.length;
			global.log("pi:"+Main.layoutManager.primaryIndex);
			for (let i = 0; i < Main.layoutManager.monitors.length; i++) {
				let monitor = Main.layoutManager.monitors[i];
					global.log("i:"+i+" x:"+monitor.x+" y:"+monitor.y+" w:"+monitor.width+" h:"+monitor.height);	
			}
			this._hideThumbnailsSlider();
			this._showThumbnailsSlider();
		}
    }

    _switchOffThumbnails() {
		if (this._ov_settings.get_boolean(WORKSPACES_ONLY_ON_PRIMARY_ID) || this._mu_settings.get_boolean(WORKSPACES_ONLY_ON_PRIMARY_ID)) {
			this._settings.set_string(THUMBNAILS_SLIDER_POSITION_ID, 'none');
		}
    }

    enable(version) {
		global.log("Enable Multi Monitors Add-On ("+version+")...")
		
		if(Main.panel.statusArea.MultiMonitorsAddOn)
			disable();
		
		this._mmMonitors = 0;

		this._switchOffThumbnailsOvId = this._ov_settings.connect('changed::'+WORKSPACES_ONLY_ON_PRIMARY_ID,
																	this._switchOffThumbnails.bind(this));
		this._switchOffThumbnailsMuId = this._mu_settings.connect('changed::'+WORKSPACES_ONLY_ON_PRIMARY_ID,
																	this._switchOffThumbnails.bind(this));

		this._showIndicatorId = this._settings.connect('changed::'+SHOW_INDICATOR_ID, this._showIndicator.bind(this));
		this._showIndicator();
		
		Main.mmLayoutManager = new MMLayout.MultiMonitorsLayoutManager();
		this._showPanelId = this._settings.connect('changed::'+MMLayout.SHOW_PANEL_ID, Main.mmLayoutManager.showPanel.bind(Main.mmLayoutManager));
		Main.mmLayoutManager.showPanel();
		
		this._thumbnailsSliderPositionId = this._settings.connect('changed::'+THUMBNAILS_SLIDER_POSITION_ID, this._showThumbnailsSlider.bind(this));
		this._relayoutId = Main.layoutManager.connect('monitors-changed', this._relayout.bind(this));
		this._relayout();
    }

    disable() {
		Main.layoutManager.disconnect(this._relayoutId);
		this._ov_settings.disconnect(this._switchOffThumbnailsOvId);
		this._mu_settings.disconnect(this._switchOffThumbnailsMuId);
		
		this._settings.disconnect(this._showPanelId);
		this._settings.disconnect(this._thumbnailsSliderPositionId);
		this._settings.disconnect(this._showIndicatorId);

		
		this._hideIndicator();
		
		Main.mmLayoutManager.hidePanel();
		Main.mmLayoutManager = null;
		
		this._hideThumbnailsSlider();
		this._mmMonitors = 0;
		
		global.log("Disable Multi Monitors Add-On ...")
    }
}

var multiMonitorsAddOn = null;
var version = null;

function init() {
    Convenience.initTranslations();

    // fix bug in panel: Destroy function many time added to this same indicator.
    Main.panel._ensureIndicator = function(role) {
        let indicator = this.statusArea[role];
        if (indicator) {
            indicator.container.show();
            return null;
        }
        else {
            let constructor = PANEL_ITEM_IMPLEMENTATIONS[role];
            if (!constructor) {
                // This icon is not implemented (this is a bug)
                return null;
            }
            indicator = new constructor(this);
            this.statusArea[role] = indicator;
        }
        return indicator;
    };

    const metaVersion = MultiMonitors.metadata['version'];
    if (Number.isFinite(metaVersion)) {
        version = 'v'+Math.trunc(metaVersion);
        switch(Math.round((metaVersion%1)*10)) {
           case 0:
               break;
            case 1:
               version += '+bugfix';
               break;
            case 2:
               version += '+develop';
               break;
           default:
               version += '+modified';
                break;
        }
    }
    else
        version = metaVersion;
}

function enable() {
    if (multiMonitorsAddOn !== null)
        return;

    multiMonitorsAddOn = new MultiMonitorsAddOn();
    multiMonitorsAddOn.enable(version);
}

function disable() {
    if (multiMonitorsAddOn == null)
        return;

    multiMonitorsAddOn.disable();
    multiMonitorsAddOn = null;
}