"""Tests for floating boxes layout."""

import pytest

from weasyprint.formatting_structure import boxes

from ..testing_utils import assert_no_logs, render_pages


def outer_area(box):
    """Return the (x, y, w, h) rectangle for the outer area of a box."""
    return (box.position_x, box.position_y,
            box.margin_width(), box.margin_height())


@assert_no_logs
def test_floats_1():
    # adjacent-floats-001
    page, = render_pages('''
      <style>
        div { float: left }
        img { width: 100px; vertical-align: top }
      </style>
      <div><img src=pattern.png /></div>
      <div><img src=pattern.png /></div>''')
    html, = page.children
    body, = html.children
    div_1, div_2 = body.children
    assert outer_area(div_1) == (0, 0, 100, 100)
    assert outer_area(div_2) == (100, 0, 100, 100)


@assert_no_logs
def test_floats_2():
    # c414-flt-fit-000
    page, = render_pages('''
      <style>
        body { width: 290px }
        div { float: left; width: 100px;  }
        img { width: 60px; vertical-align: top }
      </style>
      <div><img src=pattern.png /><!-- 1 --></div>
      <div><img src=pattern.png /><!-- 2 --></div>
      <div><img src=pattern.png /><!-- 4 --></div>
      <img src=pattern.png /><!-- 3
      --><img src=pattern.png /><!-- 5 -->''')
    html, = page.children
    body, = html.children
    div_1, div_2, div_4, anon_block = body.children
    line_3, line_5 = anon_block.children
    img_3, = line_3.children
    img_5, = line_5.children
    assert outer_area(div_1) == (0, 0, 100, 60)
    assert outer_area(div_2) == (100, 0, 100, 60)
    assert outer_area(img_3) == (200, 0, 60, 60)

    assert outer_area(div_4) == (0, 60, 100, 60)
    assert outer_area(img_5) == (100, 60, 60, 60)


@assert_no_logs
def test_floats_3():
    # c414-flt-fit-002
    page, = render_pages('''
      <style type="text/css">
        body { width: 200px }
        p { width: 70px; height: 20px }
        .left { float: left }
        .right { float: right }
      </style>
      <p class="left"> ⇦ A 1 </p>
      <p class="left"> ⇦ B 2 </p>
      <p class="left"> ⇦ A 3 </p>
      <p class="right"> B 4 ⇨ </p>
      <p class="left"> ⇦ A 5 </p>
      <p class="right"> B 6 ⇨ </p>
      <p class="right"> B 8 ⇨ </p>
      <p class="left"> ⇦ A 7 </p>
      <p class="left"> ⇦ A 9 </p>
      <p class="left"> ⇦ B 10 </p>
    ''')
    html, = page.children
    body, = html.children
    positions = [(paragraph.position_x, paragraph.position_y)
                 for paragraph in body.children]
    assert positions == [
        (0, 0), (70, 0), (0, 20), (130, 20), (0, 40), (130, 40),
        (130, 60), (0, 60), (0, 80), (70, 80), ]


@assert_no_logs
def test_floats_4():
    # c414-flt-wrap-000 ... more or less
    page, = render_pages('''
      <style>
        body { width: 100px }
        p { float: left; height: 100px }
        img { width: 60px; vertical-align: top }
      </style>
      <p style="width: 20px"></p>
      <p style="width: 100%"></p>
      <img src=pattern.png /><img src=pattern.png />
    ''')
    html, = page.children
    body, = html.children
    p_1, p_2, anon_block = body.children
    line_1, line_2 = anon_block.children
    assert anon_block.position_y == 0
    assert (line_1.position_x, line_1.position_y) == (20, 0)
    assert (line_2.position_x, line_2.position_y) == (0, 200)


@assert_no_logs
def test_floats_5():
    # c414-flt-wrap-000 with text ... more or less
    page, = render_pages('''
      <style>
        body { width: 100px; font: 60px weasyprint; }
        p { float: left; height: 100px }
        img { width: 60px; vertical-align: top }
      </style>
      <p style="width: 20px"></p>
      <p style="width: 100%"></p>
      A B
    ''')
    html, = page.children
    body, = html.children
    p_1, p_2, anon_block = body.children
    line_1, line_2 = anon_block.children
    assert anon_block.position_y == 0
    assert (line_1.position_x, line_1.position_y) == (20, 0)
    assert (line_2.position_x, line_2.position_y) == (0, 200)


