# encoding: utf-8

"""Gherkin step implementations for shape-related features."""

from __future__ import unicode_literals

import hashlib

from behave import given, when, then

from pptx import Presentation
from pptx.enum.shapes import MSO_SHAPE, MSO_SHAPE_TYPE, PP_MEDIA_TYPE
from pptx.action import ActionSetting
from pptx.util import Emu

from helpers import cls_qname, test_file, test_pptx


# given ===================================================


@given("an autoshape")
def given_an_autoshape(context):
    prs = Presentation(test_pptx("shp-autoshape-adjustments"))
    context.shape = prs.slides[0].shapes[0]


@given("(builder._start_x, builder._start_y) is ({x_str}, {y_str})")
def given_builder_start_x_builder_start_y_is_x_y(context, x_str, y_str):
    builder = context.builder
    builder._start_x, builder._start_y = int(x_str), int(y_str)


@given("(builder._x_scale, builder._y_scale) is ({p_str}, {q_str})")
def given_builder_x_scale_builder_y_scale_is_p_q(context, p_str, q_str):
    builder = context.builder
    builder._x_scale, builder._y_scale = float(p_str), float(q_str)


@given("a chevron shape")
def given_a_chevron_shape(context):
    prs = Presentation(test_pptx("shp-autoshape-adjustments"))
    context.shape = prs.slides[0].shapes[0]


@given("a Connector object as shape")
def given_a_Connector_object_as_shape(context):
    prs = Presentation(test_pptx("shp-common-props"))
    sld = prs.slides[0]
    context.shape = sld.shapes[4]


@given("a connector and a 1 inch square picture at 0, 0")
def given_a_connector_and_a_1_inch_square_picture_at_0_0(context):
    prs = Presentation(test_pptx("shp-connector-props"))
    shapes = prs.slides[1].shapes
    context.picture = shapes[0]
    context.connector = shapes[1]


@given("a connector having its begin point at ({x}, {y})")
def given_a_connector_having_its_begin_point_at_x_y(context, x, y):
    prs = Presentation(test_pptx("shp-connector-props"))
    sld = prs.slides[0]
    context.connector = sld.shapes[0]


@given("a connector having its end point at ({x}, {y})")
def given_a_connector_having_its_end_point_at_x_y(context, x, y):
    prs = Presentation(test_pptx("shp-connector-props"))
    sld = prs.slides[0]
    context.connector = sld.shapes[0]


@given("an empty GroupShape object as shape")
def given_an_empty_GroupShape_object_as_shape(context):
    prs = Presentation(test_pptx("shp-common-props"))
    sld = prs.slides[0]
    context.shape = sld.shapes.add_group_shape()


@given("a FreeformBuilder object as builder")
def given_a_FreeformBuilder_object_as_builder(context):
    shapes = Presentation(test_pptx("shp-freeform")).slides[0].shapes
    builder = shapes.build_freeform()
    context.builder = builder


@given("a GraphicFrame object as shape")
def given_a_GraphicFrame_object_as_shape(context):
    # shouldn't matter, but this one contains a table
    prs = Presentation(test_pptx("shp-common-props"))
    sld = prs.slides[0]
    context.shape = sld.shapes[2]


@given("a GraphicFrame object containing a chart as shape")
def given_a_GraphicFrame_object_containing_a_chart_as_shape(context):
    prs = Presentation(test_pptx("shp-access-chart"))
    sld = prs.slides[0]
    context.shape = sld.shapes[0]


@given("a GraphicFrame object containing a table as shape")
def given_a_GraphicFrame_object_containing_a_table_as_shape(context):
    prs = Presentation(test_pptx("shp-access-chart"))
    sld = prs.slides[1]
    context.shape = sld.shapes[0]


@given("a GraphicFrame object containing an OLE object as shape")
@given("a GraphicFrame object containing an embedded XLSX object as shape")
def given_a_GraphicFrame_object_containing_an_embedded_xlsx_object_as_shape(context):
    prs = Presentation(test_pptx("shp-access-ole-object"))
    sld = prs.slides[0]
    context.shape = sld.shapes[0]


@given("a GroupShape object as group_shape")
def given_a_GroupShape_object_as_group_shape(context):
    prs = Presentation(test_pptx("shp-groupshape"))
    sld = prs.slides[0]
    context.group_shape = sld.shapes[0]


@given("a GroupShape object as shape")
def given_a_GroupShape_object_as_shape(context):
    prs = Presentation(test_pptx("shp-common-props"))
    sld = prs.slides[0]
    context.shape = sld.shapes[3]


