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

from ..qommon import _
from ..qommon.form import *
from ..qommon.template import Template
from ..qommon import get_logger
from wcs.roles import Role, get_user_roles
from wcs.workflows import XmlSerialisable, WorkflowStatusItem, register_item_class


class AutomaticDispatchRowWidget(CompositeWidget):
    def __init__(self, name, value=None, **kwargs):
        CompositeWidget.__init__(self, name, value, **kwargs)
        if not value:
            value = {}
        self.add(StringWidget, name='value', title=_('Value'),
                value=value.get('value'), **kwargs)
        self.add(SingleSelectWidget, name='role_id', title=_('Role'),
                value=value.get('role_id'),
                options=[(None, '----', None)] + get_user_roles())

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


class AutomaticDispatchTableWidget(WidgetListAsTable):
    readonly = False
    def __init__(self, name, **kwargs):
        super(AutomaticDispatchTableWidget, self).__init__(name,
                element_type=AutomaticDispatchRowWidget, **kwargs)


class RuleNode(XmlSerialisable):
    node_name = 'rule'

    def __init__(self, rule={}):
        self.role_id = rule.get('role_id')
        self.value = rule.get('value')

    def as_dict(self):
        return {
            'role_id': self.role_id,
            'value': self.value
        }

    def get_parameters(self):
        return ('role_id', 'value')

    def role_id_export_to_xml(self, item, charset, include_id=False):
        self._role_export_to_xml('role_id', item, charset,
                include_id=include_id)

    def role_id_init_with_xml(self, elem, charset, include_id=False):
        self._role_init_with_xml('role_id', elem, charset,
                include_id=include_id)


class DispatchWorkflowStatusItem(WorkflowStatusItem):
    description = N_('Function/Role Linking')
    key = 'dispatch'
    category = 'formdata-action'

    role_id = None
    role_key = None
    dispatch_type = 'manual'
    variable = None
    rules = None

    def get_parameters(self):
        return ('role_key', 'dispatch_type', 'role_id', 'variable', 'rules',
                'condition')

    def role_id_export_to_xml(self, item, charset, include_id=False):
        self._role_export_to_xml('role_id', item, charset,
                include_id=include_id)

    def role_id_init_with_xml(self, elem, charset, include_id=False):
        self._role_init_with_xml('role_id', elem, charset,
                include_id=include_id)

    def rules_export_to_xml(self, item, charset, include_id=False):
        if self.dispatch_type != 'automatic' or not self.rules:
            return

        rules_node = ET.SubElement(item, 'rules')
        for rule in self.rules:
            rules_node.append(RuleNode(rule).export_to_xml(charset=charset,
                include_id=include_id))

        return rules_node

    def rules_init_with_xml(self, elem, charset, include_id=False):
        rules = []
        if elem is None:
            return
        for rule_xml_node in elem.findall('rule'):
            rule_node = RuleNode()
            rule_node.init_with_xml(rule_xml_node, charset,
                    include_id=include_id)
            rules.append(rule_node.as_dict())
        if rules:
            self.rules = rules

    def get_line_details(self):
        if self.role_key:
            function_label = self.parent.parent.roles.get(self.role_key, '?')
            return function_label
        return None

    def add_parameters_widgets(self, form, parameters, prefix='', formdef=None):
        super(DispatchWorkflowStatusItem, self).add_parameters_widgets(
                form, parameters, prefix=prefix, formdef=formdef)
        if 'role_key' in parameters:
            if not self.parent.parent.roles:
                self.parent.parent.roles = {}
            form.add(SingleSelectWidget, '%srole_key' % prefix,
                     title=_('Function to Set'), value=self.role_key,
                     options=[(None, '----')] + list(self.parent.parent.roles.items()))
        dispatch_types = collections.OrderedDict(
                [('manual', _('Simple')), ('automatic', _('Multiple'))])
        if 'dispatch_type' in parameters:
            form.add(RadiobuttonsWidget, '%sdispatch_type' % prefix,
                    title=_('Dispatch Type'),
                    options=list(dispatch_types.items()),
                    value=self.dispatch_type,
                    required=True,
                    attrs={'data-dynamic-display-parent': 'true'})
        if 'role_id' in parameters:
            form.add(SingleSelectWidgetWithOther, '%srole_id' % prefix,
                     title=_('Role'),
                     value=str(self.role_id) if self.role_id else None,
                     options=[(None, '---', None)] + get_user_roles(),
                     attrs={
                         'data-dynamic-display-child-of': '%sdispatch_type' % prefix,
                         'data-dynamic-display-value': dispatch_types.get('manual'),
                         }
                     )
        if 'variable' in parameters:
            form.add(StringWidget, '%svariable' % prefix,
                    title=_('Variable'),
                    value=self.variable,
                    attrs={
                        'data-dynamic-display-child-of': '%sdispatch_type' % prefix,
                        'data-dynamic-display-value': dispatch_types.get('automatic'),
                        }
                    )
        if 'rules' in parameters:
            form.add(AutomaticDispatchTableWidget, '%srules' % prefix,
                    title=_('Rules'),
                    value=self.rules,
                    attrs={
                        'data-dynamic-display-child-of': '%sdispatch_type' % prefix,
                        'data-dynamic-display-value': dispatch_types.get('automatic'),
                        }
                    )

    def perform(self, formdata):
        if not formdata.workflow_roles:
            formdata.workflow_roles = {}

        new_role_id = None

        if self.dispatch_type == 'manual' or not self.dispatch_type:
            if not (self.role_id and self.role_key):
                return
            new_role_id = self.get_computed_role_id(self.role_id)
            if not new_role_id:
                get_logger().error('error in dispatch, missing role (%s)' % self.role_id)
        elif self.dispatch_type == 'automatic':
            if not (self.role_key and self.variable and self.rules):
                return
            variable_values = []
            if Template.is_template_string(self.variable):
                variable_values = [self.compute(self.variable, formdata=formdata, status_item=self)]
            else:
                # legacy, self.variable as a straight variable name
                variables = get_publisher().substitutions.get_context_variables()
                # convert the given value to a few different types, to allow more
                # diversity in matching.
                variable_values = [variables.get(self.variable)]
                if not variable_values[0]:
                    variable_values.append(None)
                try:
                    variable_values.append(int(variable_values[0]))
                except (ValueError, TypeError):
                    pass

            for rule in self.rules:
                if rule.get('value') in variable_values:
                    new_role_id = rule.get('role_id')
                    break

        if new_role_id:
            if not Role.has_key(new_role_id):
                get_logger().error('error in dispatch, missing role %s' % new_role_id)
            else:
                formdata.workflow_roles[self.role_key] = str(new_role_id)
                formdata.store()

register_item_class(DispatchWorkflowStatusItem)
