import abjadext.book
import pytest
import types
from uqbar.strings import normalize


source_one = normalize(r'''
..  abjad::
    :hide:

    from abjad import *

::

    >>> string = 'Hello, world!'

..  import:: abjadext.book:example_function

::

    >>> print(string)
    Hello, world!

..  abjad::

    example_function(23)
''')


@pytest.fixture
def app():
    app = types.SimpleNamespace()
    config = types.SimpleNamespace()
    app.config = config
    app.config.abjadbook_ignored_documents = ()
    return app


def test_collect_abjad_input_blocks_01(app):
    handler = abjadext.book.SphinxDocumentHandler()
    document = handler.parse_rst(source_one)
    blocks = handler.collect_abjad_input_blocks(document)
    assert len(blocks) == 3
    nodes = tuple(blocks.keys())
    assert nodes[0] is document[0]
    assert nodes[1] is document[2]
    assert nodes[2] is document[4]


def test_collect_python_literal_blocks_01(app):
    handler = abjadext.book.SphinxDocumentHandler()
    document = handler.parse_rst(source_one)
    blocks = handler.collect_python_literal_blocks(
        document,
        renderable_only=False,
        )
    assert len(blocks) == 2
    nodes = tuple(blocks.keys())
    assert nodes[0] is document[1]
    assert nodes[1] is document[3]


def test_collect_python_literal_blocks_02(app):
    handler = abjadext.book.SphinxDocumentHandler()
    document = handler.parse_rst(source_one)
    blocks = handler.collect_python_literal_blocks(document)
    assert len(blocks) == 0


def test_on_doctree_read_01(app):
    handler = abjadext.book.SphinxDocumentHandler()
    document = handler.parse_rst(source_one)
    handler.on_doctree_read(app, document)
    actual = normalize(document.pformat())
    target = normalize(
        r"""
        <document source="test">
            <literal_block xml:space="preserve">
                >>> string = 'Hello, world!'
            <literal_block xml:space="preserve">
                def example_function(argument):
                    r'''This is a multiline docstring.

                    This is the third line of the docstring.
                    '''
                    # This is a comment.
                    print('Entering example function.')
                    try:
                        argument = argument + 1
                    except TypeError:
                        print('Wrong type!')
                    print(argument)
                    print('Leaving example function.')
            <literal_block xml:space="preserve">
                >>> print(string)
                Hello, world!
            <literal_block xml:space="preserve">
                >>> example_function(23)
                Entering example function.
                24
                Leaving example function.
        """)
    assert actual == target


def test_on_doctree_read_02(app):
    source = r'''
    ..  abjad::

        staff = Staff("c'1 g'1")
        for note in staff:
            abjad.show(note)

        len(staff)
    '''
    source = normalize(source)
    handler = abjadext.book.SphinxDocumentHandler()
    document = handler.parse_rst(source)
    handler.on_doctree_read(app, document)
    actual = normalize(document.pformat())
    target = normalize(
        r"""
        <document source="test">
            <literal_block xml:space="preserve">
                >>> staff = Staff("c'1 g'1")
                >>> for note in staff:
                ...     abjad.show(note)
                ...
            <abjad_output_block image_layout_specifier="True" image_render_specifier="ImageRenderSpecifier(stylesheet='default.ily')" renderer="lilypond" xml:space="preserve">
                \version "2.19.0"
                \language "english"

                #(ly:set-option 'relative-includes #t)

                \include "default.ily"

                \score {
                    {
                        c'1
                    }
                }
            <abjad_output_block image_layout_specifier="True" image_render_specifier="ImageRenderSpecifier(stylesheet='default.ily')" renderer="lilypond" xml:space="preserve">
                \version "2.19.0"
                \language "english"

                #(ly:set-option 'relative-includes #t)

                \include "default.ily"

                \score {
                    {
                        g'1
                    }
                }
            <literal_block xml:space="preserve">
                >>> len(staff)
                2
        """)
    assert actual == target


