# Copyright (C) 2009-2014 Yann Leboulanger <asterix AT lagaule.org>
#
# This file is part of Gajim.
#
# Gajim 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; version 3 only.
#
# Gajim 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 Gajim. If not, see <http://www.gnu.org/licenses/>.

from gi.repository import Gtk, Gio, GLib
from nbxmpp.namespaces import Namespace

from gajim import gtkgui_helpers
from gajim.common import app
from gajim.common import helpers
from gajim.common.helpers import is_affiliation_change_allowed
from gajim.common.helpers import is_role_change_allowed
from gajim.common.i18n import _
from gajim.common.const import URIType
from gajim.common.const import URIAction

from gajim.gui.util import get_builder
from gajim.gui.const import ControlType


def build_resources_submenu(contacts, account, action, room_jid=None,
                room_account=None, cap=None):
    """
    Build a submenu with contact's resources. room_jid and room_account are for
    action self.on_invite_to_room
    """
    roster = app.interface.roster
    sub_menu = Gtk.Menu()

    for c in contacts:
        item = Gtk.MenuItem.new_with_label(
            '%s (%s)' % (c.resource, str(c.priority)))
        sub_menu.append(item)
        if action == roster.on_invite_to_room:  # pylint: disable=comparison-with-callable
            item.connect('activate', action, [(c, account)], room_jid,
                    room_account, c.resource)
        else: # start_chat, execute_command, send_file
            item.connect('activate', action, c, account, c.resource)

        if cap and not c.supports(cap):
            item.set_sensitive(False)

    return sub_menu

def build_invite_submenu(invite_menuitem, list_, ignore_rooms=None,
show_bookmarked=False, force_resource=False):
    """
    list_ in a list of (contact, account)
    force_resource means we want to send invitation even if there is only one
        resource
    """
    if ignore_rooms is None:
        ignore_rooms = []
    roster = app.interface.roster
    # used if we invite only one contact with several resources
    contact_list = []
    if len(list_) == 1:
        contact, account = list_[0]
        contact_list = app.contacts.get_contacts(account, contact.jid)
    contacts_transport = -1
    connected_accounts = []
    # -1 is at start, False when not from the same, None when jabber
    for (contact, account) in list_:
        if not account in connected_accounts:
            connected_accounts.append(account)
        transport = app.get_transport_name_from_jid(contact.jid)
        if transport == 'jabber':
            transport = None
        if contacts_transport == -1:
            contacts_transport = transport
        elif contacts_transport != transport:
            contacts_transport = False

    if contacts_transport is False:
        # they are not all from the same transport
        invite_menuitem.set_sensitive(False)
        return
    invite_to_submenu = Gtk.Menu()
    invite_menuitem.set_submenu(invite_to_submenu)
    rooms = [] # a list of (room_jid, account) tuple
    minimized_controls = []
    for account in connected_accounts:
        minimized_controls += \
            list(app.interface.minimized_controls[account].values())
    for gc_control in app.interface.msg_win_mgr.get_controls(
            ControlType.GROUPCHAT) + minimized_controls:
        acct = gc_control.account
        if acct not in connected_accounts:
            continue
        room_jid = gc_control.room_jid
        room_name = gc_control.room_name
        if room_jid in ignore_rooms:
            continue
        if room_jid in app.gc_connected[acct] and \
        app.gc_connected[acct][room_jid] and \
        contacts_transport in ['jabber', None]:
            rooms.append((room_jid, room_name, acct))
    if rooms:
        item = Gtk.SeparatorMenuItem.new() # separator
        invite_to_submenu.append(item)
        for (room_jid, room_name, account) in rooms:
            menuitem = Gtk.MenuItem.new_with_label(room_name)
            if len(contact_list) > 1: # several resources
                menuitem.set_submenu(build_resources_submenu(
                    contact_list, account, roster.on_invite_to_room, room_jid,
                    account))
            else:
                # use resource if it's self contact
                if contact.jid == app.get_jid_from_account(account):
                    resource = contact.resource
                else:
                    resource = None
                menuitem.connect('activate', roster.on_invite_to_room, list_,
                    room_jid, account, resource)
            invite_to_submenu.append(menuitem)

    if not show_bookmarked:
        return
    rooms2 = [] # a list of (room_jid, account) tuple
    r_jids = [] # list of room jids
    for account in connected_accounts:
        con = app.connections[account]
        for bookmark in con.get_module('Bookmarks').bookmarks:
            if bookmark.jid in r_jids:
                continue
            if bookmark.jid not in app.gc_connected[account] or not \
            app.gc_connected[account][bookmark.jid]:
                rooms2.append((bookmark.jid, account))
                r_jids.append(bookmark.jid)

    if not rooms2:
        return
    item = Gtk.SeparatorMenuItem.new() # separator
    invite_to_submenu.append(item)
    for (room_jid, account) in rooms2:
        menuitem = Gtk.MenuItem.new_with_label(room_jid.localpart)
        if len(contact_list) > 1: # several resources
            menuitem.set_submenu(build_resources_submenu(
                contact_list, account, roster.on_invite_to_room, str(room_jid),
                account))
        else:
            # use resource if it's self contact
            if contact.jid == app.get_jid_from_account(account):
                resource = contact.resource
            else:
                resource = None
            menuitem.connect('activate', roster.on_invite_to_room, list_,
                str(room_jid), account, resource)
        invite_to_submenu.append(menuitem)

