# 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/>.

import sys
import locale
import logging
from collections import defaultdict

from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GLib
from gi.repository import Pango
from gi.repository import GObject

from gajim.common import app
from gajim.common import passwords
from gajim.common import helpers
from gajim.common.i18n import _
from gajim.common.connection import Connection
from gajim.common.zeroconf.connection_zeroconf import ConnectionZeroconf

from gajim import gui_menu_builder

from gajim.gtk.settings import SettingsDialog
from gajim.gtk.settings import SettingsBox
from gajim.gtk.dialogs import ConfirmationDialog
from gajim.gtk.dialogs import ConfirmationDialogDoubleRadio
from gajim.gtk.dialogs import ErrorDialog
from gajim.gtk.dialogs import PassphraseDialog
from gajim.gtk.dialogs import YesNoDialog
from gajim.gtk.dialogs import DialogButton
from gajim.gtk.dialogs import NewConfirmationDialog
from gajim.gtk.util import get_builder
from gajim.gtk.const import Setting
from gajim.gtk.const import SettingKind
from gajim.gtk.const import SettingType


log = logging.getLogger('gajim.gtk.accounts')


class AccountsWindow(Gtk.ApplicationWindow):
    def __init__(self):
        Gtk.ApplicationWindow.__init__(self)
        self.set_application(app.app)
        self.set_position(Gtk.WindowPosition.CENTER)
        self.set_show_menubar(False)
        self.set_name('AccountsWindow')
        self.set_default_size(700, 550)
        self.set_resizable(True)
        self.set_title(_('Accounts'))
        self._need_relogin = {}
        self._accounts = {}

        self._menu = AccountMenu()
        self._settings = Settings()

        box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        box.add(self._menu)
        box.add(self._settings)
        self.add(box)

        for account in app.get_accounts_sorted():
            self.add_account(account, inital=True)

        self._menu.connect('menu-activated', self._on_menu_activated)
        self.connect('destroy', self._on_destroy)
        self.connect('key-press-event', self._on_key_press)

        self.show_all()

    def _on_menu_activated(self, _listbox, account, name):
        if name == 'back':
            self._settings.set_page('add-account')
            self._check_relogin()
        elif name == 'remove':
            self.on_remove_account(account)
        else:
            self._settings.set_page(name)

    def _on_key_press(self, _widget, event):
        if event.keyval == Gdk.KEY_Escape:
            self.destroy()

    def _on_destroy(self, *args):
        self._check_relogin()

    def update_account_label(self, account):
        self._accounts[account].update_account_label()

    def update_proxy_list(self):
        for account in self._accounts:
            self._settings.update_proxy_list(account)

    def _check_relogin(self):
        for account in self._need_relogin:
            settings = self._get_relogin_settings(account)
            active = app.config.get_per('accounts', account, 'active')
            if settings != self._need_relogin[account]:
                self._need_relogin[account] = settings
                if active:
                    self._relog(account)
                break

    def _relog(self, account):
        if app.connections[account].connected == 0:
            return

        if account == app.ZEROCONF_ACC_NAME:
            app.connections[app.ZEROCONF_ACC_NAME].update_details()
            return

        def login(account, show_before, status_before):
            """
            Login with previous status
            """
            # first make sure connection is really closed,
            # 0.5 may not be enough
            app.connections[account].disconnect(True)
            app.interface.roster.send_status(
                account, show_before, status_before)

        def relog(account):
            show_before = app.SHOW_LIST[app.connections[account].connected]
            status_before = app.connections[account].status
            app.interface.roster.send_status(
                account, 'offline', _('Be right back.'))
            GLib.timeout_add(500, login, account, show_before, status_before)

        YesNoDialog(
            _('Relogin now?'),
            _('If you want all the changes to apply instantly, '
              'you must relogin.'),
            transient_for=self,
            on_response_yes=lambda *args: relog(account))

    @staticmethod
    def _get_relogin_settings(account):
        if account == app.ZEROCONF_ACC_NAME:
            settings = ['zeroconf_first_name', 'zeroconf_last_name',
                        'zeroconf_jabber_id', 'zeroconf_email']
        else:
            settings = ['client_cert', 'proxy', 'resource',
                        'use_custom_host', 'custom_host', 'custom_port']

        values = []
        for setting in settings:
            values.append(app.config.get_per('accounts', account, setting))
        return values

    def on_remove_account(self, account):
        if app.events.get_events(account):
            app.interface.raise_dialog('unread-events-on-remove-account')
            return

        if app.config.get_per('accounts', account, 'is_zeroconf'):
            # Should never happen as button is insensitive
            return

        win_opened = False
        if app.interface.msg_win_mgr.get_controls(acct=account):
            win_opened = True
        elif account in app.interface.instances:
            for key in app.interface.instances[account]:
                if (app.interface.instances[account][key] and
                        key != 'remove_account'):
                    win_opened = True
                    break

        # Detect if we have opened windows for this account

        def remove(account):
            if (account in app.interface.instances and
                    'remove_account' in app.interface.instances[account]):
                dialog = app.interface.instances[account]['remove_account']
                dialog.window.present()
            else:
                if account not in app.interface.instances:
                    app.interface.instances[account] = {}
                app.interface.instances[account]['remove_account'] = \
                    RemoveAccountWindow(account)
        if win_opened:
            ConfirmationDialog(
                _('You have opened chat in account %s') % account,
                _('All chat and groupchat windows will be closed. '
                  'Do you want to continue?'),
                on_response_ok=(remove, account),
                transient_for=self)
        else:
            remove(account)

    def remove_account(self, account):
        del self._need_relogin[account]
        self._accounts[account].remove()

    def add_account(self, account, inital=False):
        self._need_relogin[account] = self._get_relogin_settings(account)
        self._accounts[account] = Account(account, self._menu, self._settings)
        if not inital:
            self._accounts[account].show()

    def select_account(self, account):
        try:
            self._accounts[account].select()
        except KeyError:
            log.warning('select_account() failed, account %s not found',
                        account)

    @staticmethod
    def enable_account(account):
        if account == app.ZEROCONF_ACC_NAME:
            app.connections[account] = ConnectionZeroconf(account)
        else:
            app.connections[account] = Connection(account)

        app.plugin_manager.register_modules_for_account(
            app.connections[account])

        # update variables
        app.interface.instances[account] = {
            'infos': {}, 'disco': {}, 'gc_config': {}, 'search': {},
            'online_dialog': {}, 'sub_request': {}}
        app.interface.minimized_controls[account] = {}
        app.connections[account].connected = 0
        app.groups[account] = {}
        app.contacts.add_account(account)
        app.gc_connected[account] = {}
        app.automatic_rooms[account] = {}
        app.newly_added[account] = []
        app.to_be_removed[account] = []
        if account == app.ZEROCONF_ACC_NAME:
            app.nicks[account] = app.ZEROCONF_ACC_NAME
        else:
            app.nicks[account] = app.config.get_per(
                'accounts', account, 'name')
        app.block_signed_in_notifications[account] = True
        app.sleeper_state[account] = 'off'
        app.last_message_time[account] = {}
        app.status_before_autoaway[account] = ''
        app.gajim_optional_features[account] = []
        app.caps_hash[account] = ''
        helpers.update_optional_features(account)
        # refresh roster
        if len(app.connections) >= 2:
            # Do not merge accounts if only one exists
            app.interface.roster.regroup = app.config.get('mergeaccounts')
        else:
            app.interface.roster.regroup = False
        app.interface.roster.setup_and_draw_roster()
        gui_menu_builder.build_accounts_menu()

    @staticmethod
    def disable_account(account):
        app.interface.roster.close_all(account)
        if account == app.ZEROCONF_ACC_NAME:
            app.connections[account].disable_account()
        app.connections[account].cleanup()
        del app.connections[account]
        del app.interface.instances[account]
        del app.interface.minimized_controls[account]
        del app.nicks[account]
        del app.block_signed_in_notifications[account]
        del app.groups[account]
        app.contacts.remove_account(account)
        del app.gc_connected[account]
        del app.automatic_rooms[account]
        del app.to_be_removed[account]
        del app.newly_added[account]
        del app.sleeper_state[account]
        del app.last_message_time[account]
        del app.status_before_autoaway[account]
        del app.gajim_optional_features[account]
        del app.caps_hash[account]
        if len(app.connections) >= 2:
            # Do not merge accounts if only one exists
            app.interface.roster.regroup = app.config.get('mergeaccounts')
        else:
            app.interface.roster.regroup = False
        app.config.set_per(
            'accounts', account, 'roster_version', '')
        app.interface.roster.setup_and_draw_roster()
        gui_menu_builder.build_accounts_menu()


