##############################################################################
#
# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
#                    Jean-Paul Smets-Solanes <jp@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.DummyField import fields
from lxml.etree import Element
from Acquisition import aq_base
from lxml import etree
from decimal import Decimal
import re

DRAW_URI = 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0'
TEXT_URI = 'urn:oasis:names:tc:opendocument:xmlns:text:1.0'
XLINK_URI = 'http://www.w3.org/1999/xlink'
SVG_URI = 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0'


NSMAP = {
          'draw': DRAW_URI,
          'text': TEXT_URI,
          'xlink': XLINK_URI,
          'svg': SVG_URI
        }

class ImageFieldWidget(Widget.TextWidget):
    """ImageField widget.

    Renders an HTML <img> element where the src is the 'default' field value.
    The 'description' field value is used as 'alt' attribute.
    The image size is calculated using 'image_display'.
    """
    property_names = Widget.TextWidget.property_names + \
      ['image_display', 'image_format','image_resolution']

    image_display = fields.StringField('image_display',
                               title='Image Display',
                               description=(
        "The display size. See ERP5.Document.Image.default_displays_id_list "
        "for possible values. This is only used with ERP5 Images."),
                               default='thumbnail',
                               required=1)

    image_format = fields.StringField('image_format',
                               title='Image Format',
                               description=(
        "The format in which the image should be converted to. "
        "This is only used with ERP5 Images."),
                               default='',
                               required=0)

    image_resolution = fields.IntegerField('image_resolution',
                               title='Image Resolution',
                               description=(
        "The resolution used when converting the image. "
        "This is only used with ERP5 Images."),
                               default=75,
                               required=0)

    def render(self, field, key, value, REQUEST, render_prefix=None):
        """Render image field as a link to the image
        """
        return self.render_view(field, value, REQUEST=REQUEST)

    def render_view(self, field, value, REQUEST=None, render_prefix=None):
        """Render image field as a link to the image
        """
        # Url is already defined in value
        if value is None:
          return ''
        image = value
        alt = field.get_value('description') or \
              field.get_value('title')
        css_class = field.get_value('css_class')
        extra = field.get_value('extra')
        display = field.get_value('image_display')
        format = field.get_value('image_format')
        resolution = field.get_value('image_resolution')
        return Widget.render_element(
            "img",
            alt=alt,
            src="%s?display=%s&format=%s&" % (image, display, format),
            css_class=css_class,
            extra=extra,
        )

    def render_odg(self, field, value, as_string, ooo_builder, REQUEST,
        render_prefix, attr_dict):
      """
        return an image xml node rendered in odg format
        if as_string is True (default) the returned value is a string (xml
        reprensation of the node), if it's False, the value returned is the node
        object.
        attr_dict can be used for additional parameters (like style).
      """
      if attr_dict is None:
        attr_dict = {}
      draw_frame_node = None
      if value in ('', None):
        return None
      path = '/'.join(REQUEST.physicalPathFromURL(value))
      path = path.encode()
      image_object = field.getPortalObject().restrictedTraverse(path)
      display = field.get_value('image_display')
      format = field.get_value('image_format')
      image_parameter_dict = { 'display': display,
                               'format':format}
      # convert the image using fields parameters. In this way, if an image
      # is displayed in the form as a thumbnail, it will be added in the odg
      # document as thumbnail size/quality
      content_type, image_data = image_object.convert(**image_parameter_dict)
      if image_data is None:
        return draw_frame_node

      format = content_type.split('/')[-1]
      # add the image to the odg document
      picture_path = ooo_builder.addImage(image=image_data, format=format)

      # create the xml nodes related to the image
      draw_frame_tag_name = '{%s}%s' % (DRAW_URI, 'frame')
      draw_frame_node = Element(draw_frame_tag_name, nsmap=NSMAP)
      draw_frame_node.attrib.update(attr_dict.get(draw_frame_tag_name,
        {}).pop(0))

      # set the size of the image
      if display is not None:
        height, width = image_object.getSizeFromImageDisplay(display)
        if draw_frame_node.attrib.get('{%s}width' % SVG_URI, {}) != {} and \
        draw_frame_node.attrib.get('{%s}height' % SVG_URI, {}) != {}:
          # if a size already exist from attr_dict, try to resize the image to
          # fit this size (image should not be biger than size from attr_dict)
          # devide the value by 20 to have cm instead of px
          width, height = self._getPictureSize(width/20., height/20.,
              target_width=draw_frame_node.attrib.get('{%s}width' % SVG_URI, {}),
              target_height=draw_frame_node.attrib.get('{%s}height' % SVG_URI, {}))

        draw_frame_node.set('{%s}width' % SVG_URI, str(width))
        draw_frame_node.set('{%s}height' % SVG_URI, str(height))

      image_tag_name = '{%s}%s' % (DRAW_URI, 'image')
      image_node = Element(image_tag_name, nsmap=NSMAP)
      image_node.attrib.update(attr_dict.get(image_tag_name, {}).pop())
      image_node.set('{%s}href' % XLINK_URI, picture_path)

      draw_frame_node.append(image_node)
      if as_string:
        return etree.tostring(draw_frame_node)
      return draw_frame_node

    def _getPictureSize(self, picture_width, picture_height, target_width,
        target_height):
      # if not match causes exception
      width_tuple = re.match("(\d[\d\.]*)(.*)", target_width).groups()
      height_tuple = re.match("(\d[\d\.]*)(.*)", target_height).groups()
      unit = width_tuple[1]
      w = float(width_tuple[0])
      h = float(height_tuple[0])
      aspect_ratio = 1
      try: # try image properties
        aspect_ratio = picture_width / picture_height
      except (TypeError, ZeroDivisionError):
        try: # try ERP5.Document.Image API
          height = picture_height
          if height:
            aspect_ratio = picture_width / height
        except AttributeError: # fallback to Photo API
          height = float(picture_height)
          if height:
            aspect_ratio = picture_width / height
      resize_w = h * aspect_ratio
      resize_h = w / aspect_ratio
      if resize_w < w:
        w = resize_w
      elif resize_h < h:
        h = resize_h
      return (str(w) + unit, str(h) + unit)

ImageFieldWidgetInstance = ImageFieldWidget()
ImageFieldValidatorInstance = Validator.StringValidator()

class ImageField(ZMIField):
    meta_type = "ImageField"

    widget = ImageFieldWidgetInstance
    validator = ImageFieldValidatorInstance