def get_contact_menu(contact, account, use_multiple_contacts=True,
show_start_chat=True, show_encryption=False, show_buttonbar_items=True,
control=None, gc_contact=None, is_anonymous=True):
    """
    Build contact popup menu for roster and chat window. If control is not set,
    we hide invite_contacts_menuitem
    """
    if not contact:
        return

    jid = contact.jid
    our_jid = jid == app.get_jid_from_account(account)
    roster = app.interface.roster

    xml = get_builder('contact_context_menu.ui')
    contact_context_menu = xml.get_object('contact_context_menu')

    start_chat_menuitem = xml.get_object('start_chat_menuitem')
    execute_command_menuitem = xml.get_object('execute_command_menuitem')
    rename_menuitem = xml.get_object('rename_menuitem')
    edit_groups_menuitem = xml.get_object('edit_groups_menuitem')
    send_file_menuitem = xml.get_object('send_file_menuitem')
    information_menuitem = xml.get_object('information_menuitem')
    history_menuitem = xml.get_object('history_menuitem')
    send_single_message_menuitem = xml.get_object('send_single_message_menuitem')
    invite_menuitem = xml.get_object('invite_menuitem')
    block_menuitem = xml.get_object('block_menuitem')
    unblock_menuitem = xml.get_object('unblock_menuitem')
    ignore_menuitem = xml.get_object('ignore_menuitem')
    unignore_menuitem = xml.get_object('unignore_menuitem')
    # Subscription submenu
    subscription_menuitem = xml.get_object('subscription_menuitem')
    send_auth_menuitem, ask_auth_menuitem, revoke_auth_menuitem = \
            subscription_menuitem.get_submenu().get_children()
    add_to_roster_menuitem = xml.get_object('add_to_roster_menuitem')
    remove_from_roster_menuitem = xml.get_object(
            'remove_from_roster_menuitem')
    manage_contact_menuitem = xml.get_object('manage_contact')
    convert_to_gc_menuitem = xml.get_object('convert_to_groupchat_menuitem')
    last_separator = xml.get_object('last_separator')

    items_to_hide = []

    contacts = app.contacts.get_contacts(account, jid)
    if len(contacts) > 1 and use_multiple_contacts: # several resources
        send_file_menuitem.set_submenu(build_resources_submenu(
            contacts,
            account,
            roster.on_send_file_menuitem_activate,
            cap=Namespace.JINGLE_FILE_TRANSFER_5))
        execute_command_menuitem.set_submenu(build_resources_submenu(
                contacts, account, roster.on_execute_command, cap=Namespace.COMMANDS))
    else:
        if contact.supports(Namespace.JINGLE_FILE_TRANSFER_5):
            send_file_menuitem.set_sensitive(True)
            send_file_menuitem.connect('activate',
                    roster.on_send_file_menuitem_activate, contact, account)
        else:
            send_file_menuitem.set_sensitive(False)

        if contact.supports(Namespace.COMMANDS):
            execute_command_menuitem.set_sensitive(True)
            if gc_contact and gc_contact.jid and not is_anonymous:
                execute_command_menuitem.connect('activate',
                    roster.on_execute_command, gc_contact, account,
                    gc_contact.resource)
            else:
                execute_command_menuitem.connect('activate',
                    roster.on_execute_command, contact, account,
                    contact.resource)
        else:
            execute_command_menuitem.set_sensitive(False)

    start_chat_menuitem.connect(
        'activate', app.interface.on_open_chat_window, contact, account)

    rename_menuitem.connect('activate', roster.on_rename, 'contact', jid,
        account)

    history_menuitem.set_action_name('app.browse-history')
    dict_ = {'jid': GLib.Variant('s', contact.jid),
             'account': GLib.Variant('s', account)}
    variant = GLib.Variant('a{sv}', dict_)
    history_menuitem.set_action_target_value(variant)

    if control:
        convert_to_gc_menuitem.connect('activate',
            control._on_convert_to_gc_menuitem_activate)
    else:
        items_to_hide.append(convert_to_gc_menuitem)

    if _('Not in contact list') not in contact.get_shown_groups():
        # contact is in normal group
        edit_groups_menuitem.connect('activate', roster.on_edit_groups, [(contact,
                account)])
    else:
        # contact is in group 'Not in contact list'
        edit_groups_menuitem.set_sensitive(False)

    # Hide items when it's self contact row
    if our_jid:
        items_to_hide += [rename_menuitem, edit_groups_menuitem]

    # Unsensitive many items when account is offline
    if app.account_is_disconnected(account):
        for widget in (start_chat_menuitem, rename_menuitem,
        edit_groups_menuitem, send_file_menuitem, convert_to_gc_menuitem,
        information_menuitem):
            widget.set_sensitive(False)

    if not show_start_chat:
        items_to_hide.append(start_chat_menuitem)

    if not show_buttonbar_items:
        items_to_hide += [history_menuitem, send_file_menuitem,
                information_menuitem, convert_to_gc_menuitem, last_separator]

    if not control:
        items_to_hide.append(convert_to_gc_menuitem)

    # Hide items when it's a pm
    if gc_contact:
        items_to_hide += [rename_menuitem, edit_groups_menuitem,
        subscription_menuitem, remove_from_roster_menuitem]

    for item in items_to_hide:
        item.set_no_show_all(True)
        item.hide()

    # Zeroconf Account
    if app.settings.get_account_setting(account, 'is_zeroconf'):
        for item in (send_single_message_menuitem,
        invite_menuitem, block_menuitem, unblock_menuitem, ignore_menuitem,
        unignore_menuitem, subscription_menuitem,
        manage_contact_menuitem, convert_to_gc_menuitem):
            item.set_no_show_all(True)
            item.hide()

        if contact.show in ('offline', 'error'):
            information_menuitem.set_sensitive(False)
            send_file_menuitem.set_sensitive(False)
        else:
            information_menuitem.connect('activate', roster.on_info_zeroconf,
                    contact, account)

        contact_context_menu.connect('selection-done',
                gtkgui_helpers.destroy_widget)
        contact_context_menu.show_all()
        return contact_context_menu

    # normal account

    if gc_contact:
        if not gc_contact.jid:
            # it's a pm and we don't know real JID
            invite_menuitem.set_sensitive(False)
        else:
            bookmarked = False
            c_ = app.contacts.get_contact(account, gc_contact.jid,
                gc_contact.resource)
            if c_ and c_.supports(Namespace.CONFERENCE):
                bookmarked = True
            build_invite_submenu(invite_menuitem, [(gc_contact, account)],
                show_bookmarked=bookmarked)
    else:
        force_resource = False
        if control and control.resource:
            force_resource = True
        build_invite_submenu(invite_menuitem, [(contact, account)],
            show_bookmarked=contact.supports(Namespace.CONFERENCE),
            force_resource=force_resource)

    if app.account_is_disconnected(account):
        invite_menuitem.set_sensitive(False)

    send_single_message_menuitem.connect('activate',
        roster.on_send_single_message_menuitem_activate, account, contact)

    remove_from_roster_menuitem.connect('activate', roster.on_req_usub,
        [(contact, account)])
    information_menuitem.connect('activate', roster.on_info, contact, account)

    if _('Not in contact list') not in contact.get_shown_groups():
        # contact is in normal group
        add_to_roster_menuitem.hide()
        add_to_roster_menuitem.set_no_show_all(True)

        if contact.sub in ('from', 'both'):
            send_auth_menuitem.set_sensitive(False)
        else:
            send_auth_menuitem.connect('activate', roster.authorize, jid, account)
        if contact.sub in ('to', 'both'):
            ask_auth_menuitem.set_sensitive(False)
        else:
            ask_auth_menuitem.connect('activate', roster.req_sub, jid,
                    _('I would like to add you to my contact list'), account,
                    contact.groups, contact.name)
        transport = app.get_transport_name_from_jid(jid,
            use_config_setting=False)
        if contact.sub in ('to', 'none') or transport not in ['jabber', None]:
            revoke_auth_menuitem.set_sensitive(False)
        else:
            revoke_auth_menuitem.connect('activate', roster.revoke_auth, jid,
                    account)

    elif app.connections[account].roster_supported:
        # contact is in group 'Not in contact list'
        add_to_roster_menuitem.set_no_show_all(False)
        subscription_menuitem.set_sensitive(False)

        add_to_roster_menuitem.connect('activate', roster.on_add_to_roster,
                contact, account)
    else:
        add_to_roster_menuitem.hide()
        add_to_roster_menuitem.set_no_show_all(True)
        subscription_menuitem.set_sensitive(False)

    # Hide items when it's self contact row
    if our_jid:
        manage_contact_menuitem.set_sensitive(False)

    # Unsensitive items when account is offline
    if app.account_is_disconnected(account):
        for widget in (send_single_message_menuitem, subscription_menuitem,
        add_to_roster_menuitem, remove_from_roster_menuitem,
        execute_command_menuitem):
            widget.set_sensitive(False)


    con = app.connections[account]
    if con.get_module('Blocking').supported:
        transport = app.get_transport_name_from_jid(jid, use_config_setting=False)
        if helpers.jid_is_blocked(account, jid):
            block_menuitem.set_no_show_all(True)
            block_menuitem.hide()
            if transport not in ('jabber', None):
                unblock_menuitem.set_no_show_all(True)
                unblock_menuitem.hide()
                unignore_menuitem.set_no_show_all(False)
                unignore_menuitem.connect('activate', roster.on_unblock, [(contact,
                        account)])
            else:
                unblock_menuitem.connect('activate', roster.on_unblock, [(contact,
                        account)])
        else:
            unblock_menuitem.set_no_show_all(True)
            unblock_menuitem.hide()
            if transport not in ('jabber', None):
                block_menuitem.set_no_show_all(True)
                block_menuitem.hide()
                ignore_menuitem.set_no_show_all(False)
                ignore_menuitem.connect('activate', roster.on_block, [(contact,
                        account)])
            else:
                block_menuitem.connect('activate', roster.on_block, [(contact,
                        account)])
    else:
        unblock_menuitem.set_no_show_all(True)
        block_menuitem.set_sensitive(False)
        unblock_menuitem.hide()

    contact_context_menu.connect('selection-done', gtkgui_helpers.destroy_widget)
    contact_context_menu.show_all()
    return contact_context_menu