class Settings(Gtk.ScrolledWindow):
    def __init__(self):
        Gtk.ScrolledWindow.__init__(self)
        self.set_hexpand(True)
        self.set_vexpand(True)
        self.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
        self._stack = Gtk.Stack()
        self._stack.set_transition_type(Gtk.StackTransitionType.CROSSFADE)
        self._stack.add_named(AddNewAccountPage(), 'add-account')
        self.get_style_context().add_class('accounts-settings')

        self.add(self._stack)
        self._page_signal_ids = {}
        self._pages = defaultdict(list)

    def add_page(self, page):
        self._pages[page.account].append(page)
        self._stack.add_named(page, '%s-%s' % (page.account, page.name))
        self._page_signal_ids[page] = page.connect_signal(self._stack)

    def set_page(self, name):
        self._stack.set_visible_child_name(name)

    def remove_account(self, account):
        for page in self._pages[account]:
            signal_id = self._page_signal_ids[page]
            del self._page_signal_ids[page]
            self._stack.disconnect(signal_id)
            self._stack.remove(page)
            page.destroy()
        del self._pages[account]

    def update_proxy_list(self, account):
        for page in self._pages[account]:
            if page.name != 'connection':
                continue
            page.listbox.get_setting('proxy').update_values()


class AccountMenu(Gtk.Box):

    __gsignals__ = {
        'menu-activated': (GObject.SignalFlags.RUN_FIRST, None, (str, str)),
    }

    def __init__(self):
        Gtk.Box.__init__(self)
        self.set_hexpand(False)
        self.set_size_request(160, -1)

        self.get_style_context().add_class('accounts-menu')

        self._stack = Gtk.Stack()
        self._stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT)

        self._accounts_listbox = Gtk.ListBox()
        self._accounts_listbox.set_sort_func(self._sort_func)
        self._accounts_listbox.get_style_context().add_class('settings-box')
        self._accounts_listbox.connect('row-activated',
                                       self._on_account_row_activated)

        scrolled = Gtk.ScrolledWindow()
        scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
        scrolled.add(self._accounts_listbox)
        self._stack.add_named(scrolled, 'accounts')
        self.add(self._stack)

    @staticmethod
    def _sort_func(row1, row2):
        if row1.label == 'Local':
            return -1
        return locale.strcoll(row1.label.lower(), row2.label.lower())

    def add_account(self, row):
        self._accounts_listbox.add(row)
        sub_menu = AccountSubMenu(row.account)
        self._stack.add_named(sub_menu, '%s-menu' % row.account)

        sub_menu.connect('row-activated', self._on_sub_menu_row_activated)

    def remove_account(self, row):
        if self._stack.get_visible_child_name() != 'accounts':
            # activate 'back' button
            self._stack.get_visible_child().get_row_at_index(1).emit('activate')
        self._accounts_listbox.remove(row)
        sub_menu = self._stack.get_child_by_name('%s-menu' % row.account)
        self._stack.remove(sub_menu)
        row.destroy()
        sub_menu.destroy()

    def _on_account_row_activated(self, _listbox, row):
        self._stack.set_visible_child_name('%s-menu' % row.account)
        self._stack.get_visible_child().get_row_at_index(2).emit('activate')

    def _on_sub_menu_row_activated(self, listbox, row):
        if row.name == 'back':
            self._stack.set_visible_child_full(
                'accounts', Gtk.StackTransitionType.OVER_RIGHT)

        if row.name in ('back', 'remove'):
            self.emit('menu-activated', listbox.account, row.name)

        else:
            self.emit('menu-activated',
                      listbox.account,
                      '%s-%s' % (listbox.account, row.name))

    def update_account_label(self, account):
        self._accounts_listbox.invalidate_sort()
        sub_menu = self._stack.get_child_by_name('%s-menu' % account)
        sub_menu.update()


