# coding: utf-8
"""
    weasyprint.backgrounds
    ----------------------

    :copyright: Copyright 2011-2014 Simon Sapin and contributors, see AUTHORS.
    :license: BSD, see LICENSE for details.

"""

from __future__ import division, unicode_literals

from collections import namedtuple
from itertools import cycle

from . import replaced
from ..formatting_structure import boxes
from .percentages import resolve_radii_percentages

Background = namedtuple('Background', 'color, layers, image_rendering')
BackgroundLayer = namedtuple(
    'BackgroundLayer',
    'image, size, position, repeat, unbounded, '
    'painting_area, positioning_area, clipped_boxes')


def box_rectangle(box, which_rectangle):
    if which_rectangle == 'border-box':
        return (
            box.border_box_x(),
            box.border_box_y(),
            box.border_width(),
            box.border_height(),
        )
    elif which_rectangle == 'padding-box':
        return (
            box.padding_box_x(),
            box.padding_box_y(),
            box.padding_width(),
            box.padding_height(),
        )
    else:
        assert which_rectangle == 'content-box', which_rectangle
        return (
            box.content_box_x(),
            box.content_box_y(),
            box.width,
            box.height,
        )


def layout_box_backgrounds(page, box, get_image_from_uri):
    """Fetch and position background images."""
    # Resolve percentages in border-radius properties
    resolve_radii_percentages(box)

    for child in box.all_children():
        layout_box_backgrounds(page, child, get_image_from_uri)

    style = box.style
    if style.visibility == 'hidden':
        box.background = None
        if page != box:  # Pages need a background for bleed box
            return

    images = [get_image_from_uri(value) if type_ == 'url' else value
              for type_, value in style.background_image]
    color = style.get_color('background_color')
    if color.alpha == 0 and not any(images):
        box.background = None
        if page != box:  # Pages need a background for bleed box
            return

    box.background = Background(
        color=color, image_rendering=style.image_rendering, layers=[
            layout_background_layer(box, page, style.image_resolution, *layer)
            for layer in zip(images, *map(cycle, [
                style.background_size,
                style.background_clip,
                style.background_repeat,
                style.background_origin,
                style.background_position,
                style.background_attachment]))])


def percentage(value, refer_to):
    """Return the evaluated percentage value, or the value unchanged."""
    if value == 'auto':
        return value
    elif value.unit == 'px':
        return value.value
    else:
        assert value.unit == '%'
        return refer_to * value.value / 100


