/*
 * 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 Lang = imports.lang;
const St = imports.gi.St;
const Clutter = imports.gi.Clutter;
const PopupMenu = imports.ui.popupMenu;
const Signals = imports.signals;
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 Sections = Me.imports.sections;

// Menu Layout Enum
const layouts = {
    ALL: 0,
    APPS_ONLY: 1,
    SYSTEM_ONLY: 2
};

function getLayout(settings, appsBackend) {
    let layoutSetting = settings.get_enum('visible-menus');
    switch(layoutSetting) {
        case layouts.ALL:
            return new StandardLayout(appsBackend);
        case layouts.APPS_ONLY:
            return new AppListLayout(appsBackend);
        case layouts.SYSTEM_ONLY:
            return new ShortcutsLayout(appsBackend);
        default:
            return new StandardLayout(appsBackend);
    }
}

// Base Layout
var BaseLayout = new Lang.Class({
    Name: 'BaseLayout',
    Abstract: true,

    _init: function(appsBackend) {
        this.actor = new St.BoxLayout({
            vertical: false
        });
        this._appsBackend = appsBackend;
        this._loadLayout();
        this._connectSignals();
        this.reset();
        this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPress));
        this.actor.connect('destroy', this._onDestroy.bind(this));
    },

    show: function() {
        this.actor.show();
    },

    hide: function() {
        this.actor.hide();
    },

    _loadLayout: function() {
        this.emit('loaded-layout');
    },

    _connectSignals: function() {
        this.emit('connected-signals');
    },

    // Handle key presses
    _onKeyPress: function(actor, event) {
        return Clutter.EVENT_PROPAGATE;
    },

    reset: function(){
        this.emit('reset');
    },

    _activated: function() {
        this.emit('activated');
    },

    _onDestroy: function() {
        this.emit('destroy');
    },
    
    destroy: function() {
        this.actor.destroy();
    }
});
Signals.addSignalMethods(BaseLayout.prototype);

// Standard Layout
var StandardLayout = new Lang.Class({
    Name: 'StandardLayout',
    Extends: BaseLayout,

    // Initialize the layout
    _init: function(appsBackend) {
        this.parent(appsBackend);
        this.actor.add_style_class_name("main-box");
    },

    _loadLayout: function() {
        // Create Sections and Widgets
        this._categoriesSection = new Sections.CategoriesListSection(this._appsBackend);
        this._appsSection = new Sections.AppsListSection(this._appsBackend);
        this._searchEntry = new Widgets.SearchEntry();
        this._backButton = new Widgets.BackMenuItem();
        this._userItem = new Widgets.UserMenuItem();
        this._placesSection = new Sections.PlacesSection();
        this._shortcutsSection = new Sections.ShortcutsSection();
        this._sessionButtonsSection = new Sections.SessionButtonsSection();

        // Create Boxes
        this._leftBox = new St.BoxLayout({
            vertical: true,
            style_class: 'left-box'
        });
        this._rightBox = new St.BoxLayout({
            vertical: true,
            style_class: 'right-box'
        });

        // Fill Left Box
        this._leftBox.add(this._categoriesSection.actor, {
            expand: true,
            x_fill: true,
            y_fill: false,
            y_align: St.Align.START
        });
        this._leftBox.add(this._appsSection.actor, {
            expand: true,
            x_fill: true,
            y_fill: false,
            y_align: St.Align.START
        });
        this._leftBox.add(this._backButton.actor, {
            expand: false,
            x_fill: true,
            y_fill: false,
            y_align: St.Align.START
        });
        this._leftBox.add(this._searchEntry.actor, {
            expand: false,
            x_fill: true,
            y_fill: false,
            y_align: St.Align.START
        });

        // Fill Right Box
        this._rightBox.add(this._userItem.actor, {
            expand: false,
            x_fill: true,
            y_fill: false,
            y_align: St.Align.START
        });
        let separator = new PopupMenu.PopupSeparatorMenuItem();
        this._rightBox.add(separator.actor, {
                expand: false,
                x_fill: true,
                y_fill: false,
                y_align: St.Align.START
        });
        this._rightBox.add(this._placesSection.actor, {
            expand: true,
            x_fill: true,
            y_fill: false,
            y_align: St.Align.END
        });
        separator = new PopupMenu.PopupSeparatorMenuItem();
        this._rightBox.add(separator.actor, {
            expand: false,
            x_fill: true,
            y_fill: false,
            y_align: St.Align.START
        });
        this._rightBox.add(this._shortcutsSection.actor, {
            expand: true,
            x_fill: true,
            y_fill: false,
            y_align: St.Align.END
        });
        separator = new PopupMenu.PopupSeparatorMenuItem();
        this._rightBox.add(separator.actor, {
            expand: false,
            x_fill: true,
            y_fill: false,
            y_align: St.Align.START
        });
        this._rightBox.add(this._sessionButtonsSection.actor, {
            expand: true,
            x_fill: true,
            y_fill: false,
            y_align: St.Align.END
        });

        // Add Boxes
        this.actor.add(this._leftBox, {
            expand: true,
            x_fill: true,
            y_fill: true
        });
        let verticalSeparator = new Widgets.VerticalSeparator();
        this.actor.add(verticalSeparator.actor, {
            expand: false,
            x_fill: false,
            y_fill: true
        });
        this.actor.add_child(this._rightBox);
    },

    _connectSignals: function() {
        this._categoriesSection.connect('selected', this._onSelectCategory.bind(this));
        this._appsSection.connect('activated', this._activated.bind(this));
        this._searchEntry.connect('cleared', this._onSearchCleared.bind(this));
        this._searchEntry.connect('search', this._onSearch.bind(this));
        this._backButton.connect('activated', this.reset.bind(this));
        this._userItem.connect('activated', this._activated.bind(this));
        this._placesSection.connect('activated', this._activated.bind(this));
        this._shortcutsSection.connect('activated', this._activated.bind(this));
        this._sessionButtonsSection.connect('activated', this._activated.bind(this));
    },

    // Carry out a search based on the search text entry value
    _onSearch: function(actor, pattern){
        if (pattern) {
            this._appsSection.searchApps(pattern);
            this._categoriesSection.hide();
            this._appsSection.show();
            this._backButton.show();
            this._appsSection.grab_key_focus();
        }
    },

    _onSearchCleared: function(){
        this._appsSection.hide();
        this._backButton.hide();
        this._categoriesSection.show();
    },

    _onSelectCategory: function(actor, category_menu_id){
        if (category_menu_id) {
            this._appsSection.selectCategory(category_menu_id);
            this._categoriesSection.hide();
            this._appsSection.show();
            this._backButton.show();
            this._searchEntry.grab_key_focus();
        }
    },

    // Handle key presses
    _onKeyPress: function(actor, event) {
        if (event.has_control_modifier()) {
            this._searchEntry.grab_key_focus();
            return Clutter.EVENT_PROPAGATE;
        }

        let symbol = event.get_key_symbol();

        switch(symbol) {
            case Clutter.KEY_BackSpace:
                if (!this._searchEntry.has_key_focus()) {
                    this._searchEntry.grab_key_focus();
                    let newText = this._searchEntry.getText().slice(0, -1);
                    this._searchEntry.setText(newText);
                }
                return Clutter.EVENT_PROPAGATE;
            case Clutter.KEY_Tab:
            case Clutter.KEY_KP_Tab:
            case Clutter.Up:
            case Clutter.KP_Up:
            case Clutter.Down:
            case Clutter.KP_Down:
            case Clutter.Left:
            case Clutter.KP_Left:
            case Clutter.Right:
            case Clutter.KP_Right:
                return Clutter.EVENT_PROPAGATE;
            default:
                let key = event.get_key_unicode();
                if (key.length != 0) {
                    this._searchEntry.grab_key_focus();
                    let newText = this._searchEntry.getText() + key;
                    this._searchEntry.setText(newText);
                }
        }
        return Clutter.EVENT_PROPAGATE;
    },

    reset: function() {
        this._searchEntry.clear();
    }
});

// App List Layout
var AppListLayout = new Lang.Class({
    Name: 'AppListLayout',
    Extends: BaseLayout,


    // Initialize the layout
    _init: function(appsBackend) {
        this.parent(appsBackend);
        this.actor.add_style_class_name("main-box");
    },

    _loadLayout: function() {
        // Create Sections and Widgets
        this._categoriesSection = new Sections.CategoriesListSection(this._appsBackend);
        this._appsSection = new Sections.AppsListSection(this._appsBackend);
        this._searchEntry = new Widgets.SearchEntry();
        this._backButton = new Widgets.BackMenuItem();

        // Create Box
        this._box = new St.BoxLayout({
            vertical: true,
            style_class: 'left-box'
        });

        // Fill Box
        this._box.add(this._categoriesSection.actor, {
            expand: true,
            x_fill: true,
            y_fill: false,
            y_align: St.Align.START
        });
        this._box.add(this._appsSection.actor, {
            expand: true,
            x_fill: true,
            y_fill: false,
            y_align: St.Align.START
        });
        this._box.add(this._backButton.actor, {
            expand: false,
            x_fill: true,
            y_fill: false,
            y_align: St.Align.START
        });
        this._box.add(this._searchEntry.actor, {
            expand: false,
            x_fill: true,
            y_fill: false,
            y_align: St.Align.START
        });
        
        // Add Box
        this.actor.add_child(this._box, {
            expand: true,
            x_fill: true,
            y_fill: true
        });
    },

    _connectSignals: function() {
        this._categoriesSection.connect('selected', this._onSelectCategory.bind(this));
        this._appsSection.connect('activated', this._activated.bind(this));
        this._searchEntry.connect('cleared', this._onSearchCleared.bind(this));
        this._searchEntry.connect('search', this._onSearch.bind(this));
        this._backButton.connect('activated', this.reset.bind(this));
    },

    // Carry out a search based on the search text entry value
    _onSearch: function(actor, pattern){
        if (pattern) {
            this._appsSection.searchApps(pattern);
            this._categoriesSection.hide();
            this._appsSection.show();
            this._backButton.show();
            this._appsSection.grab_key_focus();
        }
    },

    _onSearchCleared: function(){
        this._appsSection.hide();
        this._backButton.hide();
        this._categoriesSection.show();
    },

    _onSelectCategory: function(actor, category_menu_id){
        if (category_menu_id) {
            this._appsSection.selectCategory(category_menu_id);
            this._categoriesSection.hide();
            this._appsSection.show();
            this._backButton.show();
            this._searchEntry.grab_key_focus();
        }
    },

    // Handle key presses
    _onKeyPress: function(actor, event) {
        if (event.has_control_modifier()) {
            this._searchEntry.grab_key_focus();
            return Clutter.EVENT_PROPAGATE;
        }

        let symbol = event.get_key_symbol();

        switch(symbol) {
            case Clutter.KEY_BackSpace:
                if (!this._searchEntry.has_key_focus()) {
                    this._searchEntry.grab_key_focus();
                    let newText = this._searchEntry.getText().slice(0, -1);
                    this._searchEntry.setText(newText);
                }
                return Clutter.EVENT_PROPAGATE;
            case Clutter.KEY_Tab:
            case Clutter.KEY_KP_Tab:
            case Clutter.Up:
            case Clutter.KP_Up:
            case Clutter.Down:
            case Clutter.KP_Down:
            case Clutter.Left:
            case Clutter.KP_Left:
            case Clutter.Right:
            case Clutter.KP_Right:
                return Clutter.EVENT_PROPAGATE;
            default:
                let key = event.get_key_unicode();
                if (key.length != 0) {
                    this._searchEntry.grab_key_focus();
                    let newText = this._searchEntry.getText() + key;
                    this._searchEntry.setText(newText);
                }
        }
        return Clutter.EVENT_PROPAGATE;
    },

    reset: function(){
        this._searchEntry.clear();
    }
});

// Shortcuts Layout
var ShortcutsLayout = new Lang.Class({
    Name: 'ShortcutsLayout',
    Extends: BaseLayout,


    // Initialize the layout
    _init: function(appsBackend) {
        this.parent(appsBackend);
    },

    _loadLayout: function() {
        // Create Sections and Widgets
        this._userItem = new Widgets.UserMenuItem();
        this._placesSection = new Sections.PlacesSection();
        this._shortcutsSection = new Sections.ShortcutsSection();
        this._sessionButtonsSection = new Sections.SessionButtonsSection();

        // Create Box
        this._box = new St.BoxLayout({
            vertical: true,
            style_class: 'right-box'
        });

        // Fill Box
        this._box.add(this._userItem.actor, {
            expand: false,
            x_fill: true,
            y_fill: false,
            y_align: St.Align.START
        });
        let separator = new PopupMenu.PopupSeparatorMenuItem();
        this._box.add(separator.actor, {
                expand: false,
                x_fill: true,
                y_fill: false,
                y_align: St.Align.START
        });
        this._box.add(this._placesSection.actor, {
            expand: true,
            x_fill: true,
            y_fill: false,
            y_align: St.Align.END
        });
        separator = new PopupMenu.PopupSeparatorMenuItem();
        this._box.add(separator.actor, {
            expand: false,
            x_fill: true,
            y_fill: false,
            y_align: St.Align.START
        });
        this._box.add(this._shortcutsSection.actor, {
            expand: true,
            x_fill: true,
            y_fill: false,
            y_align: St.Align.END
        });
        separator = new PopupMenu.PopupSeparatorMenuItem();
        this._box.add(separator.actor, {
            expand: false,
            x_fill: true,
            y_fill: false,
            y_align: St.Align.START
        });
        this._box.add(this._sessionButtonsSection.actor, {
            expand: true,
            x_fill: true,
            y_fill: false,
            y_align: St.Align.END
        });
        
        // Add Box
        this.actor.add_child(this._box);
    },

    _connectSignals: function() {
        this._userItem.connect('activated', this._activated.bind(this));
        this._placesSection.connect('activated', this._activated.bind(this));
        this._shortcutsSection.connect('activated', this._activated.bind(this));
        this._sessionButtonsSection.connect('activated', this._activated.bind(this));
    }
});