@assert_no_logs
def test_floats_6():
    # floats-placement-vertical-001b
    page, = render_pages('''
      <style>
        body { width: 90px; font-size: 0 }
        img { vertical-align: top }
      </style>
      <body>
      <span>
        <img src=pattern.png style="width: 50px" />
        <img src=pattern.png style="width: 50px" />
        <img src=pattern.png style="float: left; width: 30px" />
      </span>
    ''')
    html, = page.children
    body, = html.children
    line_1, line_2 = body.children
    span_1, = line_1.children
    span_2, = line_2.children
    img_1, = span_1.children
    img_2, img_3 = span_2.children
    assert outer_area(img_1) == (0, 0, 50, 50)
    assert outer_area(img_2) == (30, 50, 50, 50)
    assert outer_area(img_3) == (0, 50, 30, 30)


@assert_no_logs
def test_floats_7():
    # Variant of the above: no <span>
    page, = render_pages('''
      <style>
        body { width: 90px; font-size: 0 }
        img { vertical-align: top }
      </style>
      <body>
      <img src=pattern.png style="width: 50px" />
      <img src=pattern.png style="width: 50px" />
      <img src=pattern.png style="float: left; width: 30px" />
    ''')
    html, = page.children
    body, = html.children
    line_1, line_2 = body.children
    img_1, = line_1.children
    img_2, img_3 = line_2.children
    assert outer_area(img_1) == (0, 0, 50, 50)
    assert outer_area(img_2) == (30, 50, 50, 50)
    assert outer_area(img_3) == (0, 50, 30, 30)


@assert_no_logs
def test_floats_8():
    # Floats do no affect other pages
    page_1, page_2 = render_pages('''
      <style>
        body { width: 90px; font-size: 0 }
        img { vertical-align: top }
      </style>
      <body>
      <img src=pattern.png style="float: left; width: 30px" />
      <img src=pattern.png style="width: 50px" />
      <div style="page-break-before: always"></div>
      <img src=pattern.png style="width: 50px" />
    ''')
    html, = page_1.children
    body, = html.children
    float_img, anon_block, = body.children
    line, = anon_block.children
    img_1, = line.children
    assert outer_area(float_img) == (0, 0, 30, 30)
    assert outer_area(img_1) == (30, 0, 50, 50)

    html, = page_2.children
    body, = html.children
    div, anon_block = body.children
    line, = anon_block.children
    img_2, = line.children


@assert_no_logs
def test_floats_9():
    # Regression test
    # https://github.com/Kozea/WeasyPrint/issues/263
    page, = render_pages('''<div style="top:100%; float:left">''')


@assert_no_logs
def test_floats_page_breaks_1():
    # Tests floated images shorter than the page
    pages = render_pages('''
      <style>
        @page { size: 100px; margin: 10px }
        img { height: 45px; width:70px; float: left;}
      </style>
      <body>
        <img src=pattern.png>
          <!-- page break should be here !!! -->
        <img src=pattern.png>
    ''')

    assert len(pages) == 2

    page_images = []
    for page in pages:
        images = [d for d in page.descendants() if d.element_tag == 'img']
        assert all([img.element_tag == 'img' for img in images])
        assert all([img.position_x == 10 for img in images])
        page_images.append(images)
    positions_y = [[img.position_y for img in images]
                   for images in page_images]
    assert positions_y == [[10], [10]]


@assert_no_logs
def test_floats_page_breaks_2():
    # Tests floated images taller than the page
    pages = render_pages('''
      <style>
        @page { size: 100px; margin: 10px }
        img { height: 81px; width:70px; float: left;}
      </style>
      <body>
        <img src=pattern.png>
          <!-- page break should be here !!! -->
        <img src=pattern.png>
    ''')

    assert len(pages) == 2

    page_images = []
    for page in pages:
        images = [d for d in page.descendants() if d.element_tag == 'img']
        assert all([img.element_tag == 'img' for img in images])
        assert all([img.position_x == 10 for img in images])
        page_images.append(images)
    positions_y = [[img.position_y for img in images]
                   for images in page_images]
    assert positions_y == [[10], [10]]