def test_on_doctree_read_03(app):
    source = r'''
    ..  abjad::
        :no-stylesheet:

        staff = Staff("c'1 g'1")
        for note in staff:
            abjad.show(note)

        len(staff)
    '''
    source = normalize(source)
    handler = abjadext.book.SphinxDocumentHandler()
    document = handler.parse_rst(source)
    handler.on_doctree_read(app, document)
    actual = normalize(document.pformat())
    target = normalize(
        r"""
        <document source="test">
            <literal_block xml:space="preserve">
                >>> staff = Staff("c'1 g'1")
                >>> for note in staff:
                ...     abjad.show(note)
                ...
            <abjad_output_block image_layout_specifier="True" image_render_specifier="ImageRenderSpecifier(no_stylesheet=True)" renderer="lilypond" xml:space="preserve">
                \version "2.19.0"
                \language "english"

                \score {
                    {
                        c'1
                    }
                }
            <abjad_output_block image_layout_specifier="True" image_render_specifier="ImageRenderSpecifier(no_stylesheet=True)" renderer="lilypond" xml:space="preserve">
                \version "2.19.0"
                \language "english"

                \score {
                    {
                        g'1
                    }
                }
            <literal_block xml:space="preserve">
                >>> len(staff)
                2
        """)
    assert actual == target


def test_on_doctree_read_04(app):
    source = r'''
    ..  container:: example

        Duple meter:

        ::

            >>> 1 + 1

        ::

            >>> meter = abjad.Meter((2, 4))
            >>> meter
            Meter('(2/4 (1/4 1/4))')
            >>> print(meter.pretty_rtm_format)
            (2/4 (
                1/4
                1/4))

        ::

            >>> graph(meter) # doctest: +SKIP

        `2/4` comprises two beats.

    ..  container:: example

        Triple meter:

        ::

            >>> meter = abjad.Meter((3, 4))
            >>> print(meter.pretty_rtm_format)
            (3/4 (
                1/4
                1/4
                1/4))

        ::

            >>> graph(meter) # doctest: +SKIP

        `3/4` comprises three beats.
    '''
    source = normalize(source)
    handler = abjadext.book.SphinxDocumentHandler()
    document = handler.parse_rst(source)
    handler.on_doctree_read(app, document)
    actual = normalize(document.pformat())
    target = normalize(
        r"""
        <document source="test">
            <container classes="example">
                <paragraph>
                    Duple meter:
                <literal_block xml:space="preserve">
                    >>> 1 + 1
                    2
                <literal_block xml:space="preserve">
                    >>> meter = abjad.Meter((2, 4))
                    >>> meter
                    Meter('(2/4 (1/4 1/4))')
                <literal_block xml:space="preserve">
                    >>> print(meter.pretty_rtm_format)
                    (2/4 (
                        1/4
                        1/4))
                <literal_block xml:space="preserve">
                    >>> graph(meter) # doctest: +SKIP
                <abjad_output_block image_layout_specifier="True" image_render_specifier="True" layout="dot" renderer="graphviz" xml:space="preserve">
                    digraph G {
                        graph [bgcolor=transparent,
                            fontname=Arial,
                            penwidth=2,
                            truecolor=true];
                        node [fontname=Arial,
                            fontsize=12,
                            penwidth=2];
                        edge [penwidth=2];
                        node_0 [label="2/4",
                            shape=triangle];
                        node_1 [label="1/4",
                            shape=box];
                        node_2 [label="1/4",
                            shape=box];
                        subgraph cluster_offsets {
                            graph [style=rounded];
                            node_3_0 [color=white,
                                fillcolor=black,
                                fontcolor=white,
                                fontname="Arial bold",
                                label="{ <f_0_0> 0 | <f_0_1> ++ }",
                                shape=Mrecord,
                                style=filled];
                            node_3_1 [color=white,
                                fillcolor=black,
                                fontcolor=white,
                                fontname="Arial bold",
                                label="{ <f_0_0> 1/4 | <f_0_1> + }",
                                shape=Mrecord,
                                style=filled];
                            node_3_2 [label="{ <f_0_0> 1/2 | <f_0_1> ++ }",
                                shape=Mrecord];
                        }
                        node_0 -> node_1;
                        node_0 -> node_2;
                        node_1 -> node_3_0 [style=dotted];
                        node_1 -> node_3_1 [style=dotted];
                        node_2 -> node_3_1 [style=dotted];
                        node_2 -> node_3_2 [style=dotted];
                    }
                <paragraph>
                    <title_reference>
                        2/4
                     comprises two beats.
            <container classes="example">
                <paragraph>
                    Triple meter:
                <literal_block xml:space="preserve">
                    >>> meter = abjad.Meter((3, 4))
                    >>> print(meter.pretty_rtm_format)
                    (3/4 (
                        1/4
                        1/4
                        1/4))
                <literal_block xml:space="preserve">
                    >>> graph(meter) # doctest: +SKIP
                <abjad_output_block image_layout_specifier="True" image_render_specifier="True" layout="dot" renderer="graphviz" xml:space="preserve">
                    digraph G {
                        graph [bgcolor=transparent,
                            fontname=Arial,
                            penwidth=2,
                            truecolor=true];
                        node [fontname=Arial,
                            fontsize=12,
                            penwidth=2];
                        edge [penwidth=2];
                        node_0 [label="3/4",
                            shape=triangle];
                        node_1 [label="1/4",
                            shape=box];
                        node_2 [label="1/4",
                            shape=box];
                        node_3 [label="1/4",
                            shape=box];
                        subgraph cluster_offsets {
                            graph [style=rounded];
                            node_4_0 [color=white,
                                fillcolor=black,
                                fontcolor=white,
                                fontname="Arial bold",
                                label="{ <f_0_0> 0 | <f_0_1> ++ }",
                                shape=Mrecord,
                                style=filled];
                            node_4_1 [color=white,
                                fillcolor=black,
                                fontcolor=white,
                                fontname="Arial bold",
                                label="{ <f_0_0> 1/4 | <f_0_1> + }",
                                shape=Mrecord,
                                style=filled];
                            node_4_2 [color=white,
                                fillcolor=black,
                                fontcolor=white,
                                fontname="Arial bold",
                                label="{ <f_0_0> 1/2 | <f_0_1> + }",
                                shape=Mrecord,
                                style=filled];
                            node_4_3 [label="{ <f_0_0> 3/4 | <f_0_1> ++ }",
                                shape=Mrecord];
                        }
                        node_0 -> node_1;
                        node_0 -> node_2;
                        node_0 -> node_3;
                        node_1 -> node_4_0 [style=dotted];
                        node_1 -> node_4_1 [style=dotted];
                        node_2 -> node_4_1 [style=dotted];
                        node_2 -> node_4_2 [style=dotted];
                        node_3 -> node_4_2 [style=dotted];
                        node_3 -> node_4_3 [style=dotted];
                    }
                <paragraph>
                    <title_reference>
                        3/4
                     comprises three beats.
        """)
    assert actual == target