def layout_background_layer(box, page, resolution, image, size, clip, repeat,
                            origin, position, attachment):

    # TODO: respect box-sizing for table cells?
    clipped_boxes = []
    painting_area = 0, 0, 0, 0
    if box is page:
        painting_area = 0, 0, page.margin_width(), page.margin_height()
        # XXX: how does border-radius work on pages?
        clipped_boxes = [box.rounded_border_box()]
    elif isinstance(box, boxes.TableRowGroupBox):
        clipped_boxes = []
        total_height = 0
        for row in box.children:
            if row.children:
                clipped_boxes += [
                    cell.rounded_border_box() for cell in row.children]
                total_height = max(total_height, max(
                    cell.border_box_y() + cell.border_height()
                    for cell in row.children))
        painting_area = [
            box.border_box_x(), box.border_box_y(),
            box.border_box_x() + box.border_width(), total_height]
    elif isinstance(box, boxes.TableRowBox):
        if box.children:
            clipped_boxes = [
                cell.rounded_border_box() for cell in box.children]
            height = max(
                cell.border_height() for cell in box.children)
            painting_area = [
                box.border_box_x(), box.border_box_y(),
                box.border_box_x() + box.border_width(),
                box.border_box_y() + height]
    elif isinstance(box, (boxes.TableColumnGroupBox, boxes.TableColumnBox)):
        cells = box.get_cells()
        if cells:
            clipped_boxes = [cell.rounded_border_box() for cell in cells]
            max_x = max(
                cell.border_box_x() + cell.border_width()
                for cell in cells)
            painting_area = [
                box.border_box_x(), box.border_box_y(),
                max_x - box.border_box_x(),
                box.border_box_y() + box.border_height()]
    else:
        painting_area = box_rectangle(box, clip)
        if clip == 'border-box':
            clipped_boxes = [box.rounded_border_box()]
        elif clip == 'padding-box':
            clipped_boxes = [box.rounded_padding_box()]
        else:
            assert clip == 'content-box', clip
            clipped_boxes = [box.rounded_content_box()]

    if image is None or 0 in image.get_intrinsic_size(1, 1):
        return BackgroundLayer(
            image=None, unbounded=(box is page), painting_area=painting_area,
            size='unused', position='unused', repeat='unused',
            positioning_area='unused', clipped_boxes=clipped_boxes)

    if attachment == 'fixed':
        # Initial containing block
        positioning_area = box_rectangle(page, 'content-box')
    else:
        positioning_area = box_rectangle(box, origin)

    positioning_x, positioning_y, positioning_width, positioning_height = (
        positioning_area)
    painting_x, painting_y, painting_width, painting_height = (
        painting_area)

    if size == 'cover':
        image_width, image_height = replaced.cover_constraint_image_sizing(
            positioning_width, positioning_height, image.intrinsic_ratio)
    elif size == 'contain':
        image_width, image_height = replaced.contain_constraint_image_sizing(
            positioning_width, positioning_height, image.intrinsic_ratio)
    else:
        size_width, size_height = size
        iwidth, iheight = image.get_intrinsic_size(
            resolution, box.style.font_size)
        image_width, image_height = replaced.default_image_sizing(
            iwidth, iheight, image.intrinsic_ratio,
            percentage(size_width, positioning_width),
            percentage(size_height, positioning_height),
            positioning_width, positioning_height)

    origin_x, position_x, origin_y, position_y = position
    ref_x = positioning_width - image_width
    ref_y = positioning_height - image_height
    position_x = percentage(position_x, ref_x)
    position_y = percentage(position_y, ref_y)
    if origin_x == 'right':
        position_x = ref_x - position_x
    if origin_y == 'bottom':
        position_y = ref_y - position_y

    repeat_x, repeat_y = repeat

    if repeat_x == 'round':
        n_repeats = max(1, round(positioning_width / image_width))
        new_width = positioning_width / n_repeats
        position_x = 0  # Ignore background-position for this dimension
        if repeat_y != 'round' and size[1] == 'auto':
            image_height *= new_width / image_width
        image_width = new_width
    if repeat_y == 'round':
        n_repeats = max(1, round(positioning_height / image_height))
        new_height = positioning_height / n_repeats
        position_y = 0  # Ignore background-position for this dimension
        if repeat_x != 'round' and size[0] == 'auto':
            image_width *= new_height / image_height
        image_height = new_height

    return BackgroundLayer(
        image=image,
        size=(image_width, image_height),
        position=(position_x, position_y),
        repeat=repeat,
        unbounded=(box is page),
        painting_area=painting_area,
        positioning_area=positioning_area,
        clipped_boxes=clipped_boxes)


def set_canvas_background(page):
    """Set a ``canvas_background`` attribute on the PageBox,
    with style for the canvas background, taken from the root elememt
    or a <body> child of the root element.

    See http://www.w3.org/TR/CSS21/colors.html#background

    """
    assert not isinstance(page.children[0], boxes.MarginBox)
    root_box = page.children[0]
    chosen_box = root_box
    if root_box.element_tag.lower() == 'html' and root_box.background is None:
        for child in root_box.children:
            if child.element_tag.lower() == 'body':
                chosen_box = child
                break

    if chosen_box.background:
        painting_area = box_rectangle(page, 'padding-box')
        page.canvas_background = chosen_box.background._replace(
            # TODO: shouldn’t background-clip be considered here?
            layers=[
                l._replace(painting_area=painting_area)
                for l in chosen_box.background.layers])
        chosen_box.background = None
    else:
        page.canvas_background = None


def layout_backgrounds(page, get_image_from_uri):
    layout_box_backgrounds(page, page, get_image_from_uri)
    set_canvas_background(page)
