# w.c.s. - web application for online forms
# Copyright (C) 2005-2014  Entr'ouvert
#
# This program 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; either version 2 of the License, or
# (at your option) any later version.
#
# 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, see <http://www.gnu.org/licenses/>.

import datetime
import xml.etree.ElementTree as ET

from django.utils.encoding import force_text
from quixote import get_publisher

from . import force_str
from .misc import indent_xml
from .storage import StorableObject

class XmlStorableObject(StorableObject):
    XML_NODES = []

    @classmethod
    def storage_load(cls, fd):
        first_byte = fd.read(1)
        fd.seek(0)
        if first_byte == b'<':
            return cls.import_from_xml(fd, include_id=True)
        else:
            return StorableObject.storage_load(fd)

    @classmethod
    def storage_dumps(cls, object):
        return object.export_to_xml_string(include_id=True)

    def export_to_xml(self, include_id=False):
        charset = get_publisher().site_charset
        root = ET.Element(self._xml_tagname)
        if include_id and self.id:
            root.attrib['id'] = str(self.id)
        for attribute in self.XML_NODES:
            attribute_name, attribute_type = attribute
            if not getattr(self, attribute_name, None):
                continue
            element = ET.SubElement(root, attribute_name)
            getattr(self, 'export_%s_to_xml' % attribute_type)(element,
                    attribute_name, charset=charset)
        return root

    def export_str_to_xml(self, element, attribute_name, charset):
        element.text = force_text(getattr(self, attribute_name), charset)

    def export_int_to_xml(self, element, attribute_name, charset):
        element.text = str(getattr(self, attribute_name))

    def export_bool_to_xml(self, element, attribute_name, charset):
        element.text = 'true' if getattr(self, attribute_name) else 'false'

    def export_datetime_to_xml(self, element, attribute_name, charset):
        element.text = getattr(self, attribute_name).isoformat()

    def export_to_xml_string(self, include_id=False):
        x = self.export_to_xml(include_id=include_id)
        indent_xml(x)
        return ET.tostring(x)

    @classmethod
    def import_from_xml(cls, fd, charset=None, include_id=False):
        try:
            tree = ET.parse(fd)
        except:
            raise ValueError()
        return cls.import_from_xml_tree(tree, charset=charset,
                include_id=include_id)

    @classmethod
    def import_from_xml_tree(cls, tree, include_id=False, charset=None):
        if charset is None:
            charset = get_publisher().site_charset
        obj = cls()

        # if the tree we get is actually a ElementTree for real, we get its
        # root element and go on happily.
        if not ET.iselement(tree):
            tree = tree.getroot()

        if include_id and tree.attrib.get('id'):
            obj.id = tree.attrib.get('id')

        for attribute in cls.XML_NODES:
            attribute_name, attribute_type = attribute
            element = tree.find(attribute_name)
            if element is None:
                continue
            setattr(obj, attribute_name,
                    getattr(obj, 'import_%s_from_xml' %
                        attribute_type)(element, charset=charset))
        return obj

    def import_str_from_xml(self, element, charset):
        return force_str(element.text)

    def import_int_from_xml(self, element, charset):
        return int(element.text)

    def import_bool_from_xml(self, element, charset):
        return bool(element.text == 'true')

    def import_datetime_from_xml(self, element, charset):
        return datetime.datetime.strptime(element.text[:19], '%Y-%m-%dT%H:%M:%S')