def get_transport_menu(contact, account):
    roster = app.interface.roster
    jid = contact.jid

    menu = Gtk.Menu()

    # Send single message
    item = Gtk.MenuItem.new_with_mnemonic(_('Send Single _Message…'))
    item.connect('activate', roster.on_send_single_message_menuitem_activate,
        account, contact)
    menu.append(item)
    if app.account_is_disconnected(account):
        item.set_sensitive(False)

    blocked = False
    if helpers.jid_is_blocked(account, jid):
        blocked = True

    item = Gtk.SeparatorMenuItem.new() # separator
    menu.append(item)

    # Execute Command
    item = Gtk.MenuItem.new_with_mnemonic(_('E_xecute Command…'))
    menu.append(item)
    item.connect('activate', roster.on_execute_command, contact, account,
        contact.resource)
    if app.account_is_disconnected(account):
        item.set_sensitive(False)

    # Manage Transport submenu
    item = Gtk.MenuItem.new_with_mnemonic(_('_Manage Transport'))
    manage_transport_submenu = Gtk.Menu()
    item.set_submenu(manage_transport_submenu)
    menu.append(item)

    # Modify Transport
    item = Gtk.MenuItem.new_with_mnemonic(_('_Modify Transport'))
    manage_transport_submenu.append(item)
    item.connect('activate', roster.on_edit_agent, contact, account)
    if app.account_is_disconnected(account):
        item.set_sensitive(False)

    # Rename
    item = Gtk.MenuItem.new_with_mnemonic(_('_Rename…'))
    manage_transport_submenu.append(item)
    item.connect('activate', roster.on_rename, 'agent', jid, account)
    if app.account_is_disconnected(account):
        item.set_sensitive(False)

    item = Gtk.SeparatorMenuItem.new() # separator
    manage_transport_submenu.append(item)

    # Block
    if blocked:
        item = Gtk.MenuItem.new_with_mnemonic(_('_Unblock'))
        item.connect('activate', roster.on_unblock, [(contact, account)])
    else:
        item = Gtk.MenuItem.new_with_mnemonic(_('_Block'))
        item.connect('activate', roster.on_block, [(contact, account)])

    manage_transport_submenu.append(item)
    if app.account_is_disconnected(account):
        item.set_sensitive(False)

    # Remove
    item = Gtk.MenuItem.new_with_mnemonic(_('Remo_ve'))
    manage_transport_submenu.append(item)
    item.connect('activate', roster.on_remove_agent, [(contact, account)])
    if app.account_is_disconnected(account):
        item.set_sensitive(False)

    item = Gtk.SeparatorMenuItem.new() # separator
    menu.append(item)

    # Information
    information_menuitem = Gtk.MenuItem.new_with_mnemonic(_('_Information'))
    menu.append(information_menuitem)
    information_menuitem.connect('activate', roster.on_info, contact, account)
    if app.account_is_disconnected(account):
        information_menuitem.set_sensitive(False)

    menu.connect('selection-done', gtkgui_helpers.destroy_widget)
    menu.show_all()
    return menu


