# -*- coding: utf-8 -*-

import xml.sax.saxutils as saxutils
from cStringIO import StringIO

from production.model import ProductionOrder, Item, StateEvent

_PICKLE_PROTOCOL = 2  # Pickle protocol version to use ("2" is the best one at the time this is written)


def _validxml(name, clazz):
    """private - checks if an attribute should be written in the XML"""
    if not name.startswith("_"):
        value = getattr(clazz, name)
        if (not hasattr(value, '__iter__')) and (not callable(value)):
            return True
    return False


def utf(value):
    """retrieves the utf8 representation of a value"""
    return value.encode("UTF-8") if isinstance(value, unicode) else str(value)


class OrderXml(object):

    """ class OrderXml(object)
    Class used to generate an XML representation of a ProductionOrder
    """
    _OUT_TYPES = (str, unicode, int, long, float, bool,)
    _ORDER_ATTRS = [name for name in dir(ProductionOrder) if _validxml(name, ProductionOrder)]
    # level attribute generated when writing the xml to avoid gaps in the sequence
    _ITEM_ATTRS = [name for name in dir(Item) if _validxml(name, Item) and name != 'level']
    _EVENT_ATTRS = [name for name in dir(StateEvent) if _validxml(name, StateEvent)]

    def to_xml(self, order):
        """ obj.to_xml(order) -> str
        Creates and XML representation of the given order.
        @param order: {ProductionOrder} - the production box to generate XML from
        @return {str} - The XML output
        """
        io = StringIO()
        io.write('<?xml version="1.0" encoding="UTF-8"?>\n<ProductionOrder')
        self._write_attributes(io, self._ORDER_ATTRS, order)
        io.write('>\n')
        # Write custom storage
        self._write_custom_storage(io, order, 1)
        # Write items recursively
        io.write('  <Items>\n')
        for item in order.items:
            self._write_item(io, item, 0, 2)
        io.write('  </Items>\n')
        # Write the order properties
        io.write('  <Properties>\n')
        for key in order.properties:
            self._write_property(io, key, order.properties[key], 2)
        io.write('  </Properties>\n')
        # Write the state history
        io.write('  <StateHistory>\n')
        for event in order.state_history:
            io.write('    <StateEvent')
            self._write_attributes(io, self._EVENT_ATTRS, event)
            io.write('/>\n')
        io.write('  </StateHistory>\n')
        io.write('</ProductionOrder>')
        return io.getvalue()

    def _write_comments(self, io, item, level):
        level = int(level)
        level += 1
        for comment_id in item.comments:
            ident = (level * '  ')
            comment = item.comments[comment_id]
            io.write('{0}<Comment id="{1}" comment={2}/>\n'.format(ident, comment_id, utf(saxutils.quoteattr(comment.text))))

    def _write_item(self, io, item, item_level, indent_level):
        """private - writes an Item recursively"""
        indent_space = (indent_level * '  ')
        io.write('%s<Item' % indent_space)
        io.write(' %s=%s' % ('level', saxutils.quoteattr(str(item_level))))
        self._write_attributes(io, self._ITEM_ATTRS, item)
        io.write('>\n')
        indent_level += 1
        # write custom item properties
        if len(item.properties) > 0:
            prop_space = (indent_level * '  ')
            io.write('%s<Properties>\n' % prop_space)
            prop_level = indent_level + 1
            for key in item.properties:
                self._write_property(io, key, item.properties[key], prop_level)
            io.write('%s</Properties>\n' % prop_space)
        # Write custom storage
        self._write_custom_storage(io, item, indent_level)
        # Write comments
        self._write_comments(io, item, indent_level)
        # Write sub-items recursively
        item_level += 1
        for subitem in item.items:
            self._write_item(io, subitem, item_level, indent_level)
        io.write('%s</Item>\n' % indent_space)

    def _write_property(self, io, key, value, level):
        """private - writes an Item recursively"""
        ident = (int(level) * '  ')
        io.write('{0}<Property key="{1}" value={2}/>\n'.format(ident, key, utf(saxutils.quoteattr(value))))

    def _write_attributes(self, io, names, obj):
        """private - writes a list of attributes"""
        out_types = self._OUT_TYPES
        quoteattr = saxutils.quoteattr
        for name in names:
            value = getattr(obj, name)
            if type(value) in out_types:
                io.write(' %s=%s' % (name, quoteattr(utf(value))))

    def _write_custom_storage(self, io, obj, level):
        """private - writes any custom storage data of an object"""
        storage = obj._storage
        ident = (int(level) * '  ')
        quoteattr = saxutils.quoteattr
        for context, dic in storage.iteritems():
            context = quoteattr(utf(context))
            for key, value in dic.iteritems():
                if not key.startswith("_"):
                    key = quoteattr(utf(key))
                    if type(value) in (str, unicode):
                        value = utf(value)
                    else:
                        value = repr(value).decode('string_escape')
                    value = quoteattr(utf(value))
                    io.write('%s<Custom context=%s key=%s value=%s/>\n' % (ident, context, key, value))


class ProdPersistException(Exception):
    pass


class OrderNotFoundException(ProdPersistException):
    pass
