##############################################################################
#
# Copyright (c) 2005, 2006 Nexedi SARL and Contributors. All Rights Reserved.
#                    Romain Courteaud <romain@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
##############################################################################

from Products.Formulator import Widget, Validator
from Products.Formulator.Field import ZMIField
from Products.Formulator import StandardFields 
from Products.Formulator.DummyField import fields
from Products.PythonScripts.Utility import allow_class

from zLOG import LOG
from AccessControl import ClassSecurityInfo
from Products.Formulator.Errors import ValidationError

class ParallelListWidget(Widget.MultiListWidget):
    """
      Make the MultilistField more usable for the user.

      ParallelListWidget display a list of (Multi)ListField.
      Each can be required.

      Separation of items list is made with a Hash Script, which take 
      the items list in input, and return a list of dictionnaries.

      Each dictionnary describes a (Multi)Listfield.
      The keys are:
        - key: 
            default: default
        - required: {1, 0}  
            default: 0
        - field_type: {ListField, MultiListField}
            default: MultiListField
        - item_list: [(display, value), ...]
            default: []
        - value: 
            default: []
        - is_right_display: {1, 0}
            default: 0
    """

    property_names = Widget.MultiListWidget.property_names + \
      ['hash_script_id']

    hash_script_id = fields.StringField('hash_script_id',
                               title='Hash script',
                               description=(
        "The method to call to hash items list."),
                               required=0)

    def __init__(self):
      """
      Generate some subwidget used for rendering.
      """
      self.sub_widget = {
        'ListField': Widget.ListWidgetInstance,
        'MultiListField': Widget.MultiListWidgetInstance,
      }

    def render(self, field, key, value, REQUEST):
      hash_list = field._generateSubForm(value, REQUEST)
      # Call render on each sub field
      sub_field_render_list = []
      for sub_field_property_dict in hash_list:
        sub_field_render_list.append(self.render_sub_field(
                          field, key,
                          sub_field_property_dict['value'], REQUEST,
                          sub_field_property_dict))
      # Aggregate all renders
      html_string = field.get_value('view_separator').\
                                join(sub_field_render_list)
      return html_string

    def render_htmlgrid(self, field, key, value, REQUEST):
      hash_list = field._generateSubForm(value, REQUEST)
      # Call render on each sub field
      sub_field_render_list = []
      for sub_field_property_dict in hash_list:
        sub_field_render_list.append((
                          sub_field_property_dict['title'],
                          self.render_sub_field(
                            field, key,
                            sub_field_property_dict['value'], REQUEST,
                            sub_field_property_dict)))
      return sub_field_render_list

    def render_sub_field(self, field, key, value, REQUEST,
                        sub_field_property_dict):
      """
      Render dynamically a subfield
      """
      REQUEST.set('_v_plf_title', sub_field_property_dict['title'])
      REQUEST.set('_v_plf_required', sub_field_property_dict['required'])
      REQUEST.set('_v_plf_default', "")
      REQUEST.set('_v_plf_first_item', 0)
      REQUEST.set('_v_plf_items', sub_field_property_dict['item_list'])
      REQUEST.set('_v_plf_size', sub_field_property_dict['size'])
      return self.sub_widget[sub_field_property_dict['field_type']].render(
              field,
              field.generate_subfield_key(sub_field_property_dict['key'],
                                          key=key),
              sub_field_property_dict['value'],
              REQUEST)

