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

import nbxmpp
from gi.repository import Gdk
from gi.repository import Gtk
from gi.repository import GLib

from gajim.common import app
from gajim.common import ged
from gajim.common.const import Option, OptionKind, OptionType, StyleAttr
from gajim.gtk import ErrorDialog
from gajim.gtk import util
from gajim.gtk.util import get_builder
from gajim.gtk.util import get_image_button
from gajim.options_dialog import OptionsDialog


UNDECLARED = 'http://www.gajim.org/xmlns/undeclared'


class XMLConsoleWindow(Gtk.Window):
    def __init__(self, account):
        Gtk.Window.__init__(self)
        self.account = account
        self.enabled = True
        self.presence = True
        self.message = True
        self.iq = True
        self.stream = True
        self.incoming = True
        self.outgoing = True
        self.filter_dialog = None

        glade_objects = ['textview', 'input', 'scrolled_input', 'headerbar',
                         'scrolled', 'actionbar', 'paned', 'box', 'menubutton']
        self.builder = get_builder('xml_console_window.ui')
        for obj in glade_objects:
            setattr(self, obj, self.builder.get_object(obj))

        self.set_titlebar(self.headerbar)
        jid = app.get_jid_from_account(account)
        self.headerbar.set_subtitle(jid)
        self.set_default_size(600, 600)
        self.add(self.box)

        self.paned.set_position(self.paned.get_property('max-position'))

        button = get_image_button(
            'edit-clear-all-symbolic', _('Clear'))
        button.connect('clicked', self.on_clear)
        self.actionbar.pack_start(button)

        button = get_image_button(
            'applications-system-symbolic', _('Filter'))
        button.connect('clicked', self.on_filter_options)
        self.actionbar.pack_start(button)

        button = get_image_button(
            'document-edit-symbolic', _('XML Input'), toggle=True)
        button.connect('toggled', self.on_input)
        self.actionbar.pack_start(button)

        button = get_image_button('emblem-ok-symbolic', _('Send'))
        button.connect('clicked', self.on_send)
        self.actionbar.pack_end(button)

        self.actionbar.pack_start(self.menubutton)

        self.create_tags()
        self.show_all()

        self.scrolled_input.hide()
        self.menubutton.hide()

        self.connect("destroy", self.on_destroy)
        self.connect('key_press_event', self.on_key_press_event)
        self.builder.connect_signals(self)

        app.ged.register_event_handler(
            'stanza-received', ged.GUI1, self._nec_stanza_received)
        app.ged.register_event_handler(
            'stanza-sent', ged.GUI1, self._nec_stanza_sent)

    def create_tags(self):
        buffer_ = self.textview.get_buffer()
        in_color = app.css_config.get_value(
            '.gajim-incoming-nickname', StyleAttr.COLOR)
        out_color = app.css_config.get_value(
            '.gajim-outgoing-nickname', StyleAttr.COLOR)

        tags = ['presence', 'message', 'stream', 'iq']

        tag = buffer_.create_tag('incoming')
        tag.set_property('foreground', in_color)
        tag = buffer_.create_tag('outgoing')
        tag.set_property('foreground', out_color)

        for tag_name in tags:
            buffer_.create_tag(tag_name)

    def on_key_press_event(self, widget, event):
        if event.keyval == Gdk.KEY_Escape:
            self.destroy()

    def on_row_activated(self, listbox, row):
        text = row.get_child().get_text()
        input_text = None
        if text == 'Presence':
            input_text = (
                '<presence>\n'
                '<show></show>\n'
                '<status></status>\n'
                '<priority></priority>\n'
                '</presence>')
        elif text == 'Message':
            input_text = (
                '<message to="" type="">\n'
                '<body></body>\n'
                '</message>')
        elif text == 'Iq':
            input_text = (
                '<iq to="" type="">\n'
                '<query xmlns=""></query>\n'
                '</iq>')

        if input_text is not None:
            buffer_ = self.input.get_buffer()
            buffer_.set_text(input_text)
            self.input.grab_focus()

    def on_send(self, *args):
        if app.connections[self.account].connected <= 1:
            # if offline or connecting
            ErrorDialog(
                _('Connection not available'),
                _('Please make sure you are connected with "%s".') %
                self.account)
            return
        buffer_ = self.input.get_buffer()
        begin_iter, end_iter = buffer_.get_bounds()
        stanza = buffer_.get_text(begin_iter, end_iter, True)
        if stanza:
            try:
                node = nbxmpp.Protocol(node=stanza)
                if node.getNamespace() == UNDECLARED:
                    node.setNamespace(nbxmpp.NS_CLIENT)
            except Exception as error:
                ErrorDialog(_('Invalid Node'), str(error))
                return
            app.connections[self.account].connection.send(node)
            buffer_.set_text('')

    def on_input(self, button, *args):
        if button.get_active():
            self.paned.get_child2().show()
            self.menubutton.show()
            self.input.grab_focus()
        else:
            self.paned.get_child2().hide()
            self.menubutton.hide()

    def on_filter_options(self, *args):
        if self.filter_dialog:
            self.filter_dialog.present()
            return
        options = [
            Option(OptionKind.SWITCH, 'Presence',
                   OptionType.VALUE, self.presence,
                   callback=self.on_option, data='presence'),

            Option(OptionKind.SWITCH, 'Message',
                   OptionType.VALUE, self.message,
                   callback=self.on_option, data='message'),

            Option(OptionKind.SWITCH, 'Iq', OptionType.VALUE, self.iq,
                   callback=self.on_option, data='iq'),

            Option(OptionKind.SWITCH, 'Stream\nManagement',
                   OptionType.VALUE, self.stream,
                   callback=self.on_option, data='stream'),

            Option(OptionKind.SWITCH, 'In', OptionType.VALUE, self.incoming,
                   callback=self.on_option, data='incoming'),

            Option(OptionKind.SWITCH, 'Out', OptionType.VALUE, self.outgoing,
                   callback=self.on_option, data='outgoing'),
        ]

        self.filter_dialog = OptionsDialog(self, 'Filter',
                                           Gtk.DialogFlags.DESTROY_WITH_PARENT,
                                           options, self.account)
        self.filter_dialog.connect('destroy', self.on_filter_destroyed)

    def on_filter_destroyed(self, win):
        self.filter_dialog = None

    def on_clear(self, *args):
        self.textview.get_buffer().set_text('')

    def on_destroy(self, *args):
        del app.interface.instances[self.account]['xml_console']
        app.ged.remove_event_handler(
            'stanza-received', ged.GUI1, self._nec_stanza_received)
        app.ged.remove_event_handler(
            'stanza-sent', ged.GUI1, self._nec_stanza_sent)

    def on_enable(self, switch, param):
        self.enabled = switch.get_active()

    def on_option(self, value, data):
        setattr(self, data, value)
        value = not value
        table = self.textview.get_buffer().get_tag_table()
        tag = table.lookup(data)
        if data in ('incoming', 'outgoing'):
            if value:
                tag.set_priority(table.get_size() - 1)
            else:
                tag.set_priority(0)
        tag.set_property('invisible', value)

    def _nec_stanza_received(self, obj):
        if obj.conn.name != self.account:
            return
        self.print_stanza(obj.stanza_str, 'incoming')

    def _nec_stanza_sent(self, obj):
        if obj.conn.name != self.account:
            return
        self.print_stanza(obj.stanza_str, 'outgoing')

    def print_stanza(self, stanza, kind):
        # kind must be 'incoming' or 'outgoing'
        if not self.enabled:
            return
        if not stanza:
            return

        at_the_end = util.at_the_end(self.scrolled)

        buffer_ = self.textview.get_buffer()
        end_iter = buffer_.get_end_iter()

        type_ = kind
        if stanza.startswith('<presence'):
            type_ = 'presence'
        elif stanza.startswith('<message'):
            type_ = 'message'
        elif stanza.startswith('<iq'):
            type_ = 'iq'
        elif stanza.startswith('<r') or stanza.startswith('<a'):
            type_ = 'stream'

        stanza = '<!-- {kind} {time} -->\n{stanza}\n\n'.format(
            kind=kind.capitalize(),
            time=time.strftime('%c'),
            stanza=stanza.replace('><', '>\n<'))
        buffer_.insert_with_tags_by_name(end_iter, stanza, type_, kind)

        if at_the_end:
            GLib.idle_add(util.scroll_to_end, self.scrolled)