def get_singlechat_menu(control_id, account, jid, type_):
    singlechat_menu = [
        (_('Send File'), [
            ('win.send-file-httpupload-', _('Upload File…')),
            ('win.send-file-jingle-', _('Send File Directly…')),
            ]),
        ('win.send-marker-', _('Send Read Markers')),
        (_('Send Chatstate'), ['chatstate']),
        ('win.invite-contacts-', _('Invite Contacts…')),
        ('win.add-to-roster-', _('Add to Contact List…')),
        ('win.block-contact-', _('Block Contact…')),
        ('win.start-call-', _('Start Call…')),
        ('win.information-', _('Information')),
        ('app.browse-history', _('History')),
    ]

    def build_chatstate_menu():
        menu = Gio.Menu()
        entries = [
            (_('Disabled'), 'disabled'),
            (_('Composing Only'), 'composing_only'),
            (_('All Chat States'), 'all')
        ]

        for entry in entries:
            label, setting = entry
            action = 'win.send-chatstate-%s::%s' % (control_id, setting)
            menu.append(label, action)
        return menu

    def build_menu(preset):
        menu = Gio.Menu()
        for item in preset:
            if isinstance(item[1], str):
                action_name, label = item
                if action_name == 'win.send-marker-' and type_ == 'pm':
                    continue

                if action_name == 'app.browse-history':
                    menuitem = Gio.MenuItem.new(label, action_name)
                    dict_ = {'account': GLib.Variant('s', account),
                             'jid': GLib.Variant('s', jid)}
                    variant_dict = GLib.Variant('a{sv}', dict_)
                    menuitem.set_action_and_target_value(action_name,
                                                         variant_dict)
                    menu.append_item(menuitem)
                else:
                    menu.append(label, action_name + control_id)
            else:
                label, sub_menu = item
                if 'chatstate' in sub_menu:
                    submenu = build_chatstate_menu()
                else:
                    submenu = build_menu(sub_menu)
                menu.append_submenu(label, submenu)
        return menu

    return build_menu(singlechat_menu)