class AccountSubMenu(Gtk.ListBox):

    __gsignals__ = {
        'update': (GObject.SignalFlags.RUN_FIRST, None, (str,))
    }

    def __init__(self, account):
        Gtk.ListBox.__init__(self)
        self.set_vexpand(True)
        self.set_hexpand(True)
        self.get_style_context().add_class('settings-box')

        self._account = account

        self.add(AccountLabelMenuItem(self, self._account))
        self.add(BackMenuItem())
        self.add(PageMenuItem('general', _('General')))
        if account != 'Local':
            self.add(PageMenuItem('privacy', _('Privacy')))
            self.add(PageMenuItem('connection', _('Connection')))
            self.add(RemoveMenuItem())

    @property
    def account(self):
        return self._account

    def update(self):
        self.emit('update', self._account)


class MenuItem(Gtk.ListBoxRow):
    def __init__(self, name):
        Gtk.ListBoxRow.__init__(self)
        self._name = name
        self._box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL,
                            spacing=12)
        self._label = Gtk.Label()

        self.add(self._box)

    @property
    def name(self):
        return self._name


class RemoveMenuItem(MenuItem):
    def __init__(self):
        super().__init__('remove')
        self._label.set_text(_('Remove'))
        image = Gtk.Image.new_from_icon_name('user-trash-symbolic',
                                             Gtk.IconSize.MENU)

        self.set_selectable(False)
        image.get_style_context().add_class('error-color')

        self._box.add(image)
        self._box.add(self._label)


