# Source code for tinycss.page3

    # coding: utf-8
    """
        tinycss.page3
        ------------------

        Support for CSS 3 Paged Media syntax:
        http://dev.w3.org/csswg/css3-page/

        Adds support for named page selectors and margin rules.

        :copyright: (c) 2012 by Simon Sapin.
        :license: BSD, see LICENSE for more details.
    """

    from __future__ import division, unicode_literals

    from .css21 import CSS21Parser, ParseError

    class MarginRule(object):
        """A parsed at-rule for margin box.

        .. attribute:: at_keyword

            One of the 16 following strings:

            * ``@top-left-corner``
            * ``@top-left``
            * ``@top-center``
            * ``@top-right``
            * ``@top-right-corner``
            * ``@bottom-left-corner``
            * ``@bottom-left``
            * ``@bottom-center``
            * ``@bottom-right``
            * ``@bottom-right-corner``
            * ``@left-top``
            * ``@left-middle``
            * ``@left-bottom``
            * ``@right-top``
            * ``@right-middle``
            * ``@right-bottom``

        .. attribute:: declarations

            A list of :class:`~.css21.Declaration` objects.

        .. attribute:: line

            Source line where this was read.

        .. attribute:: column

            Source column where this was read.

        """

        def __init__(self, at_keyword, declarations, line, column):
            self.at_keyword = at_keyword
            self.declarations = declarations
            self.line = line
            self.column = column

    class CSSPage3Parser(CSS21Parser):
        """Extend :class:`~.css21.CSS21Parser` for `CSS 3 Paged Media`_ syntax.

        .. _CSS 3 Paged Media: http://dev.w3.org/csswg/css3-page/

        Compared to CSS 2.1, the ``at_rules`` and ``selector`` attributes of
        :class:`~.css21.PageRule` objects are modified:

        * ``at_rules`` is not always empty, it is a list of :class:`MarginRule`
          objects.

        * ``selector``, instead of a single string, is a tuple of the page name
          and the pseudo class. Each of these may be a ``None`` or a string.

        +--------------------------+------------------------+
        | CSS                      | Parsed selectors       |
        +==========================+========================+
        | .. code-block:: css      | .. code-block:: python |
        |                          |                        |
        |     @page {}             |     (None, None)       |
        |     @page :first {}      |     (None, 'first')    |
        |     @page chapter {}     |     ('chapter', None)  |
        |     @page table:right {} |     ('table', 'right') |
        +--------------------------+------------------------+

        """

        PAGE_MARGIN_AT_KEYWORDS = [
            '@top-left-corner',
            '@top-left',
            '@top-center',
            '@top-right',
            '@top-right-corner',
            '@bottom-left-corner',
            '@bottom-left',
            '@bottom-center',
            '@bottom-right',
            '@bottom-right-corner',
            '@left-top',
            '@left-middle',
            '@left-bottom',
            '@right-top',
            '@right-middle',
            '@right-bottom',
        ]

        def parse_at_rule(self, rule, previous_rules, errors, context):
            if rule.at_keyword in self.PAGE_MARGIN_AT_KEYWORDS:
                if context != '@page':
                    raise ParseError(
                        rule, '{0} rule not allowed in {1}'.format(
                            rule.at_keyword, context))
                if rule.head:
                    raise ParseError(
                        rule.head[0],
                        'unexpected {0} token in {1} rule header'.format(
                            rule.head[0].type, rule.at_keyword))
                declarations, body_errors = self.parse_declaration_list(rule.body)
                errors.extend(body_errors)
                return MarginRule(
                    rule.at_keyword, declarations, rule.line, rule.column)
            return super(CSSPage3Parser, self).parse_at_rule(
                rule, previous_rules, errors, context)

        def parse_page_selector(self, head):
            """Parse an @page selector.

            :param head:
                The ``head`` attribute of an unparsed :class:`AtRule`.
            :returns:
                A page selector. For CSS 2.1, this is 'first', 'left', 'right'
                or None. 'blank' is added by GCPM.
            :raises:
                :class`~parsing.ParseError` on invalid selectors

            """
            if not head:
                return (None, None), (0, 0, 0)
            if head[0].type == 'IDENT':
                name = head.pop(0).value
                while head and head[0].type == 'S':
                    head.pop(0)
                if not head:
                    return (name, None), (1, 0, 0)
                name_specificity = (1,)
            else:
                name = None
                name_specificity = (0,)
            if (len(head) == 2 and head[0].type == ':' and
                    head[1].type == 'IDENT'):
                pseudo_class = head[1].value
                specificity = {
                    'first': (1, 0), 'blank': (1, 0),
                    'left': (0, 1), 'right': (0, 1),
                }.get(pseudo_class)
                if specificity:
                    return (name, pseudo_class), (name_specificity + specificity)
            raise ParseError(head[0], 'invalid @page selector')