from config.ConfigSettings import ConfigSettings
from ControlFactory import ControlFactory
from utils.TargetSettings import TargetSettings
from display.Display import Display
from utils.TypeConverter import TypeConverter
from utils.datatypes import *
from utils import dialog

from xml import sax

import os
import sys

try:
    import gnomevfs
except ImportError:
    import gnome.vfs as gnomevfs

_STATE_NONE = -1
_STATE_DISPLAY = 0
_STATE_META = 1
_STATE_SCRIPT = 2
_STATE_PREFS = 3
_STATE_SENSOR = 4  # deprecated
_STATE_CONTROL = 5

_SENSOR_CONTROL = "ISensor:2peqs3czo5mlrv5ibwwkn5dqc"


#
# Class for creating Displays from XML data.
#
class _DisplayFactory(sax.handler.ContentHandler):

    def __init__(self):

        # the current state of the parser
        self.__state = [_STATE_NONE]

        # a stack of (tagname, attributes) pairs for remembering the nesting
        # of elements
        self.__nesting_stack = []

        # a stack of (type, settings, children) tuples to remember the children
        # of elements; children is a list of (type, settings, children) tuples
        self.__children_stack = [[]]

        # a list of the sensors for the display
        self.__sensors = []

        # the config settings object of the display
        self.__config_settings = None

        self.__path = ""
        self.__scripts = []
        self.__script = ""
        self.__id = ""
        self.__control_factory = ControlFactory()
        self.__type_converter = TypeConverter()

        sax.handler.ContentHandler.__init__(self)
        self.__type_converter.add_type("module", TYPE_LIST)



    #
    # Parses the given XML data and returns a new Display object.
    #
    def create_display(self, ident, data, rep):

        dsp = Display(ident, rep)

        self.__config_settings = ConfigSettings()
        self.__scripts = []
        self.__id = ident
        self.__children_stack = [[]]
        self.__nesting_stack = []
        self.__sensors = []
        self.__path = os.path.dirname(rep)

        # parse display data
        try:
            sax.parseString(data, self)
        except sax._exceptions.SAXParseException, sax_exc:
            print >> sys.stderr, "Parse Error: %s" % (sax_exc,)
            # abort if a parse error occured
            return
        except StandardError:
            import traceback
	    traceback.print_exc()
            return

        # add the sensor controls
        for ident, sensorctrl in self.__sensors:
            dsp.add_sensor(ident, sensorctrl)

        # add the children and configure
        try:
            childtype, settings, children = self.__children_stack.pop()[0]

            dsp.new_child(childtype, settings, children)
            for s in self.__scripts:
                dsp.execute_script(s)

        except StandardError, exc:
            # a stack trace could be useful for debugging
            import traceback
	    traceback.print_exc()

            # make the corrupt display visible so that the user can remove it
            print >> sys.stderr, "Error while configuring display: %s" % (exc,)
            dialog.warning(_("Error while configuring display"),
                           _("A display could not be configured properly. "
                             "It will most likely be broken and you should "
                             "consider removing it."))
            return

        dsp.set_configurator(self.__config_settings)

        self.__children_stack = [[]]
        self.__nesting_stack = []
        self.__sensors = []
        self.__scripts = []

        return dsp



    #
    # Creates a TargetSettings object from the given Attributes object.
    #
    def __create_settings(self, attrs):

        settings = TargetSettings()
        for key, value in attrs.items():
            settings.set(key, value)

        return settings



    def startElement(self, name, attrs):

        if (name == "display"):
            self.__state.append(_STATE_DISPLAY)

        elif (name == "prefs"):
            self.__state.append(_STATE_PREFS)

        elif (name == "meta"):
            self.__state.append(_STATE_META)

        elif (name == "sensor"):
            # the <sensor> tag is deprecated
            import warnings
            warnings.warn("Using sensors is deprecated now. Please consider "
                          "using controls and inline scripts now.",
                          DeprecationWarning)
            self.__state.append(_STATE_SENSOR)

        elif (name == "control"):
            self.__state.append(_STATE_CONTROL)

        elif (name == "script"):
            self.__state.append(_STATE_SCRIPT)
            self.__script = ""

        if (self.__state[-1] == _STATE_DISPLAY):
            self.__children_stack.append([])

        # remember everything for later
        self.__nesting_stack.append((name, attrs))



    def endElement(self, name):

        oname, attrs = self.__nesting_stack.pop()
        state = self.__state[-1]
        if (not name == oname):
            # nesting errors in XML are detected by the SAX parser; if we
            # get here, it means our parser is buggy, not the XML input
            print >> sys.stderr, "Nesting error: expected %s, got %s." % \
	                                                         (oname, name)
            return


        elif (state == _STATE_DISPLAY):
            settings = self.__create_settings(attrs)
            # if there is no ID given, guess a unique one
            ident = attrs.get("id", Display.make_id())
            settings["id"] = ident

            # build the tree of children from bottom up
            children = self.__children_stack.pop()
            self.__children_stack[-1].append((name, settings, children))

        elif (state == _STATE_META):
            pass

        elif (state == _STATE_SCRIPT):
            # Some distros ship a broken SAX parser and
            # their users are too lazy to reportbug. (doesn't have __contains__)
            # So we have to use has_key()
            # if ((name == "script") and ("uri" in attrs)):
            if (name == "script" and attrs.has_key("uri")):
                try:
                    path = attrs["uri"]
                    path = os.path.join(self.__path, path)
                    self.__script += "\n" + gnomevfs.read_entire_file(path)
                except StandardError:
                    import traceback
		    traceback.print_exc()

            # collect script by script to be able to execute them one by one
            # later; we would get problems with different indentation otherwise
            self.__scripts.append(self.__script)

        # deprecated sensor stuff
        elif (state == _STATE_SENSOR):
            ident = attrs["id"]
            moduledata = self.__type_converter.str2type("module",
                                                        attrs["module"])
            module = moduledata[0]
            args = moduledata[1:]
            sensorctrl = self.__control_factory.get_control(_SENSOR_CONTROL)

            if (sensorctrl):
                sensorctrl.sensor = (module, args)
                sensorctrl.config_id = self.__id + ident
                self.__sensors.append((ident, sensorctrl))
            else:
                raise RuntimeError(_("Could not load sensor"))

        elif (state == _STATE_CONTROL):
            ident = attrs["id"]
            interface = attrs["interface"]
            script = "%s = get_control('%s')" % (ident, interface)
            self.__scripts.append(script)

        elif (state == _STATE_PREFS):
            if (name == "prefs"):
                callback = attrs.get("callback", None)
                if (callback): self.__config_settings.set_callback(callback)
            else:
                self.__config_settings.add_item(name, attrs)


        if (name in ("display", "sensor", "control", "meta",
                     "prefs", "script")):
            self.__state.pop()



    def characters(self, content):

        state = self.__state[-1]
        if (state == _STATE_SCRIPT):
            self.__script += str(content)


_singleton = _DisplayFactory()
def DisplayFactory(): return _singleton