class AccountLabelMenuItem(MenuItem):
    def __init__(self, parent, account):
        super().__init__('account-label')
        self._update_account_label(parent, account)

        self.set_selectable(False)
        self.set_sensitive(False)
        self.set_activatable(False)

        image = Gtk.Image.new_from_icon_name('avatar-default-symbolic',
                                             Gtk.IconSize.MENU)
        image.get_style_context().add_class('insensitive-fg-color')

        self._label.get_style_context().add_class('accounts-label-row')
        self._label.set_ellipsize(Pango.EllipsizeMode.END)
        self._label.set_xalign(0)

        self._box.add(image)
        self._box.add(self._label)

        parent.connect('update', self._update_account_label)

    def _update_account_label(self, _listbox, account):
        account_label = app.get_account_label(account)
        self._label.set_text(account_label)


class BackMenuItem(MenuItem):
    def __init__(self):
        super().__init__('back')
        self.set_selectable(False)

        self._label.set_text(_('Back'))

        image = Gtk.Image.new_from_icon_name('go-previous-symbolic',
                                             Gtk.IconSize.MENU)
        image.get_style_context().add_class('insensitive-fg-color')

        self._box.add(image)
        self._box.add(self._label)


class PageMenuItem(MenuItem):
    def __init__(self, name, label):
        super().__init__(name)

        if name == 'general':
            icon = 'preferences-system-symbolic'
        elif name == 'privacy':
            icon = 'preferences-system-privacy-symbolic'
        elif name == 'connection':
            icon = 'preferences-system-network-symbolic'
        else:
            icon = 'dialog-error-symbolic'

        image = Gtk.Image.new_from_icon_name(icon, Gtk.IconSize.MENU)
        self._label.set_text(label)

        self._box.add(image)
        self._box.add(self._label)


class Account:
    def __init__(self, account, menu, settings):
        self._account = account
        self._menu = menu
        self._settings = settings

        if account == app.ZEROCONF_ACC_NAME:
            self._settings.add_page(ZeroConfPage(account))
        else:
            self._settings.add_page(GeneralPage(account))
            self._settings.add_page(ConnectionPage(account))
            self._settings.add_page(PrivacyPage(account))

        self._account_row = AccountRow(account)
        self._menu.add_account(self._account_row)

    def select(self):
        self._account_row.emit('activate')

    def show(self):
        self._menu.show_all()
        self._settings.show_all()
        self.select()

    def remove(self):
        self._menu.remove_account(self._account_row)
        self._settings.remove_account(self._account)

    def update_account_label(self):
        self._account_row.update_account_label()
        self._menu.update_account_label(self._account)

    @property
    def menu(self):
        return self._menu

    @property
    def account(self):
        return self._account

    @property
    def settings(self):
        return self._account


