# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2007 Nexedi SARL and Contributors. All Rights Reserved.
#          Fabien Morin <fabien.morin@gmail.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.ERP5SyncML.Conduit.ERP5Conduit import ERP5Conduit
from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions
import difflib

from zLOG import LOG

class VCardConduit(ERP5Conduit):
  """
  A conduit is in charge to read data from a particular structure,
  and then to save this data in another structure.

  VCardConduit is a piece of code to update VCards from text stream
  """


  # Declarative security
  security = ClassSecurityInfo()


  security.declareProtected(Permissions.ModifyPortalContent, 'addNode')
  def addNode(self, xml=None, object=None, previous_xml=None,
      object_id=None, sub_object=None, force=0, simulate=0, **kw):
    """
    add a new person corresponding to the vcard
    if the person already exist, she's updated
    """
    #LOG('VCardConduit',0,'addNode, object=%s, object_id=%s, sub_object:%s, \
        #xml:\n%s' % (str(object), str(object_id), str(sub_object), xml))
    if not isinstance(xml, str):
      xml = self.nodeToString(xml)
    portal_type = 'Person' #the VCard can just use Person
    if sub_object is None:

      new_object, reset_local_roles, reset_workflow =\
        ERP5Conduit.constructContent(self, object, object_id, portal_type)
    else: #if the object exist, it juste must be update
      new_object = sub_object
    #LOG('addNode', 0, 'new_object:%s, sub_object:%s' % (new_object, sub_object)) 
    self.updateNode(xml=xml,
                    object=new_object,
                    force=force,
                    simulate=simulate,
                    **kw)
    #in a first time, conflict are not used
    return {'conflict_list':[], 'object': new_object}

  security.declareProtected(Permissions.ModifyPortalContent, 'deleteNode')
  def deleteNode(self, xml=None, object=None, object_id=None, force=None,
      simulate=0, **kw):
    """
    A node is deleted
    """
    #LOG('deleteNode :', 0, 'object:%s, object_id:%s' % (str(object), str(object_id)))
    try:
      object._delObject(object_id)
    except (AttributeError, KeyError):
      LOG('VCardConduit',0,'deleteNode, Unable to delete: %s' % str(object_id))
    return []

  security.declareProtected(Permissions.ModifyPortalContent, 'updateNode')
  def updateNode(self, xml=None, object=None, previous_xml=None, force=0, 
      simulate=0,  **kw):
    """
    A node is updated
    """
    #LOG('updateNode :',0, 'xml:%s, object:%s, previous_xml:%s, force:%s,simulate:%s, kw:%s' % (xml, object, previous_xml, force, simulate, kw))
    vcard_dict = self.vcard2Dict(xml)
    object.edit(**vcard_dict)
    return []

  def getCapabilitiesCTTypeList(self):
    """
    return the a list of CTType capabilities supported
    """
    return ('text/xml', 'text/vcard', 'text/x-vcard',)

  def getCapabilitiesVerCTList(self, capabilities_ct_type):
    """
    return a list of version of the CTType supported
    """
    #add here the other version supported
    verCTTypeList = {}
    verCTTypeList['text/vcard'] = ('3.0',)
    verCTTypeList['text/x-vcard'] = ('2.1',)
    return verCTTypeList[capabilities_ct_type]

  def getPreferedCapabilitieVerCT(self):
    """
    return the prefered capabilitie VerCT
    """
    prefered_version = '2.1'
    return prefered_version

  def getPreferedCapabilitieCTType(self):
    """
    return the prefered capabilitie VerCT
    """
    prefered_type = 'text/x-vcard'
    return prefered_type
  
  def changePropertyEncoding(self, property_parameters_list, 
      property_value_list):
    """
    if there is a property 'ENCODING', change the string encoding to utf-8
    """
    encoding=''

    for item in property_parameters_list :
      if ENCODING in item:
        encoding = item['ENCODING']

    property_value_list_well_incoded=[]
    if encoding == 'QUOTED-PRINTABLE':
      import mimify
      for property_value in property_value_list:
        property_value = mimify.mime_decode(property_value)
        property_value_list_well_incoded.append(property_value)
    #elif ... put here the other encodings
    else:
      property_value_list_well_incoded = property_value_list

    return property_value_list_well_incoded

  def vcard2Dict(self, vcard):
    """
    transalate the vcard to a dict understandable by erp5 like
    {'fisrt_name':'MORIN', 'last_name':'Fabien'}
    """
    #LOG('vcard =',0,vcard)
    convert_dict = {}
    convert_dict['FN'] = 'first_name'
    convert_dict['N'] = 'last_name'
    convert_dict['TEL'] = 'default_telephone_text'
    edit_dict = {}
    vcard_list = vcard.split('\n')
    for vcard_line in vcard_list:
      if ':' in vcard_line:
        property, property_value = vcard_line.split(':')
        property_value_list = property_value.split(';')
        property_parameters_list = []
        property_name = ''
        if ';' in property:
          property_list = property.split(';')
          property_name = property_list[0] #the property name is the 1st element
          if len(property_list) > 1 and property_list[1] != '':
            property_parameters_list = property_list[1:len(property_list)]
            tmp = []
            for property_parameter in property_parameters_list:
              if '=' in property_parameter:
                property_parameter_name, property_parameter_value = \
                    property_parameter.split('=')
              else:
                property_parameter_name = property_parameter
                property_parameter_value = None
              tmp.append({property_parameter_name:property_parameter_value})
            property_parameters_list = tmp
            #now property_parameters_list looks like :
            # [{'ENCODING':'QUOTED-PRINTABLE'}, {'CHARSET':'UTF-8'}]

            property_value_list = \
                self.changePropertyEncoding(property_parameters_list,
                                            property_value_list)

        else:
          property_name=property
        if isinstance(property_name, unicode):
          property_name = property_name.encode('utf-8')

        tmp = []
        for property_value in property_value_list:
          if isinstance(property_value, unicode):
            property_value = property_value.encode('utf-8')
          tmp.append(property_value)
        property_value_list = tmp
        if property_name in convert_dict.keys():
          if property_name == 'N' and len(property_value_list) > 1:
            edit_dict[convert_dict['N']] = property_value_list[0]
            edit_dict[convert_dict['FN']] = property_value_list[1]
          else:
            edit_dict[convert_dict[property_name]] = property_value_list[0]
    #LOG('edit_dict =',0,edit_dict)
    return edit_dict

  security.declareProtected(Permissions.ModifyPortalContent,
                            'replaceIdFromXML')
  def replaceIdFromXML(self, xml, attribute_name, new_id, as_string=True):
    """
      Return the Same vlue
    """
    return xml

  def getContentType(self):
    """Content-Type of binded data
    """
    return 'text/vcard'

  def generateDiff(self, old_data, new_data):
    """return unified diff for plain-text documents
    """
    diff = '\n'.join(difflib.unified_diff(old_data.splitlines(),
                                          new_data.splitlines()))
    return diff


  def applyDiff(self, original_data, diff):
    """Use difflib to patch original_data
    """
    raise NotImplementedError('patch unified diff')