def test_on_doctree_read_05(app):
    source = r'''
    ..  abjad::
        :text-width: 40

        import abjadext.book
        [x for x in dir(abjadext.book) if not x.startswith('_')]
    '''
    source = normalize(source)
    handler = abjadext.book.SphinxDocumentHandler()
    document = handler.parse_rst(source)
    handler.on_doctree_read(app, document)
    actual = normalize(document.pformat())
    target = normalize(
        r"""
        <document source="test">
            <literal_block xml:space="preserve">
                >>> import abjadext.book
                >>> [x for x in dir(abjadext.book) if not x.startswith('_')]
                ['AbjadBookConsole', 'AbjadBookError',
                'AbjadBookScript', 'AbjadDirective',
                'AbjadDoctestDirective', 'CodeBlock',
                'CodeBlockSpecifier', 'CodeOutputProxy',
                'GraphvizOutputProxy',
                'ImageLayoutSpecifier',
                'ImageOutputProxy',
                'ImageRenderSpecifier',
                'ImportDirective',
                'LaTeXDocumentHandler', 'LilyPondBlock',
                'LilyPondOutputProxy',
                'RawLilyPondOutputProxy',
                'RevealDirective', 'ShellDirective',
                'SphinxDocumentHandler',
                'ThumbnailDirective', 'directives',
                'example_function', 'proxies',
                'specifiers', 'sphinx']
        """)
    assert actual == target


def test_on_doctree_read_06(app):
    source = u'''
    This example demonstrates the power of exploiting redundancy to model
    musical structure. The piece that concerns us here is Ligeti's *Désordre*:
    the first piano study from Book I. Specifically, we will focus on modeling
    the first section of the piece.

    ..  abjad::

        print('Désordre')
    '''
    source = normalize(source)
    handler = abjadext.book.SphinxDocumentHandler()
    document = handler.parse_rst(source)
    handler.on_doctree_read(app, document)
    actual = normalize(document.pformat())
    target = normalize(
        u"""
        <document source="test">
            <paragraph>
                This example demonstrates the power of exploiting redundancy to model
                musical structure. The piece that concerns us here is Ligeti's
                <emphasis>
                    Désordre
                :
                the first piano study from Book I. Specifically, we will focus on modeling
                the first section of the piece.
            <literal_block xml:space="preserve">
                >>> print('Désordre')
                Désordre
        """)
    assert actual == target


