# Console Jabber Client
# Copyright (C) 2004-2010 Jacek Konieczny
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as published
# by the Free Software Foundation
#
# This program 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 this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

import logging

import string
from types import UnicodeType,StringType
import curses
import libxml2

import pyxmpp
from pyxmpp.utils import from_utf8,to_utf8

from cjc.plugin import PluginBase
from cjc import ui
from cjc import cjc_globals

theme_attrs=(
    ("xmlconsole.out", curses.COLOR_YELLOW,curses.COLOR_BLACK,curses.A_BOLD, curses.A_UNDERLINE),
    ("xmlconsole.in", curses.COLOR_WHITE,curses.COLOR_BLACK,curses.A_NORMAL, curses.A_NORMAL),
)

theme_formats=(
    ("xmlconsole.out","%[xmlconsole.out][%(T:now)s] OUT: %(msg)s\n"),
    ("xmlconsole.in","%[xmlconsole.in][%(T:now)s] IN: %(msg)s\n"),
    ("xmlconsole.descr","Raw XML console"),
    ("xmlconsole.day_change","%{@day_change}"),
)

class DataInHandler(logging.Handler):
    def __init__(self,plugin):
        logging.Handler.__init__(self,level=logging.CRITICAL)
        self.plugin=plugin
    def emit(self,record):
        data=record.args[0]
        if not data or data in (" ","\n"):
            # skip keepalive data (but we are not sure it is keepalive)
            return
        self.plugin.show_data(record.args[0],"in")

class DataOutHandler(logging.Handler):
    def __init__(self,plugin):
        logging.Handler.__init__(self,level=logging.CRITICAL)
        self.plugin=plugin
    def emit(self,record):
        data=record.args[0]
        if not data or data in (" ","\n"):
            # skip keepalive data (but we are not sure it is keepalive)
            return
        self.plugin.show_data(record.args[0],"out")

class Plugin(PluginBase):
    def __init__(self,app,name):
        PluginBase.__init__(self,app,name)
        cjc_globals.theme_manager.set_default_formats(theme_formats)
        cjc_globals.theme_manager.set_default_attrs(theme_attrs)
        self.available_settings={
            "pretty_print": ("Reformat (pretty-print) sent and received XML (doesn't work well)",int),
            "check": ("Check if the raw-XML element is valid before sending",int),
            }
        self.settings={
            "pretty_print": 0,
            "check": 1,
            }
        app.add_event_handler("day changed",self.ev_day_changed)
        ui.activate_cmdtable("xmlconsole",self)
        self.away_saved_presence=None
        self.buffer=None
        self.data_in_handler=DataInHandler(self)
        self.data_out_handler=DataOutHandler(self)
        logging.getLogger("pyxmpp.Stream.in").addHandler(self.data_in_handler)
        logging.getLogger("pyxmpp.Stream.out").addHandler(self.data_out_handler)

    def ev_day_changed(self,event,arg):
        if not self.buffer:
            return
        self.buffer.append_themed("xmlconsole.day_change",{},activity_level=0)
        self.buffer.update()

    def user_input(self,s):
        if not self.cjc.stream:
            self.error("Not connected!")
            return
        if self.settings.get("check"):
            try:
                d=libxml2.parseDoc(to_utf8(s))
            except libxml2.parserError:
                if self.buffer:
                    self.buffer.append_themed("error","XML not well-formed!")
                else:
                    self.error("XML not well-formed!")
                cjc_globals.screen.redraw() # workaroud for libxml2 messing the screen
                return
            s=d.getRootElement().serialize("utf-8")
        self.cjc.stream.write_raw(s)

    def cmd_rawxml(self,args):
        if not self.cjc.stream:
            self.error("Connect first!")
            return
        s=args.all()
        self.user_input(s)

    def cmd_xmlconsole(self,args):
        args.finish()
        if self.buffer:
            cjc_globals.screen.display_buffer(self.buffer)
            return
        self.buffer=ui.TextBuffer({},"xmlconsole.descr",
                "xmlconsole buffer",self)
        self.buffer.user_input=self.user_input
        self.buffer.update()
        self.data_in_handler.setLevel(logging.DEBUG)
        self.data_out_handler.setLevel(logging.DEBUG)
        cjc_globals.screen.display_buffer(self.buffer)

    def cmd_close(self,args):
        self.data_in_handler.setLevel(logging.CRITICAL)
        self.data_out_handler.setLevel(logging.CRITICAL)
        if self.buffer:
            self.buffer.close()
        self.buffer=None

    def show_data(self,data,dir):
        if self.settings.get("pretty_print") and data:
            try:
                d=libxml2.parseDoc(to_utf8(data))
            except libxml2.parserError:
                cjc_globals.screen.redraw() # workaroud for libxml2 messing the screen
            except:
                raise
            else:
                data=d.getRootElement().serialize("utf-8",1)
        if type(data) is UnicodeType:
            pass
        elif type(data) is StringType:
            data=unicode(data,"utf-8","replace")
        else:
            data=`data`
        self.buffer.append_themed("xmlconsole."+dir,data)
        self.buffer.update()

    def unload(self):
        logging.getLogger("pyxmpp.Stream.in").removeHandler(self.data_in_handler)
        logging.getLogger("pyxmpp.Stream.in").removeHandler(self.data_out_handler)
        ui.uninstall_cmdtable("xmlconsole buffer")
        ui.uninstall_cmdtable("xmlconsole")
        self.cjc.remove_event_handler("stream created",self.ev_stream_created)
        self.cjc.remove_event_handler("stream closed",self.ev_stream_closed)
        self.cmd_close(None)
        return True

ui.CommandTable("xmlconsole buffer",51,(
    ui.Command("close",Plugin.cmd_close,
        "/close",
        "Closes current chat buffer"),
    )).install()

ui.CommandTable("xmlconsole",51,(
    ui.Command("xmlconsole",Plugin.cmd_xmlconsole,
        "/xmlconsole",
        "Open raw XML console"),
    ui.Command("rawxml",Plugin.cmd_rawxml,
        "/rawxml xmlstring",
        "Send raw xml element"),
    )).install()
# vi: sts=4 et sw=4
