/*
 * Zorin Menu: The official applications menu for Zorin OS.
 *
 * Copyright (C) 2016-2018 Zorin OS Technologies Ltd.
 *
 * 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, see <http://www.gnu.org/licenses/>.
 */

// Import Libraries
const Atk = imports.gi.Atk;
const GMenu = imports.gi.GMenu;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Clutter = imports.gi.Clutter;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const ExtensionUtils = imports.misc.extensionUtils;
const Me = ExtensionUtils.getCurrentExtension();
const Gettext = imports.gettext.domain(Me.metadata['gettext-domain']);
const _ = Gettext.gettext;
const Widgets = Me.imports.widgets;
const Layouts = Me.imports.layouts;
const AppsBackend = Me.imports.appsbackend;

const ALL_ANIMATIONS = ~0;

// Application menu class
var ApplicationsMenu = new Lang.Class({
    Name: 'ApplicationsMenu',
    Extends: PopupMenu.PopupMenu,

    // Initialize the menu
    _init: function(sourceActor, arrowAlignment, arrowSide, settings) {
        this.parent(sourceActor, arrowAlignment, arrowSide);
        this._section = new PopupMenu.PopupMenuSection();
        this.addMenuItem(this._section);
        this._settings = settings;
        this._appsBackend = new AppsBackend.AppsBackend();
        this._loadLayout();

        this._settingsId = this._settings.connect('changed::visible-menus', Lang.bind(this, function() {
            this._reloadLayout();
        }));
        this.actor.connect('destroy', this._onDestroy.bind(this));
    },

    _loadLayout: function() {
	    this._layout = Layouts.getLayout(this._settings, this._appsBackend);
        this._section.actor.add_child(this._layout.actor);
        this._layout.connect('activated', Lang.bind(this, function() {
            this.close(ALL_ANIMATIONS);
        }));
    },

    _reloadLayout: function() {
	    this._layout.destroy();
        this._loadLayout();
    },

    // Return that the menu is not empty (used by parent class)
    isEmpty: function() {
	    return false;
    },

    // Handle opening the menu
    open: function(animate) {
        this.parent(animate);
        this._layout.reset();
    },

    // Handle closing the menu
    close: function(animate) {
        this.parent(animate);
    },

    // Toggle menu open state
    toggle: function() {
        if (!this.isOpen) {
            if (Main.overview.visible)
                Main.overview.hide();
        }
        this.parent();
    },

    _onDestroy: function() {
        this._appsBackend.destroy();
        this._settings.disconnect(this._settingsId);
    }
});

// Application Menu Button class
var ApplicationsButton = new Lang.Class({
    Name: 'ApplicationsButton',
    Extends: PanelMenu.Button,

    // Initialize the menu
    _init: function(settings) {
        this.parent(1.0, null, false);

        this.setMenu(new ApplicationsMenu(this.actor, 1.0, St.Side.TOP, settings));
        Main.panel.menuManager.addMenu(this.menu);
        this.actor.accessible_role = Atk.Role.LABEL;
        this._menuButton = new Widgets.MenuButton(Main.panel.actor);
        this.actor.add_child(this._menuButton.actor);
        this.actor.name = 'panelApplications';
        this.actor.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
        this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
        this._showingId = Main.overview.connect('showing', Lang.bind(this, function() {
            this.actor.add_accessible_state(Atk.StateType.CHECKED);
        }));
        this._hidingId = Main.overview.connect('hiding', Lang.bind(this, function() {
            this.actor.remove_accessible_state(Atk.StateType.CHECKED);
        }));
        Main.layoutManager.connect('startup-complete',
                                   Lang.bind(this, this._setKeybinding));
        this._setKeybinding();
    },

    // Destroy the menu button
    _onDestroy: function() {
        Main.overview.disconnect(this._showingId);
        Main.overview.disconnect(this._hidingId);
        this._unsetKeybinding();
    },

    // Handle captured event
    _onCapturedEvent: function(actor, event) {
        if (event.type() == Clutter.EventType.BUTTON_PRESS) {
            if (!Main.overview.shouldToggleByCornerOrButton())
                return true;
        }
        return false;
    },

    // Set menu key binding
    _setKeybinding: function() {
        Main.wm.setCustomKeybindingHandler(
            'panel-main-menu',
            Shell.ActionMode.NORMAL |
            Shell.ActionMode.OVERVIEW,
            Lang.bind(this, function() {
                this.menu.toggle();
            })
        );
    },

    // Unset menu key binding
    _unsetKeybinding: function() {
        Main.wm.setCustomKeybindingHandler(
            'panel-main-menu',
            Shell.ActionMode.NORMAL |
            Shell.ActionMode.OVERVIEW,
            Main.sessionMode.hasOverview ?
            Lang.bind(Main.overview, Main.overview.toggle) :
            null
        );
    }
});
