# Path: tinycsscheme/tinycss/page3.py
# 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,
#                     '%s rule not allowed in %s' % (rule.at_keyword, context))
#             if rule.head:
#                 raise ParseError(rule.head[0],
#                     'unexpected %s token in %s rule header'
#                     % (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')
# 
# Path: tinycsscheme/tinycss/tests/test_tokenizer.py
# def jsonify(tokens):
#     """Turn tokens into "JSON-compatible" data structures."""
#     for token in tokens:
#         if token.type == 'FUNCTION':
#             yield (token.type, token.function_name,
#                    list(jsonify(token.content)))
#         elif token.is_container:
#             yield token.type, list(jsonify(token.content))
#         else:
#             yield token.type, token.value


import pytest
from ..page3 import CSSPage3Parser
from .test_tokenizer import jsonify
from . import assert_errors
@pytest.mark.parametrize(('css', 'expected_declarations',
                          'expected_rules','expected_errors'), [
    ('@page {}', [], [], []),
    ('@page { foo: 4; bar: z }',
        [('foo', [('INTEGER', 4)]), ('bar', [('IDENT', 'z')])], [], []),
    ('''@page { foo: 4;
                @top-center { content: "Awesome Title" }
                @bottom-left { content: counter(page) }
                bar: z
        }''',
        [('foo', [('INTEGER', 4)]), ('bar', [('IDENT', 'z')])],
        [('@top-center', [('content', [('STRING', 'Awesome Title')])]),
         ('@bottom-left', [('content', [
            ('FUNCTION', 'counter', [('IDENT', 'page')])])])],
        []),
    ('''@page { foo: 4;
                @bottom-top { content: counter(page) }
                bar: z
        }''',
        [('foo', [('INTEGER', 4)]), ('bar', [('IDENT', 'z')])],
        [],
        ['unknown at-rule in @page context: @bottom-top']),

    ('@page{} @top-right{}', [], [], [
        '@top-right rule not allowed in stylesheet']),
    ('@page{ @top-right 4 {} }', [], [], [
        'unexpected INTEGER token in @top-right rule header']),
    # Not much error recovery tests here. This should be covered in test_css21
])
def test_content(css, expected_declarations, expected_rules, expected_errors):

# Path: tinycsscheme/tinycss/page3.py
# 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,
#                     '%s rule not allowed in %s' % (rule.at_keyword, context))
#             if rule.head:
#                 raise ParseError(rule.head[0],
#                     'unexpected %s token in %s rule header'
#                     % (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')
# 
# Path: tinycsscheme/tinycss/tests/test_tokenizer.py
# def jsonify(tokens):
#     """Turn tokens into "JSON-compatible" data structures."""
#     for token in tokens:
#         if token.type == 'FUNCTION':
#             yield (token.type, token.function_name,
#                    list(jsonify(token.content)))
#         elif token.is_container:
#             yield token.type, list(jsonify(token.content))
#         else:
#             yield token.type, token.value


# Path: tinycsscheme/tinycss/tests/test_page3.py
import pytest
from ..page3 import CSSPage3Parser
from .test_tokenizer import jsonify
from . import assert_errors

@pytest.mark.parametrize(('css', 'expected_declarations',
                          'expected_rules','expected_errors'), [
    ('@page {}', [], [], []),
    ('@page { foo: 4; bar: z }',
        [('foo', [('INTEGER', 4)]), ('bar', [('IDENT', 'z')])], [], []),
    ('''@page { foo: 4;
                @top-center { content: "Awesome Title" }
                @bottom-left { content: counter(page) }
                bar: z
        }''',
        [('foo', [('INTEGER', 4)]), ('bar', [('IDENT', 'z')])],
        [('@top-center', [('content', [('STRING', 'Awesome Title')])]),
         ('@bottom-left', [('content', [
            ('FUNCTION', 'counter', [('IDENT', 'page')])])])],
        []),
    ('''@page { foo: 4;
                @bottom-top { content: counter(page) }
                bar: z
        }''',
        [('foo', [('INTEGER', 4)]), ('bar', [('IDENT', 'z')])],
        [],
        ['unknown at-rule in @page context: @bottom-top']),

    ('@page{} @top-right{}', [], [], [
        '@top-right rule not allowed in stylesheet']),
    ('@page{ @top-right 4 {} }', [], [], [
        'unexpected INTEGER token in @top-right rule header']),
    # Not much error recovery tests here. This should be covered in test_css21
])
def test_content(css, expected_declarations, expected_rules, expected_errors):

    stylesheet = CSSPage3Parser().parse_stylesheet(css)