@assert_no_logs
def test_floats_page_breaks_3():
    # Tests floated images shorter than the page
    pages = render_pages('''
      <style>
        @page { size: 100px; margin: 10px }
        img { height: 30px; width:70px; float: left;}
      </style>
      <body>
        <img src=pattern.png>
        <img src=pattern.png>
          <!-- page break should be here !!! -->
        <img src=pattern.png>
        <img src=pattern.png>
          <!-- page break should be here !!! -->
        <img src=pattern.png>
    ''')

    assert len(pages) == 3

    page_images = []
    for page in pages:
        images = [d for d in page.descendants() if d.element_tag == 'img']
        assert all([img.element_tag == 'img' for img in images])
        assert all([img.position_x == 10 for img in images])
        page_images.append(images)
    positions_y = [[img.position_y for img in images]
                   for images in page_images]
    assert positions_y == [[10, 40], [10, 40], [10]]


@assert_no_logs
def test_preferred_widths_1():
    def get_float_width(body_width):
        page, = render_pages('''
          <body style="width: %spx; font-family: weasyprint">
          <p style="white-space: pre-line; float: left">
            Lorem ipsum dolor sit amet,
              consectetur elit
          </p>
                   <!--  ^  No-break space here  -->
        ''' % body_width)
        html, = page.children
        body, = html.children
        paragraph, = body.children
        return paragraph.width
    # Preferred minimum width:
    assert get_float_width(10) == len('consectetur elit') * 16
    # Preferred width:
    assert get_float_width(1000000) == len('Lorem ipsum dolor sit amet,') * 16


@assert_no_logs
def test_preferred_widths_2():
    # Non-regression test:
    # Incorrect whitespace handling in preferred width used to cause
    # unnecessary line break.
    page, = render_pages('''
      <p style="float: left">Lorem <em>ipsum</em> dolor.</p>
    ''')
    html, = page.children
    body, = html.children
    paragraph, = body.children
    assert len(paragraph.children) == 1
    assert isinstance(paragraph.children[0], boxes.LineBox)


@assert_no_logs
def test_preferred_widths_3():
    page, = render_pages('''
      <style>img { width: 20px }</style>
      <p style="float: left">
        <img src=pattern.png><img src=pattern.png><br>
        <img src=pattern.png></p>
    ''')
    html, = page.children
    body, = html.children
    paragraph, = body.children
    assert paragraph.width == 40


@assert_no_logs
def test_preferred_widths_4():
    page, = render_pages(
        '<style>'
        '  p { font: 20px weasyprint }'
        '</style>'
        '<p style="float: left">XX<br>XX<br>X</p>')
    html, = page.children
    body, = html.children
    paragraph, = body.children
    assert paragraph.width == 40


@assert_no_logs
def test_preferred_widths_5():
    # The space is the start of the line is collapsed.
    page, = render_pages(
        '<style>'
        '  p { font: 20px weasyprint }'
        '</style>'
        '<p style="float: left">XX<br> XX<br>X</p>')
    html, = page.children
    body, = html.children
    paragraph, = body.children
    assert paragraph.width == 40


@assert_no_logs
def test_float_in_inline_1():
    page, = render_pages('''
      <style>
        body {
          font-family: weasyprint;
          font-size: 20px;
        }
        p {
          width: 14em;
          text-align: justify;
        }
        span {
          float: right;
        }
      </style>
      <p>
        aa bb <a><span>cc</span> ddd</a> ee ff
      </p>
    ''')
    html, = page.children
    body, = html.children
    paragraph, = body.children
    line1, line2 = paragraph.children

    p1, a, p2 = line1.children
    assert p1.width == 6 * 20
    assert p1.text == 'aa bb '
    assert p1.position_x == 0 * 20
    assert p2.width == 3 * 20
    assert p2.text == ' ee'
    assert p2.position_x == 9 * 20
    span, a_text = a.children
    assert a_text.width == 3 * 20  # leading space collapse
    assert a_text.text == 'ddd'
    assert a_text.position_x == 6 * 20
    assert span.width == 2 * 20
    assert span.children[0].children[0].text == 'cc'
    assert span.position_x == 12 * 20

    p3, = line2.children
    assert p3.width == 2 * 20


