# 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 logging
import sys

from gi.repository import Gtk
from gi.repository import Gdk

from gajim.common import app
from gajim.common import configpaths
from gajim.common import helpers
from gajim.common.const import THRESHOLD_OPTIONS
from gajim.common.nec import NetworkEvent
from gajim.common.i18n import _
from gajim.common.helpers import open_file
from gajim.common.multimedia_helpers import AudioInputManager
from gajim.common.multimedia_helpers import AudioOutputManager
from gajim.common.multimedia_helpers import VideoInputManager

from gajim.chat_control_base import ChatControlBase

from .const import Setting
from .const import SettingKind
from .const import SettingType
from .const import ControlType
from .emoji_chooser import emoji_chooser
from .settings import SettingsBox
from .settings import SettingsDialog
from .sidebar_switcher import SideBarSwitcher
from .video_preview import VideoPreview
from .util import get_available_iconsets
from .util import open_window
from .util import get_app_window
from .util import get_builder

if app.is_installed('GSPELL'):
    from gi.repository import Gspell  # pylint: disable=ungrouped-imports

log = logging.getLogger('gajim.gui.preferences')


class Preferences(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('PreferencesWindow')
        self.set_default_size(900, 650)
        self.set_resizable(True)
        self.set_title(_('Preferences'))

        self._ui = get_builder('preferences.ui')

        self._video_preview = None
        self._prefs = {}

        side_bar_switcher = SideBarSwitcher()
        side_bar_switcher.set_stack(self._ui.stack)
        self._ui.grid.attach(side_bar_switcher, 0, 0, 1, 1)

        self.add(self._ui.grid)

        self._check_emoji_theme()

        prefs = [
            ('window_behaviour', WindowBehaviour),
            ('contact_list', ContactList),
            ('chats', Chats),
            ('group_chats', GroupChats),
            ('visual_notifications', VisualNotifications),
            ('sounds', Sounds),
            ('status_message', StatusMessage),
            ('automatic_status', AutomaticStatus),
            ('themes', Themes),
            ('emoji', Emoji),
            ('status_icon', StatusIcon),
            ('server', Server),
            ('audio', Audio),
            ('video', Video),
            ('miscellaneous', Miscellaneous),
            ('advanced', Advanced),
        ]

        self._add_prefs(prefs)
        self._add_video_preview()

        self._ui.audio_video_info_bar.set_revealed(not app.is_installed('AV'))

        self.connect('key-press-event', self._on_key_press)
        self._ui.connect_signals(self)

        self.show_all()
        if sys.platform not in ('win32', 'darwin'):
            self._ui.emoji.hide()

    def get_ui(self):
        return self._ui

    def _add_prefs(self, prefs):
        for ui_name, klass in prefs:
            pref_box = getattr(self._ui, ui_name)
            if ui_name == 'video' and sys.platform == 'win32':
                continue

            pref = klass(self)
            pref_box.add(pref)
            self._prefs[ui_name] = pref

    def _add_video_preview(self):
        if sys.platform == 'win32':
            return
        self._video_preview = VideoPreview()
        self._ui.video.add(self._video_preview.widget)

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

    def get_video_preview(self):
        return self._video_preview

    @staticmethod
    def _on_features_clicked(_widget, _response):
        open_window('Features')

    def update_theme_list(self):
        self._prefs['themes'].update_theme_list()

    def update_proxy_list(self):
        self._prefs['miscellaneous'].update_proxy_list()

    @staticmethod
    def get_all_controls():
        for ctrl in app.interface.msg_win_mgr.get_controls():
            yield ctrl
        for account in app.connections:
            for ctrl in app.interface.minimized_controls[account].values():
                yield ctrl

    @staticmethod
    def get_all_muc_controls():
        for ctrl in app.interface.msg_win_mgr.get_controls(
                ControlType.GROUPCHAT):
            yield ctrl
        for account in app.connections:
            for ctrl in app.interface.minimized_controls[account].values():
                yield ctrl

    @staticmethod
    def _check_emoji_theme():
        # Ensure selected emoji theme is valid
        emoji_themes = helpers.get_available_emoticon_themes()
        settings_theme = app.settings.get('emoticons_theme')
        if settings_theme not in emoji_themes:
            app.settings.set('emoticons_theme', 'font')


class PreferenceBox(SettingsBox):
    def __init__(self, settings):
        SettingsBox.__init__(self, None)
        self.get_style_context().add_class('settings-border')
        self.set_selection_mode(Gtk.SelectionMode.NONE)
        self.set_vexpand(False)
        self.set_valign(Gtk.Align.END)

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


class WindowBehaviour(PreferenceBox):
    def __init__(self, *args):
        win_layout_items = {
            'never': _('Detached contact list with detached chats'),
            'always': _('Detached contact list with single chat'),
            'always_with_roster': _('Single window for everything'),
            'peracct': _('Detached contact list with chats grouped by account'),
            'pertype': _('Detached contact list with chats grouped by type'),
        }

        roster_on_startup_items = {
            'always': _('Always'),
            'never': _('Never'),
            'last_state': _('Restore last state'),
        }

        tab_position_items = {
            'top': _('Top'),
            'bottom': _('Bottom'),
            'left': _('Left'),
            'Right': _('Right'),
        }

        settings = [
            Setting(SettingKind.POPOVER,
                    _('Window Layout'),
                    SettingType.CONFIG,
                    'one_message_window',
                    props={'entries': win_layout_items},
                    callback=self._on_win_layout_changed),

            Setting(SettingKind.POPOVER,
                    _('Contact List on Startup'),
                    SettingType.CONFIG,
                    'show_roster_on_startup',
                    props={'entries': roster_on_startup_items},
                    desc=_('Show contact list when starting Gajim')),

            Setting(SettingKind.SWITCH,
                    _('Quit on Close'),
                    SettingType.CONFIG,
                    'quit_on_roster_x_button',
                    desc=_('Quit when closing contact list')),

            Setting(SettingKind.POPOVER,
                    _('Tab Position'),
                    SettingType.CONFIG,
                    'tabs_position',
                    props={'entries': tab_position_items},
                    desc=_('Placement of chat window tabs'),
                    callback=self._on_win_layout_changed),
        ]

        PreferenceBox.__init__(self, settings)

    @staticmethod
    def _on_win_layout_changed(*args):
        app.interface.msg_win_mgr.reconfig()


class ContactList(PreferenceBox):
    def __init__(self, *args):

        settings = [
            Setting(SettingKind.SWITCH,
                    _('Merge Accounts'),
                    SettingType.CONFIG,
                    'mergeaccounts',
                    callback=self._on_merge_accounts),

            Setting(SettingKind.SWITCH,
                    _('Enable Metacontacts'),
                    SettingType.CONFIG,
                    'metacontacts_enabled'),

            Setting(SettingKind.SWITCH,
                    _('Show Avatars'),
                    SettingType.CONFIG,
                    'show_avatars_in_roster',
                    callback=self._on_show_avatar_in_roster_changed),

            Setting(SettingKind.SWITCH,
                    _('Show Status Message'),
                    SettingType.CONFIG,
                    'show_status_msgs_in_roster',
                    callback=self._on_show_status_in_roster),

            Setting(SettingKind.SWITCH,
                    _('Sort Contacts by Status'),
                    SettingType.CONFIG,
                    'sort_by_show_in_roster',
                    callback=self._on_sort_by_show_in_roster),

            Setting(SettingKind.SWITCH,
                    _('Show Mood'),
                    SettingType.CONFIG,
                    'show_mood_in_roster'),

            Setting(SettingKind.SWITCH,
                    _('Show Activity'),
                    SettingType.CONFIG,
                    'show_activity_in_roster'),

            Setting(SettingKind.SWITCH,
                    _('Show Tune'),
                    SettingType.CONFIG,
                    'show_tunes_in_roster'),

            Setting(SettingKind.SWITCH,
                    _('Show Location'),
                    SettingType.CONFIG,
                    'show_location_in_roster'),
        ]

        PreferenceBox.__init__(self, settings)

    @staticmethod
    def _on_merge_accounts(*args):
        app.app.activate_action('merge')

    @staticmethod
    def _on_show_avatar_in_roster_changed(*args):
        app.interface.roster.setup_and_draw_roster()

    @staticmethod
    def _on_show_status_in_roster(*args):
        app.interface.roster.setup_and_draw_roster()
        controls = get_app_window('Preferences').get_all_muc_controls()
        for ctrl in controls:
            ctrl.roster.draw_contacts()

    @staticmethod
    def _on_sort_by_show_in_roster(*args):
        app.interface.roster.setup_and_draw_roster()


class Chats(PreferenceBox):
    def __init__(self, *args):

        speller_desc = None
        if not app.is_installed('GSPELL'):
            speller_desc = _('Needs gspell to be installed')

        settings = [
            Setting(SettingKind.SWITCH,
                    _('Spell Checking'),
                    SettingType.CONFIG,
                    'use_speller',
                    desc=speller_desc,
                    enabled_func=self._speller_available,
                    callback=self._on_use_speller),

            Setting(SettingKind.SWITCH,
                    _('Message Receipts (✔)'),
                    SettingType.CONFIG,
                    'positive_184_ack',
                    desc=_('Add a checkmark to received messages')),

            Setting(SettingKind.SWITCH,
                    _('XHTML Formatting'),
                    SettingType.CONFIG,
                    'show_xhtml',
                    desc=_('Render XHTML styles (colors, etc.) of incoming '
                           'messages')),

            Setting(SettingKind.SWITCH,
                    _('Show Send Message Button'),
                    SettingType.CONFIG,
                    'show_send_message_button'),

            Setting(SettingKind.SWITCH,
                    _('Show Status Message'),
                    SettingType.CONFIG,
                    'print_status_in_chats'),

            Setting(SettingKind.SWITCH,
                    _('Show Chat State In Tabs'),
                    SettingType.CONFIG,
                    'show_chatstate_in_tabs',
                    desc=_('Show the contact’s chat state (e.g. typing) in '
                           'the chat’s tab')),

            Setting(SettingKind.SWITCH,
                    _('Show Chat State In Banner'),
                    SettingType.CONFIG,
                    'show_chatstate_in_banner',
                    desc=_('Show the contact’s chat state (e.g. typing) in '
                           'the chats tab’s banner')),

            Setting(SettingKind.SWITCH,
                    _('Display Chat State In Contact List'),
                    SettingType.CONFIG,
                    'show_chatstate_in_roster',
                    desc=_('Show the contact’s chat state (e.g. typing) in '
                           'the contact list')),
        ]

        PreferenceBox.__init__(self, settings)

    @staticmethod
    def _speller_available():
        return app.is_installed('GSPELL')

    @staticmethod
    def _on_use_speller(value, *args):
        if not value:
            return

        lang = app.settings.get('speller_language')
        gspell_lang = Gspell.language_lookup(lang)
        if gspell_lang is None:
            gspell_lang = Gspell.language_get_default()
        app.settings.set('speller_language', gspell_lang.get_code())
        for ctrl in get_app_window('Preferences').get_all_controls():
            if isinstance(ctrl, ChatControlBase):
                ctrl.set_speller()


class GroupChats(PreferenceBox):
    def __init__(self, *args):

        settings = [

            Setting(SettingKind.SWITCH,
                    _('Show Subject'),
                    SettingType.CONFIG,
                    'show_subject_on_join'),

            Setting(SettingKind.SWITCH,
                    _('Sort Contacts by Status'),
                    SettingType.CONFIG,
                    'sort_by_show_in_muc',
                    callback=self._on_sort_by_show_in_muc),

            Setting(SettingKind.POPOVER,
                    _('Default Sync Threshold'),
                    SettingType.CONFIG,
                    'gc_sync_threshold_public_default',
                    desc=_('Default for new public group chats'),
                    props={'entries': THRESHOLD_OPTIONS}),

            Setting(SettingKind.SWITCH,
                    _('Direct Messages'),
                    SettingType.CONFIG,
                    'muc_prefer_direct_msg',
                    desc=_('Prefer direct messages in private group chats ')),

            Setting(SettingKind.SWITCH,
                    _('Show Joined / Left'),
                    SettingType.CONFIG,
                    'gc_print_join_left_default',
                    desc=_('Default for new group chats'),
                    props={'button-text':_('Reset'),
                           'button-tooltip': _('Reset all group chats to the '
                                               'current default value'),
                           'button-style': 'destructive-action',
                           'button-callback': self._reset_join_left}),

            Setting(SettingKind.SWITCH,
                    _('Show Status Changes'),
                    SettingType.CONFIG,
                    'gc_print_status_default',
                    desc=_('Default for new group chats'),
                    props={'button-text':_('Reset'),
                           'button-tooltip': _('Reset all group chats to the '
                                               'current default value'),
                           'button-style': 'destructive-action',
                           'button-callback': self._reset_print_status}),

        ]

        PreferenceBox.__init__(self, settings)

    @staticmethod
    def _on_sort_by_show_in_muc(*args):
        for ctrl in get_app_window('Preferences').get_all_muc_controls():
            ctrl.roster.invalidate_sort()

    @staticmethod
    def _reset_join_left(button):
        button.set_sensitive(False)
        app.settings.set_group_chat_settings('print_join_left', None)

    @staticmethod
    def _reset_print_status(button):
        button.set_sensitive(False)
        app.settings.set_group_chat_settings('print_status', None)


class VisualNotifications(PreferenceBox):
    def __init__(self, *args):
        trayicon_items = {
            'never': _('Hide icon'),
            'on_event': _('Only show for pending events'),
            'always': _('Always show icon'),
        }

        settings = [
            Setting(SettingKind.POPOVER,
                    _('Notification Area Icon'),
                    SettingType.CONFIG,
                    'trayicon',
                    props={'entries': trayicon_items},
                    callback=self._on_trayicon),

            Setting(SettingKind.SWITCH,
                    _('Open Events'),
                    SettingType.CONFIG,
                    'autopopup',
                    desc=_('Open events instead of showing a notification '
                           'in the contact list')),

            Setting(SettingKind.NOTIFICATIONS,
                    _('Show Notifications'),
                    SettingType.DIALOG,
                    props={'dialog': NotificationsDialog}),

        ]

        PreferenceBox.__init__(self, settings)

    @staticmethod
    def _on_trayicon(value, *args):
        if value == 'never':
            app.interface.hide_systray()
        elif value == 'on_event':
            app.interface.show_systray()
        else:
            app.interface.show_systray()


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

        settings = [
            Setting(SettingKind.SWITCH,
                    _('Show Notifications'),
                    SettingType.CONFIG,
                    'show_notifications'),

            Setting(SettingKind.SWITCH,
                    _('Notifications When Away'),
                    SettingType.CONFIG,
                    'autopopupaway',
                    desc=_('Show notifications even if you are Away, '
                           'Busy, etc.'),
                    bind='show_notifications'),
            ]

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


class Sounds(PreferenceBox):
    def __init__(self, *args):

        settings = [
            Setting(SettingKind.SWITCH,
                    _('Play Sounds'),
                    SettingType.CONFIG,
                    'sounds_on',
                    desc=_('Play sounds to notify about events'),
                    props={'button-icon-name': 'preferences-system-symbolic',
                           'button-callback': self._on_manage_sounds}),

            Setting(SettingKind.SWITCH,
                    _('Sounds When Away'),
                    SettingType.CONFIG,
                    'sounddnd',
                    desc=_('Play sounds even when you are Away, Busy, etc.'),
                    bind='sounds_on'),
        ]

        PreferenceBox.__init__(self, settings)

    def _on_manage_sounds(self, *args):
        open_window('ManageSounds', transient_for=self.get_toplevel())


class StatusMessage(PreferenceBox):
    def __init__(self, *args):

        settings = [
            Setting(SettingKind.SWITCH,
                    _('Sign In'),
                    SettingType.CONFIG,
                    'ask_online_status'),

            Setting(SettingKind.SWITCH,
                    _('Sign Out'),
                    SettingType.CONFIG,
                    'ask_offline_status'),

            Setting(SettingKind.SWITCH,
                    _('Status Change'),
                    SettingType.CONFIG,
                    'always_ask_for_status_message'),
        ]

        PreferenceBox.__init__(self, settings)


class AutomaticStatus(PreferenceBox):
    def __init__(self, *args):

        settings = [
            Setting(SettingKind.AUTO_AWAY,
                    _('Auto Away'),
                    SettingType.DIALOG,
                    desc=_('Change your status to \'Away\' after a certain '
                           'amount of time'),
                    props={'dialog': AutoAwayDialog}),

            Setting(SettingKind.AUTO_EXTENDED_AWAY,
                    _('Auto Not Available'),
                    SettingType.DIALOG,
                    desc=_('Change your status to \'Not Available\' after a '
                           'certain amount of time'),
                    props={'dialog': AutoExtendedAwayDialog}),

        ]

        PreferenceBox.__init__(self, settings)

    @staticmethod
    def _get_auto_away():
        return app.settings.get('autoaway')

    @staticmethod
    def _get_auto_xa():
        return app.settings.get('autoxa')


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

        settings = [
            Setting(SettingKind.SWITCH,
                    _('Auto Away'),
                    SettingType.CONFIG,
                    'autoaway'),

            Setting(SettingKind.SPIN,
                    _('Time Until Away'),
                    SettingType.CONFIG,
                    'autoawaytime',
                    desc=_('Minutes until your status gets changed'),
                    props={'range_': (1, 720)},
                    bind='autoaway'),

            Setting(SettingKind.ENTRY,
                    _('Status Message'),
                    SettingType.CONFIG,
                    'autoaway_message',
                    bind='autoaway'),
            ]

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


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

        settings = [
            Setting(SettingKind.SWITCH,
                    _('Auto Not Available'),
                    SettingType.CONFIG,
                    'autoxa'),

            Setting(SettingKind.SPIN,
                    _('Time Until Not Available'),
                    SettingType.CONFIG,
                    'autoxatime',
                    desc=_('Minutes until your status gets changed'),
                    props={'range_': (1, 720)},
                    bind='autoxa'),

            Setting(SettingKind.ENTRY,
                    _('Status Message'),
                    SettingType.CONFIG,
                    'autoxa_message',
                    bind='autoxa'),
            ]

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


class Themes(PreferenceBox):
    def __init__(self, *args):

        theme_items = self._get_theme_items()

        dark_theme_items = {
            0: _('Disabled'),
            1: _('Enabled'),
            2: _('System'),
        }

        settings = [
            Setting(SettingKind.POPOVER,
                    _('Dark Theme'),
                    SettingType.CONFIG,
                    'dark_theme',
                    props={'entries': dark_theme_items},
                    callback=self._on_dark_theme),

            Setting(SettingKind.POPOVER,
                    _('Theme'),
                    SettingType.CONFIG,
                    'roster_theme',
                    name='roster_theme',
                    props={'entries': theme_items,
                           'button-icon-name': 'preferences-system-symbolic',
                           'button-callback': self._on_edit_themes},
                    callback=self._on_theme_changed),
        ]

        PreferenceBox.__init__(self, settings)

    @staticmethod
    def _get_theme_items():
        theme_items = ['default']
        for settings_theme in app.css_config.themes:
            theme_items.append(settings_theme)
        return theme_items

    def update_theme_list(self):
        self.get_setting('roster_theme').update_entries(self._get_theme_items())

    def _on_edit_themes(self, *args):
        open_window('Themes', transient=self.get_toplevel())

    @staticmethod
    def _on_theme_changed(value, *args):
        app.css_config.change_theme(value)
        app.nec.push_incoming_event(NetworkEvent('theme-update'))
        app.interface.roster.repaint_themed_widgets()
        app.interface.roster.change_roster_style(None)

    @staticmethod
    def _on_dark_theme(value, *args):
        app.css_config.set_dark_theme(int(value))


class Emoji(PreferenceBox):
    def __init__(self, *args):
        if sys.platform not in ('win32', 'darwin'):
            PreferenceBox.__init__(self, [])
            return

        emoji_themes_items = []
        for theme in helpers.get_available_emoticon_themes():
            emoji_themes_items.append(theme)

        settings = [
            Setting(SettingKind.POPOVER,
                    _('Emoji Theme'),
                    SettingType.CONFIG,
                    'emoticons_theme',
                    desc=_('Choose from various emoji styles'),
                    props={'entries': emoji_themes_items},
                    callback=self._on_emoticons_theme)
        ]

        PreferenceBox.__init__(self, settings)

    def _on_emoticons_theme(self, *args):
        emoji_chooser.load()
        self._toggle_emoticons()

    @staticmethod
    def _toggle_emoticons():
        controls = get_app_window('Preferences').get_all_controls()
        for ctrl in controls:
            ctrl.toggle_emoticons()


class StatusIcon(PreferenceBox):
    def __init__(self, *args):

        iconset_items = []
        for _index, iconset_name in enumerate(get_available_iconsets()):
            iconset_items.append(iconset_name)

        settings = [
            Setting(SettingKind.POPOVER,
                    _('Status Icon Set'),
                    SettingType.CONFIG,
                    'iconset',
                    props={'entries': iconset_items},
                    callback=self._on_iconset_changed),

            Setting(SettingKind.SWITCH,
                    _('Use Transport Icons'),
                    SettingType.CONFIG,
                    'use_transports_iconsets',
                    desc=_('Display protocol-specific status icons '
                           '(ICQ, ..)')),
        ]

        PreferenceBox.__init__(self, settings)

    @staticmethod
    def _on_iconset_changed(*args):
        app.interface.roster.update_icons()


class Server(PreferenceBox):
    def __init__(self, *args):

        settings = [

            Setting(SettingKind.USE_STUN_SERVER,
                    _('Use Stun Server'),
                    SettingType.DIALOG,
                    desc=_('Helps to establish calls through firewalls'),
                    props={'dialog': StunServerDialog}),
        ]

        PreferenceBox.__init__(self, settings)

        self.set_sensitive(app.is_installed('AV'))


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

        settings = [
            Setting(SettingKind.SWITCH,
                    _('Use Stun Server'),
                    SettingType.CONFIG,
                    'use_stun_server'),

            Setting(SettingKind.ENTRY,
                    _('STUN Server'),
                    SettingType.CONFIG,
                    'stun_server',
                    bind='use_stun_server')
            ]

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


class Audio(PreferenceBox):
    def __init__(self, *args):

        deps_installed = app.is_installed('AV')

        audio_input_devices = {}
        audio_output_devices = {}
        if deps_installed:
            audio_input_devices = AudioInputManager().get_devices()
            audio_output_devices = AudioOutputManager().get_devices()

        audio_input_items = self._create_av_combo_items(audio_input_devices)
        audio_output_items = self._create_av_combo_items(audio_output_devices)

        settings = [
            Setting(SettingKind.POPOVER,
                    _('Audio Input Device'),
                    SettingType.CONFIG,
                    'audio_input_device',
                    desc=_('Select your audio input (e.g. microphone)'),
                    props={'entries': audio_input_items}),

            Setting(SettingKind.POPOVER,
                    _('Audio Output Device'),
                    SettingType.CONFIG,
                    'audio_output_device',
                    desc=_('Select an audio output (e.g. speakers, '
                           'headphones)'),
                    props={'entries': audio_output_items}),
        ]

        PreferenceBox.__init__(self, settings)

        self.set_sensitive(deps_installed)

    @staticmethod
    def _create_av_combo_items(items_dict):
        items = enumerate(sorted(
            items_dict.items(),
            key=lambda x: '' if x[1].startswith('auto') else x[0].lower()))
        combo_items = {}
        for _index, (name, value) in items:
            combo_items[value] = name
        return combo_items


class Video(PreferenceBox):
    def __init__(self, *args):

        deps_installed = app.is_installed('AV')

        video_input_devices = {}
        if deps_installed:
            video_input_devices = VideoInputManager().get_devices()

        video_input_items = self._create_av_combo_items(video_input_devices)

        video_framerates = {
            '': _('Default'),
            '15/1': '15 fps',
            '10/1': '10 fps',
            '5/1': '5 fps',
            '5/2': '2.5 fps',
        }

        video_sizes = {
            '': _('Default'),
            '800x600': '800x600',
            '640x480': '640x480',
            '320x240': '320x240',
        }

        settings = [
            Setting(SettingKind.POPOVER,
                    _('Video Input Device'),
                    SettingType.CONFIG,
                    'video_input_device',
                    props={'entries': video_input_items},
                    desc=_('Select your video input device (e.g. webcam, '
                           'screen capture)'),
                    callback=self._on_video_input_changed),

            Setting(SettingKind.POPOVER,
                    _('Video Framerate'),
                    SettingType.CONFIG,
                    'video_framerate',
                    props={'entries': video_framerates}),

            Setting(SettingKind.POPOVER,
                    _('Video Resolution'),
                    SettingType.CONFIG,
                    'video_size',
                    props={'entries': video_sizes}),

            Setting(SettingKind.SWITCH,
                    _('Show My Video Stream'),
                    SettingType.CONFIG,
                    'video_see_self',
                    desc=_('Show your own video stream in calls')),

            Setting(SettingKind.SWITCH,
                    _('Live Preview'),
                    SettingType.VALUE,
                    desc=_('Show a live preview to test your video source'),
                    callback=self._toggle_live_preview),
        ]

        PreferenceBox.__init__(self, settings)

        self.set_sensitive(deps_installed)

    @staticmethod
    def _on_video_input_changed(value, *args):
        preview = get_app_window('Preferences').get_video_preview()
        if preview is None or not preview.is_active:
            # changed signal gets triggered when we fill the combobox
            return
        preview.refresh()

    @staticmethod
    def _toggle_live_preview(value, *args):
        preview = get_app_window('Preferences').get_video_preview()
        preview.toggle_preview(value)

    @staticmethod
    def _create_av_combo_items(items_dict):
        items = enumerate(sorted(
            items_dict.items(),
            key=lambda x: '' if x[1].startswith('auto') else x[0].lower()))
        combo_items = {}
        for _index, (name, value) in items:
            combo_items[value] = name
        return combo_items


class Miscellaneous(PreferenceBox):
    def __init__(self, pref_window):
        self._hints_list = [
            'start_chat',
        ]

        settings = [
            Setting(SettingKind.POPOVER,
                    _('Global Proxy'),
                    SettingType.CONFIG,
                    'global_proxy',
                    name='global_proxy',
                    props={'entries': self._get_proxies(),
                           'default-text': _('System'),
                           'button-icon-name': 'preferences-system-symbolic',
                           'button-callback': self._on_proxy_edit}),

            Setting(SettingKind.SWITCH,
                    _('Use System Keyring'),
                    SettingType.CONFIG,
                    'use_keyring',
                    desc=_('Use your system’s keyring to store passwords')),
        ]

        if sys.platform in ('win32', 'darwin'):
            settings.append(
                Setting(SettingKind.SWITCH,
                        _('Check For Updates'),
                        SettingType.CONFIG,
                        'check_for_update',
                        desc=_('Check for Gajim updates periodically')))

        PreferenceBox.__init__(self, settings)

        reset_button = pref_window.get_ui().reset_button
        reset_button.connect('clicked', self._on_reset_hints)
        reset_button.set_sensitive(self._check_hints_reset)

    @staticmethod
    def _get_proxies():
        return {proxy: proxy for proxy in app.settings.get_proxies()}

    @staticmethod
    def _on_proxy_edit(*args):
        open_window('ManageProxies')

    def update_proxy_list(self):
        self.get_setting('global_proxy').update_entries(self._get_proxies())

    def _check_hints_reset(self):
        for hint in self._hints_list:
            if app.settings.get('show_help_%s' % hint) is False:
                return True
        return False

    def _on_reset_hints(self, button):
        for hint in self._hints_list:
            app.settings.set('show_help_%s' % hint, True)
        button.set_sensitive(False)


class Advanced(PreferenceBox):
    def __init__(self, pref_window):

        settings = [
            Setting(SettingKind.SWITCH,
                    _('Debug Logging'),
                    SettingType.VALUE,
                    app.get_debug_mode(),
                    props={'button-icon-name': 'folder-symbolic',
                           'button-callback': self._on_open_debug_logs},
                    callback=self._on_debug_logging),
        ]

        PreferenceBox.__init__(self, settings)

        pref_window.get_ui().ace_button.connect(
            'clicked', self._on_advanced_config_editor)

    @staticmethod
    def _on_debug_logging(value, *args):
        app.set_debug_mode(value)

    @staticmethod
    def _on_open_debug_logs(*args):
        open_file(configpaths.get('DEBUG'))

    @staticmethod
    def _on_advanced_config_editor(*args):
        open_window('AdvancedConfig')