class AccountRow(Gtk.ListBoxRow):
    def __init__(self, account):
        Gtk.ListBoxRow.__init__(self)
        self.set_selectable(False)

        box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=12)

        self._account = account

        self._label = Gtk.Label(label=app.get_account_label(account))
        self._label.set_halign(Gtk.Align.START)
        self._label.set_hexpand(True)
        self._label.set_ellipsize(Pango.EllipsizeMode.END)
        self._label.set_xalign(0)
        self._label.set_width_chars(18)

        next_icon = Gtk.Image.new_from_icon_name('go-next-symbolic',
                                                 Gtk.IconSize.MENU)
        next_icon.get_style_context().add_class('insensitive-fg-color')

        switch = Gtk.Switch()
        switch.set_active(
            app.config.get_per('accounts', self._account, 'active'))
        switch.set_vexpand(False)

        if (self._account == app.ZEROCONF_ACC_NAME and
                not app.is_installed('ZEROCONF')):
            switch.set_active(False)
            self.set_activatable(False)
            self.set_sensitive(False)
            if sys.platform in ('win32', 'darwin'):
                tooltip = _('Please check if Bonjour is installed.')
            else:
                tooltip = _('Please check if Avahi is installed.')
            self.set_tooltip_text(tooltip)

        switch.connect('state-set', self._on_enable_switch, self._account)

        box.add(switch)
        box.add(self._label)
        box.add(next_icon)
        self.add(box)

    @property
    def account(self):
        return self._account

    @property
    def label(self):
        return self._label.get_text()

    def update_account_label(self):
        self._label.set_text(app.get_account_label(self._account))

    def _on_enable_switch(self, switch, state, account):
        def _disable():
            app.connections[account].change_status('offline', 'offline')
            app.connections[account].disconnect(reconnect=False)
            self.get_toplevel().disable_account(account)
            app.config.set_per('accounts', account, 'active', False)
            switch.set_state(state)

        old_state = app.config.get_per('accounts', account, 'active')
        if old_state == state:
            return Gdk.EVENT_PROPAGATE

        if (account in app.connections and
                app.connections[account].connected > 0):
            # Connecting or connected
            NewConfirmationDialog(
                _('Disable Account'),
                _('Account %s is still connected') % account,
                _('All chat and group chat windows will be closed. '
                  'Do you want to continue?'),
                [DialogButton.make('Cancel',
                                   callback=lambda: switch.set_active(True)),
                 DialogButton.make('Remove',
                                   text=_('Disable Account'),
                                   callback=_disable)],
                transient_for=self.get_toplevel()).show()
            return Gdk.EVENT_STOP

        if state:
            self.get_toplevel().enable_account(account)
        else:
            self.get_toplevel().disable_account(account)
        app.config.set_per('accounts', account, 'active', state)

        return Gdk.EVENT_PROPAGATE


class AddNewAccountPage(Gtk.Box):
    def __init__(self):
        Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL)
        self.set_vexpand(True)
        self.set_hexpand(True)
        button = Gtk.Button(label='Add Account')
        button.get_style_context().add_class('suggested-action')
        button.set_action_name('app.add-account')
        button.set_halign(Gtk.Align.CENTER)
        button.set_valign(Gtk.Align.CENTER)
        self.add(button)


class GenericSettingPage(Gtk.Box):
    def __init__(self, account, settings):
        Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL, spacing=12)
        self.set_valign(Gtk.Align.START)
        self.set_vexpand(True)
        self.account = account

        self.listbox = SettingsBox(account)
        self.listbox.get_style_context().add_class('accounts-settings-border')
        self.listbox.set_selection_mode(Gtk.SelectionMode.NONE)
        self.listbox.set_vexpand(False)
        self.listbox.set_valign(Gtk.Align.END)

        for setting in settings:
            self.listbox.add_setting(setting)
        self.listbox.update_states()

        self.pack_end(self.listbox, True, True, 0)

        self.listbox.connect('row-activated', self.on_row_activated)

    def connect_signal(self, stack):
        return stack.connect('notify::visible-child',
                             self._on_visible_child_changed)

    def _on_visible_child_changed(self, stack, param):
        if self == stack.get_visible_child():
            self.listbox.update_states()

    @staticmethod
    def on_row_activated(_listbox, row):
        row.on_row_activated()

    def set_entry_text(self, toggle, update=False):
        account_label = app.get_account_label(self.account)
        if update:
            self.entry.set_text(account_label)
            return
        if toggle.get_active():
            self.entry.set_sensitive(True)
            self.entry.grab_focus()
        else:
            self.entry.set_sensitive(False)
            value = self.entry.get_text()
            if not value:
                value = account_label
            app.config.set_per('accounts', self.account,
                               'account_label', value or self.account)
            if app.config.get_per('accounts', self.account, 'active'):
                app.interface.roster.draw_account(self.account)
                gui_menu_builder.build_accounts_menu()