@given("a movie shape")
def given_a_movie_shape(context):
    prs = Presentation(test_pptx("shp-movie-props"))
    context.movie = prs.slides[0].shapes[0]


@given("an _OleFormat object for an OLE object as ole_format")
@given("an _OleFormat object for an embedded XLSX as ole_format")
def given_an_OleFormat_object_for_an_embedded_XLSX_as_ole_format(context):
    prs = Presentation(test_pptx("shp-access-ole-object"))
    context.ole_format = prs.slides[0].shapes[0].ole_format


@given("a Picture object as picture")
def given_a_Picture_object_as_picture(context):
    slide = Presentation(test_pptx("shp-picture")).slides[0]
    context.picture = slide.shapes[0]


@given("a Picture object as shape")
def given_a_Picture_object_as_shape(context):
    slide = Presentation(test_pptx("shp-common-props")).slides[0]
    context.shape = slide.shapes[1]


@given("a Picture object with {crop_or_no} as picture")
def given_a_Picture_object_with_crop_or_no_as_picture(context, crop_or_no):
    shape_idx = {"no cropping": 0, "cropping": 1}[crop_or_no]
    slide = Presentation(test_pptx("shp-picture")).slides[0]
    context.picture = slide.shapes[shape_idx]


@given("a rotated {shape_type} object as shape")
def given_a_rotated_shape_type_object_as_shape(context, shape_type):
    shape_idx = {
        "Shape": 0,
        "Picture": 1,
        "GraphicFrame": 2,
        "GroupShape": 3,
        "Connector": 4,
    }[shape_type]
    prs = Presentation(test_pptx("shp-common-props"))
    sld = prs.slides[1]
    context.shape = sld.shapes[shape_idx]


@given("a Shape object as shape")
def given_a_Shape_object_as_shape(context):
    prs = Presentation(test_pptx("shp-common-props"))
    sld = prs.slides[0]
    context.shape = sld.shapes[0]


@given("a Shape object having text as shape")
def given_a_Shape_object_having_text_as_shape(context):
    prs = Presentation(test_pptx("shp-autoshape-props"))
    context.shape = prs.slides[0].shapes[0]


@given("a {shape_type} object on a slide as shape")
def given_a_shape_on_a_slide(context, shape_type):
    shape_idx = {
        "Shape": 0,
        "Picture": 1,
        "GraphicFrame": 2,
        "GroupShape": 3,
        "Connector": 4,
    }[shape_type]
    prs = Presentation(test_pptx("shp-common-props"))
    sld = prs.slides[0]
    context.shape = sld.shapes[shape_idx]
    context.slide = sld


@given("a textbox")
def given_a_textbox(context):
    prs = Presentation(test_pptx("shp-common-props"))
    sld = prs.slides[0]
    context.shape = sld.shapes[5]


@given("a shape of known position and size")
def given_a_shape_of_known_position_and_size(context):
    prs = Presentation(test_pptx("shp-pos-and-size"))
    context.shape = prs.slides[0].shapes[0]


# when ====================================================


@when("I add a {cx} x {cy} shape at ({x}, {y})")
def when_I_add_a_cx_cy_shape_at_x_y(context, cx, cy, x, y):
    context.shape.shapes.add_shape(
        MSO_SHAPE.ROUNDED_RECTANGLE, int(x), int(y), int(cx), int(cy)
    )


@when("I assign 0.15 to shape.adjustments[0]")
def when_I_assign_to_shape_adjustments(context):
    context.shape.adjustments[0] = 0.15


@when("I assign builder.convert_to_shape() to shape")
def when_I_assign_builder_convert_to_shape_to_shape(context):
    builder = context.builder
    context.shape = builder.convert_to_shape()


@when("I assign builder.convert_to_shape({x_str}, {y_str}) to shape")
def when_I_assign_builder_convert_to_shape_origin_x_y(context, x_str, y_str):
    builder = context.builder
    origin_x, origin_y = int(x_str), int(y_str)
    context.shape = builder.convert_to_shape(origin_x, origin_y)


@when("I assign shape.text = {value}")
def when_I_assign_shape_text_eq_value(context, value):
    context.shape.text = eval(value)


@when("I assign {value} to connector.begin_x")
def when_I_assign_value_to_connector_begin_x(context, value):
    context.connector.begin_x = int(value)


@when("I assign {value} to connector.begin_y")
def when_I_assign_value_to_connector_begin_y(context, value):
    context.connector.begin_y = int(value)


@when("I assign {value} to connector.end_x")
def when_I_assign_value_to_connector_end_x(context, value):
    context.connector.end_x = int(value)


@when("I assign {value} to connector.end_y")
def when_I_assign_value_to_connector_end_y(context, value):
    context.connector.end_y = int(value)


