##############################################################################
#
# Copyright (c) 2012 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.
#
##############################################################################
"""``ul``, ``ol``, and ``li`` directives.
"""
__docformat__ = "reStructuredText"
import copy

import reportlab.lib.styles
import reportlab.platypus
import zope.schema
from reportlab.platypus import flowables  # noqa: F401 imported but unused

from z3c.rml import attr
from z3c.rml import directive
from z3c.rml import flowable
from z3c.rml import interfaces
from z3c.rml import occurence
from z3c.rml import stylesheet


class IListItem(stylesheet.IMinimalListStyle, flowable.IFlow):
    """A list item in an ordered or unordered list."""

    style = attr.Style(
        title='Style',
        description='The list style that is applied to the list.',
        required=False)


class ListItem(flowable.Flow):
    signature = IListItem
    klass = reportlab.platypus.ListItem
    attrMapping = {}

    styleAttributes = zope.schema.getFieldNames(stylesheet.IMinimalListStyle)

    def processStyle(self, style):
        attrs = self.getAttributeValues(select=self.styleAttributes)
        if attrs or not hasattr(style, 'value'):
            style = copy.deepcopy(style)
            # Sigh, this is needed since unordered list items expect the value.
            style.value = style.start
            for name, value in attrs:
                setattr(style, name, value)
        return style

    def process(self):
        self.processSubDirectives()
        args = dict(self.getAttributeValues(ignore=self.styleAttributes))
        if 'style' not in args:
            args['style'] = self.parent.baseStyle
        args['style'] = self.processStyle(args['style'])
        li = self.klass(self.flow, **args)
        self.parent.flow.append(li)


class IOrderedListItem(IListItem):
    """An ordered list item."""

    value = attr.Integer(
        title='Bullet Value',
        description='The counter value.',
        required=False)


class OrderedListItem(ListItem):
    signature = IOrderedListItem


class IUnorderedListItem(IListItem):
    """An ordered list item."""

    value = attr.Combination(
        title='Bullet Value',
        description='The type of bullet character.',
        value_types=(
            attr.Choice(choices=interfaces.UNORDERED_BULLET_VALUES),
            attr.Text(max_length=1)
        ),
        required=False)


class UnorderedListItem(ListItem):
    signature = IUnorderedListItem

    styleAttributes = ListItem.styleAttributes + ['value']


class IListBase(stylesheet.IBaseListStyle):

    style = attr.Style(
        title='Style',
        description='The list style that is applied to the list.',
        required=False)


class ListBase(directive.RMLDirective):
    klass = reportlab.platypus.ListFlowable
    factories = {'li': ListItem}
    attrMapping = {}

    styleAttributes = zope.schema.getFieldNames(stylesheet.IBaseListStyle)

    def __init__(self, *args, **kw):
        super().__init__(*args, **kw)
        self.flow = []

    def processStyle(self, style):
        attrs = self.getAttributeValues(
            select=self.styleAttributes, attrMapping=self.attrMapping)
        if attrs:
            style = copy.deepcopy(style)
            for name, value in attrs:
                setattr(style, name, value)
        return style

    def process(self):
        args = dict(self.getAttributeValues(
            ignore=self.styleAttributes, attrMapping=self.attrMapping))
        if 'style' not in args:
            args['style'] = reportlab.lib.styles.ListStyle('List')
        args['style'] = self.baseStyle = self.processStyle(args['style'])
        self.processSubDirectives()
        li = self.klass(self.flow, **args)
        self.parent.flow.append(li)


class IOrderedList(IListBase):
    """An ordered list."""
    occurence.containing(
        occurence.ZeroOrMore('li', IOrderedListItem),
    )

    bulletType = attr.Choice(
        title='Bullet Type',
        description='The type of bullet formatting.',
        choices=interfaces.ORDERED_LIST_TYPES,
        doLower=False,
        required=False)


class OrderedList(ListBase):
    signature = IOrderedList
    factories = {'li': OrderedListItem}

    styleAttributes = ListBase.styleAttributes + ['bulletType']


class IUnorderedList(IListBase):
    """And unordered list."""
    occurence.containing(
        occurence.ZeroOrMore('li', IUnorderedListItem),
    )

    value = attr.Choice(
        title='Bullet Value',
        description='The type of bullet character.',
        choices=interfaces.UNORDERED_BULLET_VALUES,
        default='circle',
        required=False)


class UnorderedList(ListBase):
    signature = IUnorderedList
    attrMapping = {'value': 'start'}
    factories = {'li': UnorderedListItem}

    def getAttributeValues(self, *args, **kw):
        res = super().getAttributeValues(*args, **kw)
        res.append(('bulletType', 'bullet'))
        return res


flowable.Flow.factories['ol'] = OrderedList
flowable.IFlow.setTaggedValue(
    'directives',
    flowable.IFlow.getTaggedValue('directives') +
    (occurence.ZeroOrMore('ol', IOrderedList),)
)

flowable.Flow.factories['ul'] = UnorderedList
flowable.IFlow.setTaggedValue(
    'directives',
    flowable.IFlow.getTaggedValue('directives') +
    (occurence.ZeroOrMore('ul', IUnorderedList),)
)