class GeneralPage(GenericSettingPage):

    name = 'general'

    def __init__(self, account):

        settings = [
            Setting(SettingKind.ENTRY, _('Label'),
                    SettingType.ACCOUNT_CONFIG, 'account_label',
                    callback=self._on_account_name_change),

            Setting(SettingKind.LOGIN, _('Login'), SettingType.DIALOG,
                    props={'dialog': LoginDialog}),

            Setting(SettingKind.ACTION, _('Import Contacts'),
                    SettingType.ACTION, '-import-contacts',
                    props={'account': account}),

            Setting(SettingKind.DIALOG, _('Client Certificate'),
                    SettingType.DIALOG, props={'dialog': CertificateDialog}),

            Setting(SettingKind.SWITCH, _('Connect on startup'),
                    SettingType.ACCOUNT_CONFIG, 'autoconnect'),

            Setting(SettingKind.SWITCH,
                    _('Save conversations for all contacts'),
                    SettingType.ACCOUNT_CONFIG, 'no_log_for',
                    desc=_('Store conversations on the harddrive')),

            Setting(SettingKind.SWITCH, _('Global Status'),
                    SettingType.ACCOUNT_CONFIG, 'sync_with_global_status',
                    desc=_('Synchronise the status of all accounts')),

            Setting(SettingKind.SWITCH, _('Use file transfer proxies'),
                    SettingType.ACCOUNT_CONFIG, 'use_ft_proxies'),
        ]
        GenericSettingPage.__init__(self, account, settings)

    def _on_account_name_change(self, *args):
        self.get_toplevel().update_account_label(self.account)


class PrivacyPage(GenericSettingPage):

    name = 'privacy'

    def __init__(self, account):

        settings = [
            Setting(SettingKind.SWITCH, _('Idle Time'),
                    SettingType.ACCOUNT_CONFIG, 'send_idle_time',
                    desc=_('Disclose the time of your last activity')),

            Setting(SettingKind.SWITCH, _('Local System Time'),
                    SettingType.ACCOUNT_CONFIG, 'send_time_info',
                    desc=_('Disclose the local system time of the '
                           'device Gajim runs on')),

            Setting(SettingKind.SWITCH, _('Client / Operating System'),
                    SettingType.ACCOUNT_CONFIG, 'send_os_info',
                    desc=_('Disclose informations about the client '
                           'and operating system you currently use')),

            Setting(SettingKind.SWITCH, _('Ignore Unknown Contacts'),
                    SettingType.ACCOUNT_CONFIG, 'ignore_unknown_contacts',
                    desc=_('Ignore everything from contacts not in your '
                           'Roster')),

            ]
        GenericSettingPage.__init__(self, account, settings)


class ConnectionPage(GenericSettingPage):

    name = 'connection'

    def __init__(self, account):

        settings = [
            Setting(SettingKind.SWITCH, 'HTTP_PROXY',
                    SettingType.ACCOUNT_CONFIG, 'use_env_http_proxy',
                    desc=_('Use environment variable')),

            Setting(SettingKind.PROXY, _('Proxy'),
                    SettingType.ACCOUNT_CONFIG, 'proxy', name='proxy'),

            Setting(SettingKind.SWITCH, _('Warn on insecure connection'),
                    SettingType.ACCOUNT_CONFIG,
                    'warn_when_insecure_ssl_connection'),

            Setting(SettingKind.SWITCH, _('Send keep-alive packets'),
                    SettingType.ACCOUNT_CONFIG, 'keep_alives_enabled'),

            Setting(SettingKind.HOSTNAME, _('Hostname'), SettingType.DIALOG,
                    desc=_('Manually set the hostname for the server'),
                    props={'dialog': CutstomHostnameDialog}),

            Setting(SettingKind.ENTRY, _('Resource'),
                    SettingType.ACCOUNT_CONFIG, 'resource'),

            Setting(SettingKind.PRIORITY, _('Priority'),
                    SettingType.DIALOG, props={'dialog': PriorityDialog}),
            ]

        GenericSettingPage.__init__(self, account, settings)


class ZeroConfPage(GenericSettingPage):

    name = 'general'

    def __init__(self, account):

        settings = [
            Setting(SettingKind.DIALOG, _('Profile'),
                    SettingType.DIALOG,
                    props={'dialog': ZeroconfProfileDialog}),

            Setting(SettingKind.SWITCH, _('Connect on startup'),
                    SettingType.ACCOUNT_CONFIG, 'autoconnect',
                    desc=_('Use environment variable')),

            Setting(SettingKind.SWITCH,
                    _('Save conversations for all contacts'),
                    SettingType.ACCOUNT_CONFIG, 'no_log_for',
                    desc=_('Store conversations on the harddrive')),

            Setting(SettingKind.SWITCH, _('Global Status'),
                    SettingType.ACCOUNT_CONFIG, 'sync_with_global_status',
                    desc=_('Synchronize the status of all accounts')),
            ]

        GenericSettingPage.__init__(self, account, settings)