def get_groupchat_menu(control_id, account, jid):
    groupchat_menu = [
        ('win.information-', _('Information')),
        ('win.groupchat-settings-', _('Settings…')),
        (_('Manage Group Chat'), [
            ('win.rename-groupchat-', _('Rename…')),
            ('win.change-subject-', _('Change Subject…')),
            ('win.upload-avatar-', _('Upload Avatar…')),
            ('win.configure-', _('Configure…')),
            ('win.destroy-', _('Destroy…')),
        ]),
        ('win.change-nickname-', _('Change Nickname…')),
        ('win.request-voice-', _('Request Voice')),
        ('win.execute-command-', _('Execute Command…')),
        ('app.browse-history', _('History')),
        ('win.disconnect-', _('Leave')),
    ]

    def build_menu(preset):
        menu = Gio.Menu()
        for item in preset:
            if isinstance(item[1], str):
                action_name, label = item
                if action_name == 'app.browse-history':
                    menuitem = Gio.MenuItem.new(label, action_name)
                    dict_ = {'account': GLib.Variant('s', account),
                             'jid': GLib.Variant('s', jid)}
                    variant_dict = GLib.Variant('a{sv}', dict_)
                    menuitem.set_action_and_target_value(action_name,
                                                         variant_dict)
                    menu.append_item(menuitem)

                elif action_name == 'win.execute-command-':
                    action_name = action_name + control_id
                    menuitem = Gio.MenuItem.new(label, action_name)
                    menuitem.set_action_and_target_value(action_name,
                                                         GLib.Variant('s', ''))
                    menu.append_item(menuitem)
                else:
                    menu.append(label, action_name + control_id)
            else:
                label, sub_menu = item
                submenu = build_menu(sub_menu)
                menu.append_submenu(label, submenu)
        return menu

    return build_menu(groupchat_menu)


