# -*- coding: utf-8 -*-
# Copyright (c) 2013  Infrae. All rights reserved.
# See also LICENSE.txt
from DateTime import DateTime
from zope.component import getUtility
from zope.interface.interfaces import IInterface

from Products.Formulator import XMLObjects
from Products.Formulator.MethodField import Method
from Products.Formulator.TALESField import TALESMethod


def XMLToForm(s, form, override_encoding=None):
    """Takes an xml string and changes formulator form accordingly.
    Heavily inspired by code from Nikolay Kim.

    If override_encoding is set, form data is read assuming given
    encoding instead of the one in the XML data itself. The form will
    have to be modified afterwards to this stored_encoding itself.
    """
    top = XMLObjects.XMLToObjectsFromString(s)
    # wipe out groups
    form.groups = {'Default': []}
    form.group_list = ['Default']

    if override_encoding is None:
        try:
            unicode_mode = top.first.form.first.unicode_mode.text
        except AttributeError:
            unicode_mode = 'false'
        # retrieve encoding information from XML
        if unicode_mode == 'true':
            # just use unicode strings being read in
            encoding = None
        else:
            # store strings as specified encoding
            try:
                encoding = top.first.form.first.stored_encoding.text
            except AttributeError:
                encoding = 'ISO-8859-1'
    else:
        if override_encoding == 'unicode':
            encoding = None
        else:
            encoding = override_encoding

    #  get the settings
    settings = [field.id for field in form.settings_form.get_fields()]
    for setting in settings:
        value = getattr(top.first.form.first, setting, None)
        if value is None:
            continue
        if setting == 'unicode_mode':
            v = value.text == 'true'
        elif setting == 'row_length':
            v = int(value.text)
        else:
            v = encode(value.text, encoding)
        setattr(form, setting, v)

    # create groups
    has_default = 0
    for group in top.first.form.first.groups.elements.group:
        # get group title and create group
        group_title = encode(group.first.title.text, encoding)
        if group_title == 'Default':
            has_default = 1
        form.add_group(group_title)
        # create fields in group
        if not hasattr(group.first.fields.elements, 'field'):
            # empty <fields> element
            continue
        for entry in group.first.fields.elements.field:
            id = str(encode(entry.first.id.text, encoding))
            meta_type = encode(entry.first.type.text, encoding)
            try:
                form._delObject(id)
            except (KeyError, AttributeError):
                pass
            form.manage_addField(id, '', meta_type)
            field = form._getOb(id)
            if group_title != 'Default':
                form.move_field_group([id], 'Default', group_title)
            # set values
            values = entry.first.values
            for name in values.getElementNames():
                value = getattr(values.first, name)
                if value.attributes.get('type') == 'float':
                    field.values[name] = float(value.text)
                elif value.attributes.get('type') == 'int':
                    field.values[name] = int(value.text)
                elif value.attributes.get('type') == 'method':
                    field.values[name] = Method(str(value.text))
                elif value.attributes.get('type') == 'list':
                    # XXX bare eval here (this may be a security leak ?)
                    field.values[name] = eval(
                        encode(value.text, encoding))
                elif value.attributes.get('type') == 'interface':
                    field.values[name] = getUtility(
                        IInterface, name=value.text)
                elif value.attributes.get('type') == 'datetime':
                    field.values[name] = DateTime(value.text)
                else:
                    field.values[name] = encode(value.text, encoding)

            # special hack for the DateTimeField
            if field.meta_type == 'DateTimeField':
                field.on_value_input_style_changed(
                    field.get_value('input_style'))

            # set tales
            tales = entry.first.tales
            for name in tales.getElementNames():
                field.tales[name] = TALESMethod(
                    encode(getattr(tales.first, name).text, encoding))

            # set messages
            if hasattr(entry.first, 'messages'):
                messages = entry.first.messages
                entries = getattr(messages.elements, 'message', [])
                for entry in entries:
                    name = entry.attributes.get('name')
                    text = entry.text
                    # ignore messages that are identical to default
                    if (name in field.validator.message_names and
                            getattr(field.validator, name) == text):
                        continue
                    text = encode(text, encoding)
                    field.message_values[name] = text

            # for persistence machinery
            field.values = field.values
            field.tales = field.tales
            field.message_values = field.message_values

    # delete default group
    if not has_default:
        form.move_group_down('Default')
        form.remove_group('Default')

    # delete fields which did exist before setting the values in XML
    # but are not present in the XML
    old_ids = [x for x in form.objectIds()
               if x not in form.get_field_ids(include_disabled=1)]
    if old_ids:
        form.manage_delObjects(old_ids)


def encode(text, encoding):
    if encoding is None:
        return text
    else:
        return text.encode(encoding)