def test_on_doctree_read_07(app):
    source = '''
    ..  abjad::
        :hide:
        :no-trim:
        :pages: 1-4
        :with-columns: 2

        abjad.show(Staff("c'4 d'4 e'4 f'4"))
    '''
    handler = abjadext.book.SphinxDocumentHandler()
    document = handler.parse_rst(source)
    handler.on_doctree_read(app, document)
    actual = normalize(document.pformat())
    target = normalize(
        r"""
        <document source="test">
            <block_quote>
                <abjad_output_block image_layout_specifier="ImageLayoutSpecifier(pages=(1, 2, 3, 4), with_columns=2)" image_render_specifier="ImageRenderSpecifier(no_trim=True, stylesheet='default.ily')" renderer="lilypond" xml:space="preserve">
                    \version "2.19.0"
                    \language "english"

                    #(ly:set-option 'relative-includes #t)

                    \include "default.ily"

                    \score {
                        \new Staff
                        {
                            c'4
                            d'4
                            e'4
                            f'4
                        }
                    }
        """)
    assert actual == target


def test_on_doctree_read_08(app):
    source = '''
    ..  abjad::

        staff = Staff("c'4 d'4 e'4 f'4")
        spanner = Slur()
        attach(spanner, staff[:])

    ..  abjad::

        for leaf in staff:
            is_exterior_leaf = spanner._is_exterior_leaf(leaf)
            is_interior_leaf = spanner._is_interior_leaf(leaf)
            print(repr(leaf), is_exterior_leaf, is_interior_leaf)
    '''
    handler = abjadext.book.SphinxDocumentHandler()
    document = handler.parse_rst(source)
    handler.on_doctree_read(app, document)
    actual = normalize(document.pformat())
    target = normalize(
        r"""
        <document source="test">
            <block_quote>
                <literal_block xml:space="preserve">
                    >>> staff = Staff("c'4 d'4 e'4 f'4")
                    >>> spanner = Slur()
                    >>> attach(spanner, staff[:])
                <literal_block xml:space="preserve">
                    >>> for leaf in staff:
                    ...     is_exterior_leaf = spanner._is_exterior_leaf(leaf)
                    ...     is_interior_leaf = spanner._is_interior_leaf(leaf)
                    ...     print(repr(leaf), is_exterior_leaf, is_interior_leaf)
                    ...
                    Note("c'4") True False
                    Note("d'4") False True
                    Note("e'4") False True
                    Note("f'4") True False
        """)
    assert actual == target


def test_on_doctree_read_09(app):
    source = '''
    ..  abjad::
        :strip-prompt:

        class Foo(object):

            def bar(self):
                print('OK')

            def baz(self):
                print('NO')

            def quux(self):
                return 23

    ..  abjad::

        foo = Foo()
        foo.quux()
    '''
    handler = abjadext.book.SphinxDocumentHandler()
    document = handler.parse_rst(source)
    handler.on_doctree_read(app, document)
    actual = normalize(document.pformat())
    target = normalize(
        r"""
        <document source="test">
            <block_quote>
                <literal_block xml:space="preserve">
                    class Foo(object):

                        def bar(self):
                            print('OK')

                        def baz(self):
                            print('NO')

                        def quux(self):
                            return 23
                <literal_block xml:space="preserve">
                    >>> foo = Foo()
                    >>> foo.quux()
                    23
        """)
    assert actual == target


def test_on_doctree_read_10(app):
    source = '''
    ..  abjad::
        :hide:
        :reveal-label: foo
        :strip-prompt:

        def foo(x):
            return x + 1

    ..  abjad::

        3 * 5

    ..  reveal:: foo

    ..  abjad::

        foo(23)
    '''
    handler = abjadext.book.SphinxDocumentHandler()
    document = handler.parse_rst(source)
    handler.on_doctree_read(app, document)
    actual = normalize(document.pformat())
    target = normalize(
        r"""
        <document source="test">
            <block_quote>
                <literal_block xml:space="preserve">
                    >>> 3 * 5
                    15
                <literal_block xml:space="preserve">
                    def foo(x):
                        return x + 1
                <literal_block xml:space="preserve">
                    >>> foo(23)
                    24
        """)
    assert actual == target