@when("I assign {value} to picture.crop_{side}")
def when_I_assign_value_to_picture_crop_side(context, value, side):
    new_value = (
        None if value == "None" else float(value) if "." in value else int(value)
    )
    setattr(context.picture, "crop_%s" % side, new_value)


@when("I assign {value} to shape.height")
def when_I_assign_value_to_shape_height(context, value):
    context.shape.height = int(value)


@when("I assign {value} to shape.left")
def when_I_assign_value_to_shape_left(context, value):
    context.shape.left = int(value)


@when("I assign '{value}' to shape.name")
def when_I_assign_value_to_shape_name(context, value):
    context.shape.name = value


@when("I assign {value} to shape.rotation")
def when_I_assign_value_to_shape_rotation(context, value):
    context.shape.rotation = float(value)


@when("I assign {value} to shape.top")
def when_I_assign_value_to_shape_top(context, value):
    context.shape.top = int(value)


@when("I assign {value} to shape.width")
def when_I_assign_value_to_shape_width(context, value):
    context.shape.width = int(value)


@when("I call builder.add_line_segments([(100, 25), (25, 100)])")
def when_I_call_builder_add_line_segments_100_25_25_100(context):
    builder = context.builder
    builder.add_line_segments([(100, 25), (25, 100)])


@when("I call connector.begin_connect(picture, 3)")
def when_I_call_connector_begin_connect_picture_3(context):
    connector, picture = context.connector, context.picture
    connector.begin_connect(picture, 3)


@when("I call connector.end_connect(picture, 3)")
def when_I_call_connector_end_connect_picture_3(context):
    connector, picture = context.connector, context.picture
    connector.end_connect(picture, 3)


# then ====================================================


@then("accessing shape.click_action raises TypeError")
def then_accessing_shape_click_action_raises_TypeError(context):
    try:
        context.shape.click_action
    except TypeError:
        return
    except Exception as e:
        raise AssertionError(
            "Accessing GroupShape.click_action raised %s" % type(e).__name__
        )
    raise AssertionError("Accessing GroupShape.click_action did not raise")


@then("builder is a FreeformBuilder object")
def then_builder_is_a_FreeformBuilder_object(context):
    builder = context.builder
    class_name = builder.__class__.__name__
    expected_value = "FreeformBuilder"
    assert class_name == expected_value, "Expected class name '%s', got '%s'" % (
        expected_value,
        class_name,
    )


@then("(builder._start_x, builder._start_y) is ({x_str}, {y_str})")
def then_builder_start_x_builder_start_y_is_x_y(context, x_str, y_str):
    builder = context.builder
    actual_value = builder._start_x, builder._start_y
    expected_value = int(x_str), int(y_str)
    assert actual_value == expected_value, "Expected %s, got %s" % (
        expected_value,
        actual_value,
    )


@then("(builder._x_scale, builder._y_scale) is ({p_str}, {q_str})")
def then_builder_x_scale_builder_y_scale_is_x_y(context, p_str, q_str):
    builder = context.builder
    actual_value = builder._x_scale, builder._y_scale
    expected_value = float(p_str), float(q_str)
    assert actual_value == expected_value, "Expected %s, got %s" % (
        expected_value,
        actual_value,
    )


@then("connector is a Connector object")
def then_connector_is_a_Connector_object(context):
    assert type(context.connector).__name__ == "Connector"


@then("connector.begin_x == {value}")
def then_connector_begin_x_equals_value(context, value):
    assert context.connector.begin_x == int(value)


@then("connector.begin_x is an Emu object with value {x}")
def then_connector_begin_x_is_an_Emu_object_with_value_x(context, x):
    begin_x = context.connector.begin_x
    assert isinstance(begin_x, Emu)
    assert begin_x == int(x)


@then("connector.begin_y == {value}")
def then_connector_begin_y_equals_value(context, value):
    assert context.connector.begin_y == int(value)


@then("connector.begin_y is an Emu object with value {y}")
def then_connector_begin_y_is_an_Emu_object_with_value_y(context, y):
    begin_y = context.connector.begin_y
    assert isinstance(begin_y, Emu)
    assert begin_y == int(y)


@then("connector.end_x == {value}")
def then_connector_end_x_equals_value(context, value):
    assert context.connector.end_x == int(value)


@then("connector.end_x is an Emu object with value {x}")
def then_connector_end_x_is_an_Emu_object_with_value_x(context, x):
    end_x = context.connector.end_x
    assert isinstance(end_x, Emu)
    assert end_x == int(x)


@then("connector.end_y == {value}")
def then_connector_end_y_equals_value(context, value):
    assert context.connector.end_y == int(value)