def get_account_menu(account):
    '''
    [(action, label/sub_menu)]
        action: string
        label: string
        sub menu: list
    '''
    account_menu = [
        ('-add-contact', _('Add Contact…')),
        ('-profile', _('Profile')),
        ('-start-single-chat', _('Send Single Message…')),
        ('-services', _('Discover Services…')),
        ('-server-info', _('Server Info')),
        (_('Advanced'), [
            ('-archive', _('Archiving Preferences')),
            ('-blocking', _('Blocking List')),
            ('-bookmarks', _('Bookmarks')),
            ('-pep-config', _('PEP Configuration')),
            ('-sync-history', _('Synchronise History…')),
        ]),
        (_('Admin'), [
            ('-send-server-message', _('Send Server Message…')),
            ('-set-motd', _('Set MOTD…')),
            ('-update-motd', _('Update MOTD…')),
            ('-delete-motd', _('Delete MOTD…'))
        ]),
    ]

    def build_menu(preset):
        menu = Gio.Menu()
        for item in preset:
            if isinstance(item[1], str):
                action, label = item
                action = 'app.{}{}'.format(account, action)
                menuitem = Gio.MenuItem.new(label, action)
                if 'add-contact' in action:
                    variant = GLib.Variant('as', [account, ''])
                else:
                    variant = GLib.Variant('s', account)
                menuitem.set_action_and_target_value(action, variant)
                menu.append_item(menuitem)
            else:
                label, sub_menu = item
                # This is a submenu
                submenu = build_menu(sub_menu)
                menu.append_submenu(label, submenu)
        return menu

    return build_menu(account_menu)


