# 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 xml.etree.ElementTree as ET

from ..qommon import _
from ..qommon.form import *

from wcs.workflows import WorkflowStatusItem, register_item_class
from wcs.formdef import FormDef, lax_int

from wcs.admin.fields import FieldDefPage, FieldsDirectory
from wcs.formdata import get_dict_with_varnames

from wcs.forms.common import FileDirectory

def lookup_wf_form_file(self, filename):
    # supports for URLs such as /$formdata/$id/files/form-$formvar-$fieldvar/test.txt
    try:
        literal, formvar, fieldvar = self.reference.split('-')
    except ValueError:
        return
    if literal != 'form':
        return
    try:
        return self.formdata.workflow_data['%s_var_%s_raw' % (formvar, fieldvar)]
    except KeyError:
        return


class WorkflowFormFieldsFormDef(FormDef):
    lightweight = False

    def __init__(self, item):
        self.item = item
        self.fields = []
        self.id = None

    @property
    def name(self):
        return _('Form action in workflow "%s"') % self.item.parent.parent.name

    def get_admin_url(self):
        base_url = get_publisher().get_backoffice_url()
        return '%s/workflows/%s/status/%s/items/%s/fields/' % (
            base_url, self.item.parent.parent.id, self.item.parent.id, self.item.id)

    def store(self):
        self.item.parent.parent.store()


class WorkflowFormFieldDefPage(FieldDefPage):
    section = 'workflows'
    blacklisted_attributes = ['display_locations']

    def get_deletion_extra_warning(self):
        return None


class WorkflowFormFieldsDirectory(FieldsDirectory):
    section = 'workflows'
    support_import = False
    blacklisted_types = ['page']
    field_def_page_class = WorkflowFormFieldDefPage


class FormWorkflowStatusItem(WorkflowStatusItem):
    description = N_('Form')
    key = 'form'
    category = 'interaction'
    ok_in_global_action = False
    endpoint = False
    waitpoint = True

    by = []
    formdef = None
    varname = None

    @classmethod
    def init(cls):
        if not 'lookup_wf_form_file' in FileDirectory._lookup_methods:
            FileDirectory._lookup_methods.append('lookup_wf_form_file')
            FileDirectory.lookup_wf_form_file = lookup_wf_form_file

    def add_parameters_widgets(self, form, parameters, prefix='', formdef=None):
        super(FormWorkflowStatusItem, self).add_parameters_widgets(
                form, parameters, prefix=prefix, formdef=formdef)
        if 'by' in parameters:
            form.add(WidgetList, '%sby' % prefix, title = _('To'), element_type = SingleSelectWidget,
                value = self.by,
                add_element_label = _('Add Role'),
                element_kwargs = {'render_br': False,
                                  'options': [(None, '---', None)] +
                                              self.get_list_of_roles(include_logged_in_users=False)})
        if 'varname' in parameters:
            form.add(VarnameWidget, '%svarname' % prefix, required=True,
                     title=_('Identifier'), value=self.varname,
                     hint=_('This is used as prefix for form fields variable names.'))

            form.widgets.append(HtmlWidget(htmltext('<p><a href="fields/">%s</a></p>') % _('Edit Fields')))

    def get_parameters(self):
        return ('by', 'varname', 'condition')

    def migrate(self):
        changed = False
        if self.formdef and self.formdef.fields:
            for field in self.formdef.fields:
                changed |= field.migrate()
        return changed

    def export_to_xml(self, charset, include_id=False):
        item = WorkflowStatusItem.export_to_xml(self, charset, include_id=include_id)
        if not hasattr(self, 'formdef') or not self.formdef or not self.formdef.fields:
            return item
        formdef = ET.SubElement(item, 'formdef')

        # we give a name to the formdef because it is required in the formdef
        # xml import.
        ET.SubElement(formdef, 'name').text = '-'

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

    def init_with_xml(self, elem, charset, include_id=False):
        WorkflowStatusItem.init_with_xml(self, elem, charset)
        el = elem.find('formdef')
        if el is None:
            return
        # we can always include id in the formdef export as it lives in
        # a different space, isolated from other formdefs.
        imported_formdef = FormDef.import_from_xml_tree(el, include_id=True)
        self.formdef = WorkflowFormFieldsFormDef(item=self)
        self.formdef.fields = imported_formdef.fields
        if self.formdef.max_field_id is None and self.formdef.fields:
            self.formdef.max_field_id = max([lax_int(x.id) for x in self.formdef.fields])

    def q_admin_lookup(self, workflow, status, component, html_top):
        if component == 'fields':
            if not self.formdef:
                self.formdef = WorkflowFormFieldsFormDef(item=self)
            fields_directory = WorkflowFormFieldsDirectory(self.formdef)
            if self.varname:
                fields_directory.field_var_prefix = '%s_var_' % self.varname
            fields_directory.html_top = html_top
            return fields_directory
        return None

    def fill_form(self, form, formdata, user, displayed_fields=None, **kwargs):
        if not self.formdef:
            return
        self.formdef.var_prefix = self.varname
        self.formdef.add_fields_to_form(form, displayed_fields=displayed_fields)
        form.add_submit('submit', _('Submit'))

        # put varname in a form attribute so it can be used in templates to
        # identify the form.
        form.varname = self.varname

        formdata.feed_session()

        req = get_request()
        self.formdef.set_live_condition_sources(form, self.formdef.fields)

        fields = self.formdef.fields
        if displayed_fields is not None:
            fields = displayed_fields

        for field in fields:
            if ('f%s' % field.id) in req.form:
                continue
            if not field.prefill or field.prefill.get('type') == 'none':
                continue
            # FIXME: this code duplicates code from wcs/forms/root.py :/
            prefill_user = get_request().user
            if get_request().is_in_backoffice():
                prefill_user = get_publisher().substitutions.get_context_variables(
                        ).get('form_user')
            v, verified = field.get_prefill_value(user=prefill_user)
            if get_request().is_in_backoffice() and (
                    field.prefill and field.prefill.get('type') == 'geoloc'):
                # turn off prefilling from geolocation attributes if
                # the form is filled from the backoffice
                v = None
            if v:
                form.get_widget('f%s' % field.id).set_value(v)
                req.form['f%s' % field.id] = v

    def evaluate_live_form(self, form, formdata, user):
        workflow_data = {}
        for k, v in get_dict_with_varnames(
                        self.formdef.fields, self.formdef.get_data(form),
                        varnames_only=True).items():
            workflow_data['%s_%s' % (self.varname, k)] = v
        formdata.update_workflow_data(workflow_data)

    def submit_form(self, form, formdata, user, evo):
        if not self.formdef:
            return
        if form.get_submit() == 'submit' and not form.has_errors():
            self.evaluate_live_form(form, formdata, user)
            formdata.store()

    def get_parameters_view(self):
        r = TemplateIO(html=True)
        r += super(FormWorkflowStatusItem, self).get_parameters_view()
        if self.formdef and self.formdef.fields:
            r += htmltext('<p>%s</p>') % _('Form:')
            r += htmltext('<ul>')
            for field in self.formdef.fields:
                r += htmltext('<li>')
                r += field.label
                if getattr(field, 'required', True):
                    r += htmltext(' (%s)') % _('required')
                r += htmltext(' (%s)') % _(field.description)
                if field.varname:
                    r += htmltext(' (<tt>%s</tt>)') % field.varname
                r += htmltext('</li>')
            r += htmltext('</ul>')
        return r.getvalue()

register_item_class(FormWorkflowStatusItem)