class ZeroconfProfileDialog(SettingsDialog):
    def __init__(self, account, parent):

        settings = [
            Setting(SettingKind.ENTRY, _('First Name'),
                    SettingType.ACCOUNT_CONFIG, 'zeroconf_first_name'),

            Setting(SettingKind.ENTRY, _('Last Name'),
                    SettingType.ACCOUNT_CONFIG, 'zeroconf_last_name'),

            Setting(SettingKind.ENTRY, _('Jabber ID'),
                    SettingType.ACCOUNT_CONFIG, 'zeroconf_jabber_id'),

            Setting(SettingKind.ENTRY, _('Email'),
                    SettingType.ACCOUNT_CONFIG, 'zeroconf_email'),
            ]

        SettingsDialog.__init__(self, parent, _('Profile'),
                                Gtk.DialogFlags.MODAL, settings, account)


class PriorityDialog(SettingsDialog):
    def __init__(self, account, parent):

        neg_priority = app.config.get('enable_negative_priority')
        if neg_priority:
            range_ = (-128, 127)
        else:
            range_ = (0, 127)

        settings = [
            Setting(SettingKind.SWITCH, _('Adjust to status'),
                    SettingType.ACCOUNT_CONFIG, 'adjust_priority_with_status',
                    'adjust'),

            Setting(SettingKind.SPIN, _('Priority'),
                    SettingType.ACCOUNT_CONFIG, 'priority',
                    enabledif=('adjust', False), props={'range_': range_}),
            ]

        SettingsDialog.__init__(self, parent, _('Priority'),
                                Gtk.DialogFlags.MODAL, settings, account)

        self.connect('destroy', self.on_destroy)

    def on_destroy(self, *args):
        # Update priority
        if self.account not in app.connections:
            return
        show = app.SHOW_LIST[app.connections[self.account].connected]
        status = app.connections[self.account].status
        app.connections[self.account].change_status(show, status)


class CutstomHostnameDialog(SettingsDialog):
    def __init__(self, account, parent):

        settings = [
            Setting(SettingKind.SWITCH, _('Enable'),
                    SettingType.ACCOUNT_CONFIG,
                    'use_custom_host', name='custom'),

            Setting(SettingKind.ENTRY, _('Hostname'),
                    SettingType.ACCOUNT_CONFIG, 'custom_host',
                    enabledif=('custom', True)),

            Setting(SettingKind.ENTRY, _('Port'),
                    SettingType.ACCOUNT_CONFIG, 'custom_port',
                    enabledif=('custom', True)),
            ]

        SettingsDialog.__init__(self, parent, _('Connection Settings'),
                                Gtk.DialogFlags.MODAL, settings, account)


class CertificateDialog(SettingsDialog):
    def __init__(self, account, parent):

        settings = [
            Setting(SettingKind.FILECHOOSER, _('Client Certificate'),
                    SettingType.ACCOUNT_CONFIG, 'client_cert',
                    props={'filefilter': (_('PKCS12 Files'), '*.p12')}),

            Setting(SettingKind.SWITCH, _('Encrypted Certificate'),
                    SettingType.ACCOUNT_CONFIG, 'client_cert_encrypted'),
            ]

        SettingsDialog.__init__(self, parent, _('Certificate Settings'),
                                Gtk.DialogFlags.MODAL, settings, account)


class LoginDialog(SettingsDialog):
    def __init__(self, account, parent):

        settings = [
            Setting(SettingKind.ENTRY, _('Password'),
                    SettingType.ACCOUNT_CONFIG, 'password', name='password',
                    enabledif=('savepass', True)),

            Setting(SettingKind.SWITCH, _('Save Password'),
                    SettingType.ACCOUNT_CONFIG, 'savepass', name='savepass'),

            Setting(SettingKind.CHANGEPASSWORD, _('Change Password'),
                    SettingType.DIALOG, callback=self.on_password_change,
                    props={'dialog': None}),
            ]

        SettingsDialog.__init__(self, parent, _('Login Settings'),
                                Gtk.DialogFlags.MODAL, settings, account)

        self.connect('destroy', self.on_destroy)

    def on_password_change(self, new_password, _data):
        passwords.save_password(self.account, new_password)

    def on_destroy(self, *args):
        savepass = app.config.get_per('accounts', self.account, 'savepass')
        if not savepass:
            passwords.delete_password(self.account)