def build_accounts_menu():
    menubar = app.app.get_menubar()
    # Accounts Submenu
    menu_position = 1

    acc_menu = menubar.get_item_link(menu_position, 'submenu')
    acc_menu.remove_all()

    accounts_list = sorted(app.contacts.get_accounts())
    if not accounts_list:
        modify_account_item = Gio.MenuItem.new(_('_Add Account…'),
                                               'app.accounts::')
        acc_menu.append_item(modify_account_item)
        return

    if len(accounts_list) > 1:
        modify_account_item = Gio.MenuItem.new(_('_Modify Accounts…'),
                                               'app.accounts::')
        acc_menu.append_item(modify_account_item)
        for acc in accounts_list:
            label = escape_mnemonic(app.get_account_label(acc))
            if acc != 'Local':
                acc_menu.append_submenu(
                    label, get_account_menu(acc))
    else:
        acc_menu = get_account_menu(accounts_list[0])
        modify_account_item = Gio.MenuItem.new(_('_Modify Account…'),
                                               'app.accounts::')
        acc_menu.insert_item(0, modify_account_item)
        menubar.remove(menu_position)
        menubar.insert_submenu(menu_position, _('Accounts'), acc_menu)


def get_encryption_menu(control_id, control_type, zeroconf=False):
    menu = Gio.Menu()
    menu.append(
        _('Disabled'),
        'win.set-encryption-{}::{}'.format(control_id, 'disabled'))
    for name, plugin in app.plugin_manager.encryption_plugins.items():
        if control_type.is_groupchat:
            if not hasattr(plugin, 'allow_groupchat'):
                continue
        if control_type.is_privatechat:
            if not hasattr(plugin, 'allow_privatechat'):
                continue
        if zeroconf:
            if not hasattr(plugin, 'allow_zeroconf'):
                continue
        menu_action = 'win.set-encryption-{}::{}'.format(
            control_id, name)
        menu.append(name, menu_action)
    if menu.get_n_items() == 1:
        return None
    return menu


def get_conv_context_menu(account, uri):
    if uri.type == URIType.XMPP:
        if uri.action == URIAction.JOIN:
            context_menu = [
                ('copy-text', _('Copy XMPP Address')),
                ('groupchat-join', _('Join Groupchat')),
            ]
        else:
            context_menu = [
                ('copy-text', _('Copy XMPP Address')),
                ('-start-chat', _('Start Chat')),
                ('-add-contact', _('Add to Contact List…')),
            ]

    elif uri.type == URIType.WEB:
        context_menu = [
            ('copy-text', _('Copy Link Location')),
            ('open-link', _('Open Link in Browser')),
        ]

    elif uri.type == URIType.MAIL:
        context_menu = [
            ('copy-text', _('Copy Email Address')),
            ('open-mail', _('Open Email Composer')),
        ]

    elif uri.type == URIType.GEO:
        context_menu = [
            ('copy-text', _('Copy Location')),
            ('open-link', _('Show Location')),
        ]

    elif uri.type == URIType.AT:
        context_menu = [
            ('copy-text', _('Copy XMPP Address/Email')),
            ('open-mail', _('Open Email Composer')),
            ('-start-chat', _('Start Chat')),
            ('groupchat-join', _('Join Groupchat')),
            ('-add-contact', _('Add to Contact List…')),
        ]
    else:
        return

    menu = Gtk.Menu()
    for item in context_menu:
        action, label = item
        menuitem = Gtk.MenuItem()
        menuitem.set_label(label)

        if action.startswith('-'):
            action = 'app.%s%s' % (account, action)
        else:
            action = 'app.%s' % action
        menuitem.set_action_name(action)

        data = uri.data
        if uri.type == URIType.XMPP:
            data = uri.data['jid']

        if action in ('app.open-mail', 'app.copy-text'):
            value = GLib.Variant.new_string(data)
        else:
            value = GLib.Variant.new_strv([account, data])
        menuitem.set_action_target_value(value)
        menuitem.show()
        menu.append(menuitem)
    return menu