@assert_no_logs
def test_float_in_inline_2():
    page, = render_pages('''
      <style>
        @page {
          size: 10em;
        }
        article {
          font-family: weasyprint;
          line-height: 1;
        }
        div {
          float: left;
          width: 50%;
        }
      </style>
      <article>
        <span>
          <div>a b c</div>
          1 2 3 4 5 6
        </span>
      </article>''')
    html, = page.children
    body, = html.children
    article, = body.children
    line1, line2 = article.children
    span1, = line1.children
    div, text = span1.children
    assert div.children[0].children[0].text.strip() == 'a b c'
    assert text.text.strip() == '1 2 3'
    span2, = line2.children
    text, = span2.children
    assert text.text.strip() == '4 5 6'


@assert_no_logs
def test_float_in_inline_3():
    page, = render_pages('''
      <style>
        @page {
          size: 10em;
        }
        article {
          font-family: weasyprint;
          line-height: 1;
        }
        div {
          float: left;
          width: 50%;
        }
      </style>
      <article>
        <span>
          1 2 3 <div>a b c</div> 4 5 6
        </span>
      </article>''')
    html, = page.children
    body, = html.children
    article, = body.children
    line1, line2 = article.children
    span1, = line1.children
    text, div = span1.children
    assert text.text.strip() == '1 2 3'
    assert div.children[0].children[0].text.strip() == 'a b c'
    span2, = line2.children
    text, = span2.children
    assert text.text.strip() == '4 5 6'


@assert_no_logs
def test_float_in_inline_4():
    page, = render_pages('''
      <style>
        @page {
          size: 10em;
        }
        article {
          font-family: weasyprint;
          line-height: 1;
        }
        div {
          float: left;
          width: 50%;
        }
      </style>
      <article>
        <span>
          1 2 3 4 <div>a b c</div> 5 6
        </span>
      </article>''')
    html, = page.children
    body, = html.children
    article, = body.children
    line1, line2 = article.children
    span1, div = line1.children
    text1, text2 = span1.children
    assert text1.text.strip() == '1 2 3 4'
    assert text2.text.strip() == '5'
    assert div.position_y == 16
    assert div.children[0].children[0].text.strip() == 'a b c'
    span2, = line2.children
    text, = span2.children
    assert text.text.strip() == '6'


@assert_no_logs
def test_float_next_line():
    page, = render_pages('''
      <style>
        body {
          font-family: weasyprint;
          font-size: 20px;
        }
        p {
          text-align: justify;
          width: 13em;
        }
        span {
          float: left;
        }
      </style>
      <p>pp pp pp pp <a><span>ppppp</span> aa</a> pp pp pp pp pp</p>''')
    html, = page.children
    body, = html.children
    paragraph, = body.children
    line1, line2, line3 = paragraph.children
    assert len(line1.children) == 1
    assert len(line3.children) == 1
    a, p = line2.children
    span, a_text = a.children
    assert span.position_x == 0
    assert span.width == 5 * 20
    assert a_text.position_x == a.position_x == 5 * 20
    assert a_text.width == a.width == 2 * 20
    assert p.position_x == 7 * 20


@assert_no_logs
def test_float_text_indent_1():
    page, = render_pages('''
      <style>
        body {
          font-family: weasyprint;
          font-size: 20px;
        }
        p {
          text-align: justify;
          text-indent: 1em;
          width: 14em;
        }
        span {
          float: left;
        }
      </style>
      <p><a>aa <span>float</span> aa</a></p>''')
    html, = page.children
    body, = html.children
    paragraph, = body.children
    line1, = paragraph.children
    a, = line1.children
    a1, span, a2 = a.children
    span_text, = span.children
    assert span.position_x == span_text.position_x == 0
    assert span.width == span_text.width == (
        (1 + 5) * 20)  # text-indent + span text
    assert a1.width == 3 * 20
    assert a1.position_x == (1 + 5 + 1) * 20  # span + a1 text-indent
    assert a2.width == 2 * 20  # leading space collapse
    assert a2.position_x == (1 + 5 + 1 + 3) * 20  # span + a1 t-i + a1


@assert_no_logs
def test_float_text_indent_2():
    page, = render_pages('''
      <style>
        body {
          font-family: weasyprint;
          font-size: 20px;
        }
        p {
          text-align: justify;
          text-indent: 1em;
          width: 14em;
        }
        span {
          float: left;
        }
      </style>
      <p>
        oooooooooooo
        <a>aa <span>float</span> aa</a></p>''')
    html, = page.children
    body, = html.children
    paragraph, = body.children
    line1, line2 = paragraph.children

    p1, = line1.children
    assert p1.position_x == 1 * 20  # text-indent
    assert p1.width == 12 * 20  # p text

    a, = line2.children
    a1, span, a2 = a.children
    span_text, = span.children
    assert span.position_x == span_text.position_x == 0
    assert span.width == span_text.width == (
        (1 + 5) * 20)  # text-indent + span text
    assert a1.width == 3 * 20
    assert a1.position_x == (1 + 5) * 20  # span
    assert a2.width == 2 * 20  # leading space collapse
    assert a2.position_x == (1 + 5 + 3) * 20  # span + a1