class ParallelListValidator(Validator.MultiSelectionValidator):

  property_names = Validator.MultiSelectionValidator.property_names 

  def __init__(self):
    """
    Generate some subvalidator used for rendering.
    """
    self.sub_validator = {
      'ListField': Validator.SelectionValidatorInstance,
      'MultiListField': Validator.MultiSelectionValidatorInstance,
    }

  def validate(self, field, key, REQUEST):    

    result_list = []
    hash_list = field._generateSubForm(None, REQUEST)
    is_sub_field_required = 0
    for sub_field_property_dict in hash_list:
      try:
        sub_result_list = self.validate_sub_field(
                                  field,
                                  field.generate_subfield_key(
                                      sub_field_property_dict['key'], 
                                      validation=1, key=key),
                                  REQUEST,
                                  sub_field_property_dict)
        if not isinstance(sub_result_list, (list, tuple)):
          sub_result_list = [sub_result_list]
        else:
          sub_result_list = list(sub_result_list)
        result_list.extend(sub_result_list)
      except ValidationError:
        is_sub_field_required = 1
    
    result_list = [x for x in result_list if x!='']
    if result_list == []:
      if field.get_value('required'):
        self.raise_error('required_not_found', field)
    else:
      if is_sub_field_required:
        self.raise_error('required_not_found', field)
    return result_list

  def validate_sub_field(self, field, id, REQUEST, sub_field_property_dict):
    """
    Validates a subfield (as part of field validation).
    """
    REQUEST.set('_v_plf_title', sub_field_property_dict['title'])
    REQUEST.set('_v_plf_required', sub_field_property_dict['required'])
    REQUEST.set('_v_plf_default', "")
    REQUEST.set('_v_plf_items', sub_field_property_dict['item_list'])
    REQUEST.set('_v_plf_size', sub_field_property_dict['size'])
    return self.sub_validator[sub_field_property_dict['field_type']].validate(
        field, id, REQUEST)

ParallelListWidgetInstance = ParallelListWidget()
ParallelListFieldValidatorInstance = ParallelListValidator()

class ParallelListField(ZMIField):
  security = ClassSecurityInfo()
  meta_type = "ParallelListField"

  widget = ParallelListWidgetInstance
  validator = ParallelListFieldValidatorInstance 

  def render_htmlgrid(self, value=None, REQUEST=None, key=None):
    """
    render_htmlgrid returns a list of tuple (title, html render)
    We will use title generated by the widget.
    """
    key = self.generate_field_key(key=key)
    value = self._get_default(key, value, REQUEST)
    html = self.widget.render_htmlgrid(self, key, value, REQUEST)
    return html

  def _generateSubForm(self, value, REQUEST):
    item_list = [x for x in self.get_value('items') \
                 if x not in (('',''), ['',''])]

    value_list = value
    if not isinstance(value_list, (list, tuple)):
      value_list = [value_list]

    empty_sub_field_property_dict = {
      'key': 'default',
      'title': self.get_value('title'),
      'required': 0,
      'field_type': 'MultiListField',
      'item_list': [],
      'value': [],
      'is_right_display': 0,
      'size': 5
    }

    hash_list = []
    hash_script_id = self.get_value('hash_script_id')
    if hash_script_id not in [None, '']:
      script = getattr(self, hash_script_id)
      script_hash_list = script(
              item_list,
              value_list,
              default_sub_field_property_dict=empty_sub_field_property_dict,
              is_right_display=0)
      hash_list.extend(script_hash_list)
    else:
      # No hash_script founded, generate a little hash_script 
      # to display only a MultiListField
      default_sub_field_property_dict = empty_sub_field_property_dict.copy()
      default_sub_field_property_dict.update({
          'item_list': item_list,
          'value': value_list,
      })
      hash_list.append(default_sub_field_property_dict)
    # XXX Clean up old ParallelListField
    try:
      delattr(self, 'sub_form')
    except KeyError:
      pass
    return hash_list

  security.declareProtected('Access contents information', 'get_value')
  def get_value(self, id, **kw):
    """
    Get value for id.
    Optionally pass keyword arguments that get passed to TALES
    expression.
    """
    REQUEST = kw.get('REQUEST')
    key = '_v_plf_%s' % id
    if (REQUEST is not None) and \
       (REQUEST.has_key(key)):
      result = REQUEST.get(key)
    else:
      result = ZMIField.get_value(self, id, **kw)
    return result