# 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 random

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

from nbxmpp.errors import StanzaError

from gajim.common import app
from gajim.common.const import MUC_CREATION_EXAMPLES
from gajim.common.const import MUC_DISCO_ERRORS
from gajim.common.i18n import _
from gajim.common.helpers import validate_jid
from gajim.common.helpers import to_user_string

from .dialogs import ErrorDialog
from .util import get_builder
from .util import ensure_not_destroyed

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


class CreateGroupchatWindow(Gtk.ApplicationWindow):
    def __init__(self, account):
        Gtk.ApplicationWindow.__init__(self)
        self.set_name('CreateGroupchat')
        self.set_application(app.app)
        self.set_position(Gtk.WindowPosition.CENTER)
        self.set_type_hint(Gdk.WindowTypeHint.DIALOG)
        self.set_default_size(500, -1)
        self.set_show_menubar(False)
        self.set_resizable(True)
        self.set_title(_('Create Group Chat'))

        self._ui = get_builder('groupchat_creation.ui')
        self.add(self._ui.create_group_chat)

        self._destroyed = False

        self._account = self._fill_account_combo(account)

        self._create_entry_completion()
        self._fill_placeholders()

        self._ui.connect_signals(self)
        self.connect('key-press-event', self._on_key_press_event)
        self.connect('destroy', self._on_destroy)

        self.show_all()
        self.set_focus(self._ui.address_entry)

    def _get_muc_service_jid(self):
        con = app.connections[self._account]
        return str(con.get_module('MUC').service_jid or 'muc.example.com')

    def _fill_account_combo(self, account):
        accounts = app.get_enabled_accounts_with_labels(connected_only=True)
        account_liststore = self._ui.account_combo.get_model()
        for acc in accounts:
            account_liststore.append(acc)

        # Hide account combobox if there is only one account
        if len(accounts) == 1:
            self._ui.account_combo.hide()
            self._ui.account_label.hide()

        if account is None:
            account = accounts[0][0]

        self._ui.account_combo.set_active_id(account)
        return account

    def _create_entry_completion(self):
        entry_completion = Gtk.EntryCompletion()
        model = Gtk.ListStore(str)
        entry_completion.set_model(model)
        entry_completion.set_text_column(0)

        entry_completion.set_inline_completion(True)
        entry_completion.set_popup_single_match(False)

        self._ui.address_entry.set_completion(entry_completion)

    def _fill_placeholders(self):
        placeholder = random.choice(MUC_CREATION_EXAMPLES)
        server = self._get_muc_service_jid()

        self._ui.name_entry.set_placeholder_text(
            placeholder[0] + _(' (optional)...'))
        self._ui.description_entry.set_placeholder_text(
            placeholder[1] + _(' (optional)...'))
        self._ui.address_entry.set_placeholder_text(
            '%s@%s' % (placeholder[2], server))

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

    def _on_account_combo_changed(self, combo):
        self._account = combo.get_active_id()
        self._fill_placeholders()

    def _update_entry_completion(self, entry, text):
        text = entry.get_text()
        if '@' in text:
            text = text.split('@', 1)[0]

        model = entry.get_completion().get_model()
        model.clear()

        server = self._get_muc_service_jid()
        model.append(['%s@%s' % (text, server)])

    def _validate_jid(self, text):
        if not text:
            self._set_warning_icon(False)
            self._ui.create_button.set_sensitive(False)
            return

        try:
            jid = validate_jid(text)
            if jid.resource:
                raise ValueError

        except ValueError:
            self._set_warning(_('Invalid Address'))
        else:
            self._set_warning_icon(False)
            self._ui.create_button.set_sensitive(True)

    def _set_processing_state(self, enabled):
        if enabled:
            self._ui.spinner.start()
            self._ui.create_button.set_sensitive(False)
        else:
            self._ui.spinner.stop()
        self._ui.grid.set_sensitive(not enabled)

    def _set_warning_icon(self, enabled):
        icon = 'dialog-warning-symbolic' if enabled else None
        self._ui.address_entry.set_icon_from_icon_name(
            Gtk.EntryIconPosition.SECONDARY, icon)

    def _set_warning_tooltip(self, text):
        self._ui.address_entry.set_icon_tooltip_text(
            Gtk.EntryIconPosition.SECONDARY, text)

    def _set_warning(self, text):
        self._set_warning_icon(True)
        self._set_warning_tooltip(text)
        self._ui.create_button.set_sensitive(False)

    def _set_warning_from_error(self, error):
        condition = error.condition
        if condition == 'gone':
            condition = 'already-exists'
        text = MUC_DISCO_ERRORS.get(condition, to_user_string(error))
        self._set_warning(text)

    def _set_warning_from_error_code(self, error_code):
        self._set_warning(MUC_DISCO_ERRORS[error_code])

    def _on_address_entry_changed(self, entry):
        text = entry.get_text()
        self._update_entry_completion(entry, text)
        self._validate_jid(text)

    def _on_address_entry_activate(self, _widget):
        self._on_create_clicked()

    def _on_create_clicked(self, *args):
        if not app.account_is_available(self._account):
            ErrorDialog(
                _('Not Connected'),
                _('You have to be connected to create a group chat.'))
            return

        room_jid = self._ui.address_entry.get_text()

        self._set_processing_state(True)
        con = app.connections[self._account]
        con.get_module('Discovery').disco_info(
            room_jid, callback=self._disco_info_received)

    @ensure_not_destroyed
    def _disco_info_received(self, task):
        try:
            result = task.finish()
        except StanzaError as error:
            if error.condition == 'item-not-found':
                self._create_muc(error.jid)
                return
            self._set_warning_from_error(error)

        else:
            self._set_warning_from_error_code(
                'already-exists' if result.is_muc else 'not-muc-service')

        self._set_processing_state(False)

    def _create_muc(self, room_jid):
        name = self._ui.name_entry.get_text()
        description = self._ui.description_entry.get_text()
        is_public = self._ui.public_switch.get_active()

        config = {
            # XEP-0045 options
            'muc#roomconfig_roomname': name,
            'muc#roomconfig_roomdesc': description,
            'muc#roomconfig_publicroom': is_public,
            'muc#roomconfig_membersonly': not is_public,
            'muc#roomconfig_whois': 'moderators' if is_public else 'anyone',
            'muc#roomconfig_changesubject': not is_public,

            # Ejabberd options
            'public_list': is_public,
        }

        # Create new group chat by joining
        app.interface.create_groupchat(
            self._account,
            str(room_jid),
            config=config)

        self.destroy()

    def _on_destroy(self, *args):
        self._destroyed = True
