# w.c.s. - web application for online forms
# Copyright (C) 2005-2016  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 xml.etree.ElementTree as ET

from ..qommon import _
from ..qommon.form import (WidgetListAsTable, CompositeWidget, SingleSelectWidget,
        ComputedExpressionWidget)
from wcs.fields import WidgetField
from wcs.workflows import (WorkflowStatusItem,
        register_item_class)
from wcs.wf.profile import FieldNode


class SetBackofficeFieldRowWidget(CompositeWidget):
    def __init__(self, name, value=None, workflow=None, **kwargs):
        CompositeWidget.__init__(self, name, value, **kwargs)
        if not value:
            value = {}

        fields = [('', '', '')]
        for field in workflow.get_backoffice_fields():
            if not issubclass(field.__class__, WidgetField):
                continue
            fields.append((field.id, field.label, field.id))
        self.add(SingleSelectWidget, name='field_id', title=_('Field'),
                value=value.get('field_id'),
                options=fields, **kwargs)
        self.add(ComputedExpressionWidget, name='value', title=_('Value'),
                value=value.get('value'))

    def _parse(self, request):
        if self.get('value') and self.get('field_id'):
            self.value = {
                'value': self.get('value'),
                'field_id': self.get('field_id')
            }
        else:
            self.value = None


class SetBackofficeFieldsTableWidget(WidgetListAsTable):
    readonly = False
    def __init__(self, name, **kwargs):
        super(SetBackofficeFieldsTableWidget, self).__init__(name,
                element_type=SetBackofficeFieldRowWidget,
                element_kwargs={'workflow': kwargs.pop('workflow')},
                **kwargs)


class SetBackofficeFieldsWorkflowStatusItem(WorkflowStatusItem):
    description = N_('Backoffice Data')
    key = 'set-backoffice-fields'
    category = 'formdata-action'

    fields = None

    @classmethod
    def is_available(cls, workflow=None):
        return bool(workflow and getattr(workflow.backoffice_fields_formdef, 'fields', None))

    def get_parameters(self):
        return ('fields', 'condition')

    def add_parameters_widgets(self, form, parameters, prefix='', formdef=None):
        super(SetBackofficeFieldsWorkflowStatusItem, self).add_parameters_widgets(
                form, parameters, prefix=prefix, formdef=formdef)
        if 'fields' in parameters:
            form.add(SetBackofficeFieldsTableWidget, '%sfields' % prefix,
                    title=_('Fields Update'), value=self.fields,
                    workflow=self.parent.parent)

    def perform(self, formdata):
        if not self.fields:
            return
        for field in self.fields:
            try:
                formdef_field = [x for x in self.parent.parent.get_backoffice_fields()
                                 if x.id == field['field_id']][0]
            except IndexError:
                continue

            try:
                new_value = self.compute(field['value'], raises=True,
                                         formdata=formdata, status_item=self)
            except:
                continue

            if formdef_field.convert_value_from_anything:
                try:
                    new_value = formdef_field.convert_value_from_anything(new_value)
                except ValueError as e:
                    from wcs.logged_errors import LoggedError
                    summary = _('Failed to convert %(class)s value to %(kind)s field (%(id)s)') % {
                                    'class': type(new_value),
                                    'kind': _(getattr(formdef_field, 'description', 'unknown')),
                                    'id': field['field_id'],
                                }
                    expression_dict = self.get_expression(field['value'])
                    LoggedError.record(summary, formdata=formdata, status_item=self,
                                       expression=expression_dict['value'],
                                       expression_type=expression_dict['type'],
                                       exception=e)
                    continue

            formdata.data['%s' % field['field_id']] = new_value
            if formdef_field.store_display_value:
                display_value = formdef_field.store_display_value(
                        formdata.data, formdef_field.id)
                if display_value:
                    formdata.data['%s_display' % formdef_field.id] = display_value
                elif '%s_display' % formdef_field.id in formdata.data:
                    del formdata.data['%s_display' % formdef_field.id]
            if formdef_field.store_structured_value and formdata.data.get(formdef_field.id):
                structured_value = formdef_field.store_structured_value(
                        formdata.data, formdef_field.id)
                if structured_value:
                    if isinstance(structured_value, dict) and structured_value.get('id'):
                        # in case of list field, override id
                        formdata.data['%s' % formdef_field.id] = str(structured_value.get('id'))
                    formdata.data['%s_structured' % formdef_field.id] = structured_value
                elif '%s_structured' % formdef_field.id in formdata.data:
                    del formdata.data['%s_structured' % formdef_field.id]

            # store formdata everytime so substitution cache is invalidated,
            # and backoffice field values can be used in subsequent fields.
            formdata.store()

    def fields_export_to_xml(self, item, charset, include_id=False):
        if not self.fields:
            return

        fields_node = ET.SubElement(item, 'fields')
        for field in self.fields:
            fields_node.append(FieldNode(field).export_to_xml(charset=charset,
                include_id=include_id))

        return fields_node

    def fields_init_with_xml(self, elem, charset, include_id=False):
        fields = []
        if elem is None:
            return
        for field_xml_node in elem.findall('field'):
            field_node = FieldNode()
            field_node.init_with_xml(field_xml_node, charset,
                    include_id=include_id)
            fields.append(field_node.as_dict())
        if fields:
            self.fields = fields

register_item_class(SetBackofficeFieldsWorkflowStatusItem)