class RemoveAccountWindow:
    """
    Ask whether to remove from gajim only or both from gajim and the server,
    then remove the account given
    """

    def on_remove_account_window_destroy(self, _widget):
        if self.account in app.interface.instances:
            del app.interface.instances[self.account]['remove_account']

    def on_cancel_button_clicked(self, _widget):
        self._ui.remove_account_window.destroy()

    def __init__(self, account):
        self.account = account
        self._ui = get_builder('remove_account_window.ui')
        active_window = app.app.get_active_window()
        self._ui.remove_account_window.set_transient_for(active_window)
        self._ui.remove_account_window.set_title(
            _('Removing account %s') % self.account)
        self._ui.connect_signals(self)
        self._ui.remove_account_window.show_all()

    def on_remove_button_clicked(self, _widget):
        def remove():
            if self.account in app.connections and \
            app.connections[self.account].connected and \
            not self._ui.remove_and_unregister_radiobutton.get_active():
                # change status to offline only if we will not
                # remove this JID from server
                app.connections[self.account].change_status('offline', 'offline')
            if self._ui.remove_and_unregister_radiobutton.get_active():
                if not self.account in app.connections:
                    ErrorDialog(
                        _('Account is disabled'),
                        _('To unregister from a server, the account must be '
                          'enabled.'),
                        transient_for=self._ui.remove_account_window)
                    return
                if not app.connections[self.account].password:
                    def on_ok(passphrase, _checked):
                        if passphrase == -1:
                            # We don't remove account cause we
                            # canceled pw window
                            return
                        app.connections[self.account].password = passphrase
                        app.connections[self.account].unregister_account(
                            self._on_remove_success)

                    PassphraseDialog(
                        _('Password required'),
                        _('Enter your password for account %s') % self.account,
                        _('Save password'), ok_handler=on_ok,
                        transient_for=self._ui.remove_account_window)
                    return
                app.connections[self.account].unregister_account(
                    self._on_remove_success)
            else:
                self._on_remove_success(True)

        if self.account in app.connections and \
        app.connections[self.account].connected:
            ConfirmationDialog(
                _('Account "%s" is connected to the server') % self.account,
                _('If you remove it, the connection will be lost.'),
                on_response_ok=remove,
                transient_for=self._ui.remove_account_window)
        else:
            remove()

    def on_remove_response_ok(self, is_checked):
        if is_checked[0]:
            self._on_remove_success(True)

    def _on_remove_success(self, res):
        # action of unregistration has failed, we don't remove the account
        # Error message is send by connect_and_auth()
        if not res:
            ConfirmationDialogDoubleRadio(
                _('Connection to server %s failed') % self.account,
                _('What would you like to do?'),
                _('Remove only from Gajim'),
                _('Don\'t remove anything. I\'ll try again later'),
                on_response_ok=self.on_remove_response_ok, is_modal=False,
                transient_for=self._ui.remove_account_window)
            return
        # Close all opened windows
        app.interface.roster.close_all(self.account, force=True)
        if self.account in app.connections:
            app.connections[self.account].disconnect(reconnect=False)
            app.connections[self.account].cleanup()
            del app.connections[self.account]
        app.logger.remove_roster(app.get_jid_from_account(self.account))
        # Delete password must be before del_per() because it calls set_per()
        # which would recreate the account with defaults values if not found
        passwords.delete_password(self.account)
        app.config.del_per('accounts', self.account)
        del app.interface.instances[self.account]
        if self.account in app.nicks:
            del app.interface.minimized_controls[self.account]
            del app.nicks[self.account]
            del app.block_signed_in_notifications[self.account]
            del app.groups[self.account]
            app.contacts.remove_account(self.account)
            del app.gc_connected[self.account]
            del app.automatic_rooms[self.account]
            del app.to_be_removed[self.account]
            del app.newly_added[self.account]
            del app.sleeper_state[self.account]
            del app.last_message_time[self.account]
            del app.status_before_autoaway[self.account]
            del app.gajim_optional_features[self.account]
            del app.caps_hash[self.account]
        if len(app.connections) >= 2: # Do not merge accounts if only one exists
            app.interface.roster.regroup = app.config.get('mergeaccounts')
        else:
            app.interface.roster.regroup = False
        app.interface.roster.setup_and_draw_roster()
        app.app.remove_account_actions(self.account)
        gui_menu_builder.build_accounts_menu()

        window = app.get_app_window('AccountsWindow')
        if window is not None:
            window.remove_account(self.account)
        self._ui.remove_account_window.destroy()

    def destroy(self):
        self._ui.remove_account_window.destroy()