@assert_no_logs
def test_float_text_indent_3():
    page, = render_pages('''
      <style>
        body {
          font-family: weasyprint;
          font-size: 20px;
        }
        p {
          text-align: justify;
          text-indent: 1em;
          width: 14em;
        }
        span {
          float: right;
        }
      </style>
      <p>
        oooooooooooo
        <a>aa <span>float</span> aa</a>
        oooooooooooo
      </p>''')
    html, = page.children
    body, = html.children
    paragraph, = body.children
    line1, line2, line3 = paragraph.children

    p1, = line1.children
    assert p1.position_x == 1 * 20  # text-indent
    assert p1.width == 12 * 20  # p text

    a, = line2.children
    a1, span, a2 = a.children
    span_text, = span.children
    assert span.position_x == span_text.position_x == (14 - 5 - 1) * 20
    assert span.width == span_text.width == (
        (1 + 5) * 20)  # text-indent + span text
    assert a1.position_x == 0  # span
    assert a2.width == 2 * 20  # leading space collapse
    assert a2.position_x == (14 - 5 - 1 - 2) * 20

    p2, = line3.children
    assert p2.position_x == 0
    assert p2.width == 12 * 20  # p text


@assert_no_logs
def test_float_previous_break():
    page1, page2 = render_pages('''
      <style>
        @page {
          size: 13px;
        }
        body {
          font-family: weasyprint;
          font-size: 2px;
          line-height: 1;
        }
        p {
          break-after: avoid;
        }
        section {
          break-inside: avoid;
          float: left;
        }
      </style>
      <article>
        oooooo
        oooooo
        oooooo
        oooooo
      </article>
      <p>xxxxxx</p>
      <p>yyyyyy</p>
      <section>
        <div>aaaaaa</div>
        <div>cccccc</div>
      </section>
      <article>
        dddddd
      </article>''')

    html, = page1.children
    body, = html.children
    article, = body.children

    html, = page2.children
    body, = html.children
    p1, p2, section, article = body.children


@pytest.mark.xfail
@assert_no_logs
def test_float_fail():
    page, = render_pages('''
      <style>
        body {
          font-family: weasyprint;
          font-size: 20px;
        }
        p {
          text-align: justify;
          width: 12em;
        }
        span {
          float: left;
          background: red;
        }
        a {
          background: yellow;
        }
      </style>
      <p>bb bb pp bb pp pb <a><span>pp pp</span> apa</a> bb bb</p>''')
    html, = page.children
    body, = html.children
    paragraph, = body.children
    line1, line2, line3 = paragraph.children


def test_float_table_aborted_row():
    page1, page2 = render_pages('''
      <style>
        @page {size: 10px 7px}
        body {font-family: weasyprint; font-size: 2px; line-height: 1}
        div {float: right; orphans: 1}
        td {break-inside: avoid}
      </style>
      <table><tbody>
        <tr><td>abc</td></tr>
        <tr><td>abc</td></tr>
        <tr><td>def <div>f<br>g</div> ghi</td></tr>
      </tbody></table>
    ''')

    html, = page1.children
    body, = html.children
    table_wrapper, = body.children
    table, = table_wrapper.children
    tbody, = table.children
    for tr in tbody.children:
        td, = tr.children
        line, = td.children
        textbox, = line.children
        assert textbox.text == 'abc'

    html, = page2.children
    body, = html.children
    table_wrapper, = body.children
    table, = table_wrapper.children
    tbody, = table.children
    tr, = tbody.children
    td, = tr.children
    line1, line2 = td.children
    textbox, div = line1.children
    assert textbox.text == 'def '
    textbox, = line2.children
    assert textbox.text == 'ghi'
    line1, line2 = div.children
    textbox, br = line1.children
    assert textbox.text == 'f'
    textbox, = line2.children
    assert textbox.text == 'g'