@then("connector.end_y is an Emu object with value {y}")
def then_connector_end_y_is_an_Emu_object_with_value_y(context, y):
    end_y = context.connector.end_y
    assert isinstance(end_y, Emu)
    assert end_y == int(y)


@then("group_shape.shapes is a GroupShapes object")
def then_group_shape_shapes_is_a_GroupShapes_object(context):
    class_name = context.group_shape.shapes.__class__.__name__
    assert class_name == "GroupShapes", "got %s" % class_name


@then("len(ole_format.blob) == {value}")
def then_len_ole_format_blob_eq_value(context, value):
    actual = len(context.ole_format.blob)
    assert actual == int(value)


@then("movie is a Movie object")
def then_movie_is_a_Movie_object(context):
    class_name = context.movie.__class__.__name__
    assert class_name == "Movie", "got %s" % class_name


@then("movie.left, movie.top == x, y")
def then_movie_left_movie_top_eq_x_y(context):
    movie = context.movie
    position = movie.left, movie.top
    assert position == (Emu(2590800), Emu(571500)), "got %s" % position


@then("movie.media_format is a _MediaFormat object")
def then_movie_media_format_is_a_MediaFormat_object(context):
    class_name = context.movie.media_format.__class__.__name__
    assert class_name == "_MediaFormat", "got %s" % class_name


@then("movie.media_type is PP_MEDIA_TYPE.MOVIE")
def then_movie_media_type_is_PP_MEDIA_TYPE_MOVIE(context):
    media_type = context.movie.media_type
    assert media_type == PP_MEDIA_TYPE.MOVIE, "got %s" % media_type


@then("movie.poster_frame is the same image as poster_frame")
def then_movie_poster_frame_is_the_same_image_as_poster_frame(context):
    actual_sha1 = context.movie.poster_frame.sha1
    with open(test_file("just-two-mice.png"), "rb") as f:
        expected_sha1 = hashlib.sha1(f.read()).hexdigest()
    assert actual_sha1 == expected_sha1, "not the same image"


@then("movie.shape_type is MSO_SHAPE_TYPE.MEDIA")
def then_movie_shape_type_is_MSO_SHAPE_TYPE_MEDIA(context):
    shape_type = context.movie.shape_type
    assert shape_type == MSO_SHAPE_TYPE.MEDIA, "got %s" % shape_type


@then("movie.width, movie.height == cx, cy")
def then_movie_width_movie_height_eq_cx_cy(context):
    movie = context.movie
    size = movie.width, movie.height
    assert size == (Emu(3962400), Emu(5715000)), "got %s" % size


@then("ole_format.blob matches ole_object_file byte-for-byte")
def then_ole_format_bytes_matches_ole_object_file_byte_for_byte(context):
    assert context.ole_format.blob == context.ole_object_file.getvalue()


@then('ole_format.prog_id == "{expected_value}"')
def then_ole_format_prog_id_eq_value(context, expected_value):
    actual_value = context.ole_format.prog_id
    assert actual_value == expected_value, "expected %r, got %r" % (
        expected_value,
        actual_value,
    )


@then("ole_format.show_as_icon is {value}")
def then_ole_format_show_as_icon_is_value(context, value):
    expected_value = {"True": True, "False": False}[value]
    actual_value = context.ole_format.show_as_icon
    assert actual_value == expected_value, "expected %r, got %r" % (
        expected_value,
        actual_value,
    )


@then("picture.crop_{side} == {value}")
def then_picture_crop_side_eq_value(context, side, value):
    expected_value = round(float(value), 5)
    actual_value = round(getattr(context.picture, "crop_%s" % side), 5)
    assert actual_value == expected_value, "picture.crop_%s == %s" % (
        side,
        actual_value,
    )


@then("picture.image is an Image object")
def then_picture_image_is_an_Image_object(context):
    class_name = context.picture.image.__class__.__name__
    assert class_name == "Image", "picture.image is a %s object" % class_name


@then("shape.adjustments[0] is 0.15")
def then_shape_adjustments_is_value(context):
    shape = context.shape
    assert shape.adjustments[0] == 0.15


@then("shape.chart is a Chart object")
def then_shape_chart_is_a_Chart_object(context):
    chart = context.shape.chart
    class_name = chart.__class__.__name__
    assert class_name == "Chart", "got %s" % class_name


@then("shape.click_action is an ActionSetting object")
def then_shape_click_action_is_an_ActionSetting_object(context):
    assert isinstance(context.shape.click_action, ActionSetting)


