##############################################################################
#
# Copyright (c) 2007 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Page Drawing Related Element Processing
"""
import types
import reportlab.pdfbase.pdfform
from z3c.rml import attr, directive, interfaces, occurence

try:
    import reportlab.graphics.barcode
except ImportError:
    # barcode package has not been installed
    import types
    import reportlab.graphics
    reportlab.graphics.barcode = types.ModuleType('barcode')
    reportlab.graphics.barcode.getCodeNames = lambda : ()


class IBarCodeBase(interfaces.IRMLDirectiveSignature):
    """Create a bar code."""

    code = attr.Choice(
        title=u'Code',
        description=u'The name of the type of code to use.',
        choices=reportlab.graphics.barcode.getCodeNames(),
        required=True)

    value = attr.TextNode(
        title=u'Value',
        description=u'The value represented by the code.',
        required=True)

    width = attr.Measurement(
        title=u'Width',
        description=u'The width of the barcode.',
        required=False)

    height = attr.Measurement(
        title=u'Height',
        description=u'The height of the barcode.',
        required=False)

    barStrokeColor = attr.Color(
        title=u'Bar Stroke Color',
        description=(u'The color of the line strokes in the barcode.'),
        required=False)

    barStrokeWidth = attr.Measurement(
        title=u'Bar Stroke Width',
        description=u'The width of the line strokes in the barcode.',
        required=False)

    barFillColor = attr.Color(
        title=u'Bar Fill Color',
        description=(u'The color of the filled shapes in the barcode.'),
        required=False)

    gap = attr.Measurement(
        title=u'Gap',
        description=u'The width of the inter-character gaps.',
        required=False)

    # Bar code dependent attributes
    # I2of5, Code128, Standard93, FIM, POSTNET, Ean13B
    barWidth = attr.Measurement(
        title=u'Bar Width',
        description=u'The width of the smallest bar within the barcode',
        required=False)

    # I2of5, Code128, Standard93, FIM, POSTNET
    barHeight = attr.Measurement(
        title=u'Bar Height',
        description=u'The height of the symbol.',
        required=False)

    # I2of5
    ratio = attr.Float(
        title=u'Ratio',
        description=(u'The ratio of wide elements to narrow elements. '
                     u'Must be between 2.0 and 3.0 (or 2.2 and 3.0 if the '
                     u'barWidth is greater than 20 mils (.02 inch)).'),
        min=2.0,
        max=3.0,
        required=False)

    # I2of5
    # Should be boolean, but some code want it as int; will still work
    checksum = attr.Integer(
        title=u'Ratio',
        description=(u'A flag that enables the computation and inclusion of '
                     u'the check digit.'),
        required=False)

    # I2of5
    bearers = attr.Float(
        title=u'Bearers',
        description=(u'Height of bearer bars (horizontal bars along the top '
                     u'and bottom of the barcode). Default is 3 '
                     u'x-dimensions. Set to zero for no bearer bars.'
                     u'(Bearer bars help detect misscans, so it is '
                     u'suggested to leave them on).'),
        required=False)

    # I2of5, Code128, Standard93, FIM, Ean13
    quiet = attr.Boolean(
        title=u'Quiet Zone',
        description=(u'A flag to include quiet zones in the symbol.'),
        required=False)

    # I2of5, Code128, Standard93, FIM, Ean13
    lquiet = attr.Measurement(
        title=u'Left Quiet Zone',
        description=(u"Quiet zone size to the left of code, if quiet is "
                     u"true. Default is the greater of .25 inch or .15 times "
                     u"the symbol's length."),
        required=False)

    # I2of5, Code128, Standard93, FIM, Ean13
    rquiet = attr.Measurement(
        title=u'Right Quiet Zone',
        description=(u"Quiet zone size to the right of code, if quiet is "
                     u"true. Default is the greater of .25 inch or .15 times "
                     u"the symbol's length."),
        required=False)

    # I2of5, Code128, Standard93, FIM, POSTNET, Ean13
    fontName = attr.String(
        title=u'Font Name',
        description=(u'The font used to print the value.'),
        required=False)

    # I2of5, Code128, Standard93, FIM, POSTNET, Ean13
    fontSize = attr.Measurement(
        title=u'Font Size',
        description=(u'The size of the value text.'),
        required=False)

    # I2of5, Code128, Standard93, FIM, POSTNET, Ean13
    humanReadable = attr.Boolean(
        title=u'Human Readable',
        description=(u'A flag when set causes the value to be printed below '
                     u'the bar code.'),
        required=False)

    # I2of5, Standard93
    stop = attr.Boolean(
        title=u'Show Start/Stop',
        description=(u'A flag to specify whether the start/stop symbols '
                     u'are to be shown.'),
        required=False)

    # FIM, POSTNET
    spaceWidth = attr.Measurement(
        title=u'Space Width',
        description=u'The space of the inter-character gaps.',
        required=False)

    # POSTNET
    shortHeight = attr.Measurement(
        title=u'Short Height',
        description=u'The height of the short bar.',
        required=False)

    # Ean13
    textColor = attr.Color(
        title=u'Text Color',
        description=(u'The color of human readable text.'),
        required=False)

    # USPS4S
    routing = attr.String(
        title=u'Routing',
        description=u'The routing information string.',
        required=False)
    
    # QR
    barLevel = attr.Choice(
        title=u'Bar Level',
        description=u'The error correction level for QR code',
        choices=['L', 'M', 'Q', 'H'],
        required=False)


class IBarCode(IBarCodeBase):
    """A barcode graphic."""

    x = attr.Measurement(
        title=u'X-Position',
        description=u'The x-position of the lower-left corner of the barcode.',
        default=0,
        required=False)

    y = attr.Measurement(
        title=u'Y-Position',
        description=u'The y-position of the lower-left corner of the barcode.',
        default=0,
        required=False)

    isoScale = attr.Boolean(
        title=u'Isometric Scaling',
        description=u'When set, the aspect ration of the barcode is enforced.',
        required=False)

class BarCode(directive.RMLDirective):
    signature = IBarCode

    def process(self):
        kw = dict(self.getAttributeValues())
        name = kw.pop('code')
        kw['value'] = str(kw['value'])
        x = kw.pop('x', 0)
        y = kw.pop('y', 0)
        code = reportlab.graphics.barcode.createBarcodeDrawing(name, **kw)
        manager = attr.getManager(self, interfaces.ICanvasManager)
        code.drawOn(manager.canvas, x, y)


class IField(interfaces.IRMLDirectiveSignature):
    """A field."""

    title = attr.Text(
        title=u'Title',
        description=u'The title of the field.',
        required=True)

    x = attr.Measurement(
        title=u'X-Position',
        description=u'The x-position of the lower-left corner of the field.',
        default=0,
        required=True)

    y = attr.Measurement(
        title=u'Y-Position',
        description=u'The y-position of the lower-left corner of the field.',
        default=0,
        required=True)


class Field(directive.RMLDirective):
    signature = IField
    callable = None
    attrMapping = {}

    def process(self):
        kwargs = dict(self.getAttributeValues(attrMapping=self.attrMapping))
        canvas = attr.getManager(self, interfaces.ICanvasManager).canvas
        getattr(reportlab.pdfbase.pdfform, self.callable)(canvas, **kwargs)


class ITextField(IField):
    """A text field within the PDF"""

    width = attr.Measurement(
        title=u'Width',
        description=u'The width of the text field.',
        required=True)

    height = attr.Measurement(
        title=u'Height',
        description=u'The height of the text field.',
        required=True)

    value = attr.Text(
        title=u'Value',
        description=u'The default text value of the field.',
        required=False)

    maxLength = attr.Integer(
        title=u'Maximum Length',
        description=u'The maximum amount of characters allowed in the field.',
        required=False)

    multiline = attr.Boolean(
        title=u'Multiline',
        description=u'A flag when set allows multiple lines within the field.',
        required=False)

class TextField(Field):
    signature = ITextField
    callable = 'textFieldAbsolute'
    attrMapping = {'maxLength': 'maxlen'}


class IButtonField(IField):
    """A button field within the PDF"""

    value = attr.Choice(
        title=u'Value',
        description=u'The value of the button.',
        choices=('Yes', 'Off'),
        required=True)

class ButtonField(Field):
    signature = IButtonField
    callable = 'buttonFieldAbsolute'


class IOption(interfaces.IRMLDirectiveSignature):
    """An option in the select field."""

    value = attr.TextNode(
        title=u'Value',
        description=u'The value of the option.',
        required=True)

class Option(directive.RMLDirective):
    signature = IOption

    def process(self):
        value = self.getAttributeValues(valuesOnly=True)[0]
        self.parent.options.append(value)


class ISelectField(IField):
    """A selection field within the PDF"""
    occurence.containing(
        occurence.ZeroOrMore('option', IOption))

    width = attr.Measurement(
        title=u'Width',
        description=u'The width of the select field.',
        required=True)

    height = attr.Measurement(
        title=u'Height',
        description=u'The height of the select field.',
        required=True)

    value = attr.Text(
        title=u'Value',
        description=u'The default value of the field.',
        required=False)

class SelectField(Field):
    signature = ISelectField
    callable = 'selectFieldAbsolute'
    factories = {'option': Option}

    def process(self):
        self.options = []
        self.processSubDirectives()
        kwargs = dict(self.getAttributeValues(attrMapping=self.attrMapping))
        kwargs['options'] = self.options
        canvas = attr.getManager(self, interfaces.ICanvasManager).canvas
        getattr(reportlab.pdfbase.pdfform, self.callable)(canvas, **kwargs)