def get_groupchat_roster_menu(account, control_id, self_contact, contact):
    menu = Gtk.Menu()

    item = Gtk.MenuItem(label=_('Information'))
    action = 'win.contact-information-%s::%s' % (control_id, contact.name)
    item.set_detailed_action_name(action)
    menu.append(item)

    item = Gtk.MenuItem(label=_('Add to Contact List…'))
    action = 'app.{account}-add-contact(["{account}", "{jid}"])'.format(
        account=account, jid=contact.jid or '')
    if contact.jid is None:
        item.set_sensitive(False)
    else:
        item.set_detailed_action_name(action)
    menu.append(item)

    item = Gtk.MenuItem(label=_('Invite'))
    if contact.jid is not None:
        build_invite_submenu(item,
                             ((contact, account),),
                             show_bookmarked=True)
    else:
        item.set_sensitive(False)
    menu.append(item)

    item = Gtk.MenuItem(label=_('Execute Command…'))
    action = 'win.execute-command-%s::%s' % (control_id, contact.name)
    item.set_detailed_action_name(action)
    menu.append(item)

    menu.append(Gtk.SeparatorMenuItem())

    item = Gtk.MenuItem(label=_('Kick'))
    action = 'win.kick-%s::%s' % (control_id, contact.name)
    if is_role_change_allowed(self_contact, contact):
        item.set_detailed_action_name(action)
    else:
        item.set_sensitive(False)
    menu.append(item)

    item = Gtk.MenuItem(label=_('Ban'))
    action = 'win.ban-%s::%s' % (control_id, contact.jid or '')
    if is_affiliation_change_allowed(self_contact, contact, 'outcast'):
        item.set_detailed_action_name(action)
    else:
        item.set_sensitive(False)
    menu.append(item)

    menu.append(Gtk.SeparatorMenuItem())

    item = Gtk.MenuItem(label=_('Make Owner'))
    action = 'win.change-affiliation-%s(["%s", "owner"])' % (control_id,
                                                             contact.jid)
    if is_affiliation_change_allowed(self_contact, contact, 'owner'):
        item.set_detailed_action_name(action)
    else:
        item.set_sensitive(False)
    menu.append(item)

    item = Gtk.MenuItem(label=_('Make Admin'))
    action = 'win.change-affiliation-%s(["%s", "admin"])' % (control_id,
                                                             contact.jid)
    if is_affiliation_change_allowed(self_contact, contact, 'admin'):
        item.set_detailed_action_name(action)
    else:
        item.set_sensitive(False)
    menu.append(item)

    item = Gtk.MenuItem(label=_('Make Member'))
    action = 'win.change-affiliation-%s(["%s", "member"])' % (control_id,
                                                              contact.jid)
    if is_affiliation_change_allowed(self_contact, contact, 'member'):
        item.set_detailed_action_name(action)
    else:
        item.set_sensitive(False)
    menu.append(item)

    item = Gtk.MenuItem(label=_('Revoke Member'))
    action = 'win.change-affiliation-%s(["%s", "none"])' % (control_id,
                                                            contact.jid)
    if is_affiliation_change_allowed(self_contact, contact, 'none'):
        item.set_detailed_action_name(action)
    else:
        item.set_sensitive(False)
    menu.append(item)

    if contact.role.is_visitor:
        label = _('Grant Voice')
        role = 'participant'
    else:
        label = _('Revoke Voice')
        role = 'visitor'

    item = Gtk.MenuItem(label=label)
    action = 'win.change-role-%s(["%s", "%s"])' % (control_id,
                                                   contact.name,
                                                   role)
    if is_role_change_allowed(self_contact, contact):
        item.set_detailed_action_name(action)
    else:
        item.set_sensitive(False)
    menu.append(item)

    menu.show_all()
    return menu


class SearchMenu(Gtk.Menu):
    def __init__(self, treeview):
        Gtk.Menu.__init__(self)
        self._copy_item = Gtk.MenuItem(label=_('Copy'))
        self._copy_item.set_action_name('app.copy-text')
        self.set_copy_text('')
        self._copy_item.show()
        self.append(self._copy_item)
        self.attach_to_widget(treeview, None)

    def set_copy_text(self, text):
        self._copy_item.set_action_target_value(GLib.Variant('s', text))


def escape_mnemonic(label):
    if label is None:
        return
    # Underscore inside a label means the next letter is a keyboard
    # shortcut. To show an underscore we have to use double underscore
    return label.replace('_', '__')