@then("shape.has_chart is {value}")
def then_shape_has_chart_is_value(context, value):
    expected_value = {"True": True, "False": False}[value]
    actual_value = context.shape.has_chart
    assert actual_value is expected_value, "shape.has_chart is %s" % actual_value


@then("shape.has_table is {value}")
def then_shape_has_table_is_value(context, value):
    expected_value = {"True": True, "False": False}[value]
    actual_value = context.shape.has_table
    assert actual_value is expected_value, "shape.has_table is %s" % actual_value


@then("shape.has_text_frame is {value_str}")
def then_shape_has_text_frame_is(context, value_str):
    expected_value = {"True": True, "False": False}[value_str]
    has_text_frame = context.shape.has_text_frame
    assert has_text_frame is expected_value, "got %s" % has_text_frame


@then("shape.height == {value}")
def then_shape_height_eq_value(context, value):
    expected_height = int(value)
    actual_height = context.shape.height
    assert actual_height == expected_height, "shape.height == %s" % actual_height


@then("shape.left == {value}")
def then_shape_left_eq_value(context, value):
    expected_left = int(value)
    actual_left = context.shape.left
    assert actual_left == expected_left, "shape.left == %s" % actual_left


@then("shape.line is a LineFormat object")
def then_shape_line_is_a_LineFormat_object(context):
    shape = context.shape
    line_format = shape.line
    line_format_cls_name = cls_qname(line_format)
    expected_cls_name = "pptx.dml.line.LineFormat"
    assert line_format_cls_name == expected_cls_name, "expected '%s', got '%s'" % (
        expected_cls_name,
        line_format_cls_name,
    )


@then("shape.name == '{expected_value}'")
def then_shape_name_eq_value(context, expected_value):
    shape = context.shape
    msg = "expected shape name '%s', got '%s'" % (shape.name, expected_value)
    assert shape.name == expected_value, msg


@then("shape.ole_format is an _OleFormat object")
def then_shape_ole_format_is_an_OleFormat_object(context):
    cls_name = type(context.shape.ole_format).__name__
    expected_cls_name = "_OleFormat"
    assert cls_name == expected_cls_name, "expected %r, got %r" % (
        expected_cls_name,
        cls_name,
    )


@then("shape.part is a SlidePart object")
def then_shape_part_is_a_SlidePart_object(context):
    cls_name = type(context.shape.part).__name__
    expected_cls_name = "SlidePart"
    assert cls_name == expected_cls_name, "expected '%s', got '%s'" % (
        expected_cls_name,
        cls_name,
    )


@then("shape.part is slide.part")
def then_shape_part_is_slide_part(context):
    assert context.shape.part is context.slide.part


@then("shape.rotation == {value}")
def then_shape_rotation_eq_value(context, value):
    shape = context.shape
    expected_value = float(value)
    assert shape.rotation == expected_value, "got %s" % expected_value


@then("shape.shadow is a ShadowFormat object")
def then_shape_shadow_is_a_ShadowFormat_object(context):
    cls_name = type(context.shape.shadow).__name__
    assert cls_name == "ShadowFormat", "shape.shadow is a '%s' object" % cls_name


@then("shape.shadow raises NotImplementedError")
def then_shape_shadow_raises_NotImplementedError(context):
    try:
        context.shape.shadow
    except NotImplementedError:
        return
    except Exception as e:
        raise AssertionError("shape.shadow raises %s" % type(e).__name__)
    raise AssertionError("shape.shadow did not raise")


@then("shape.shape_id == {value_str}")
def then_shape_shape_id_equals(context, value_str):
    expected_value = int(value_str)
    shape_id = context.shape.shape_id
    assert shape_id == expected_value, "got %s" % shape_id


@then("shape.shape_type == MSO_SHAPE_TYPE.{member_name}")
def then_shape_shape_type_is_MSO_SHAPE_TYPE_member(context, member_name):
    expected_shape_type = getattr(MSO_SHAPE_TYPE, member_name)
    actual_shape_type = context.shape.shape_type
    assert actual_shape_type == expected_shape_type, (
        "shape.shape_type == %s" % actual_shape_type
    )


@then("shape.text == {value}")
def then_shape_text_eq_value(context, value):
    actual, expected = context.shape.text, eval(value)
    assert actual == expected, 'shape.text == "%s"' % actual


@then("shape.top == {value}")
def then_shape_top_eq_value(context, value):
    expected_top = int(value)
    actual_top = context.shape.top
    assert actual_top == expected_top, "shape.top == %s" % actual_top


@then("shape.width == {value}")
def then_shape_width_eq_value(context, value):
    expected_width = int(value)
    actual_width = context.shape.width
    assert actual_width == expected_width, "shape.width == %s" % actual_width
