from io import BytesIO
import cgm_input
import cgm_enums
import cgm_parse

import pytest

import inkex


def stream_commands(*commands):
    conv = cgm_input.CGMConverter()
    for command in commands:
        conv.stream(command)
    return conv.svg


def test_polygon_set():
    """Test polygon sets with re-shuffling and points, as well as fill and stroke."""
    data = [
        cgm_parse.BeginPicture(""),
        cgm_parse.PolygonSet(
            [
                cgm_enums.PolygonSetEntry(
                    cgm_enums.Point(500, 500), cgm_enums.PolygonSetLineTypeEnum.VISIBLE
                ),
                cgm_enums.PolygonSetEntry(
                    cgm_enums.Point(800, 1000),
                    cgm_enums.PolygonSetLineTypeEnum.INVISIBLE,
                ),
                cgm_enums.PolygonSetEntry(
                    cgm_enums.Point(1100, 500),
                    cgm_enums.PolygonSetLineTypeEnum.CLOSE_VISIBLE,
                ),
                cgm_enums.PolygonSetEntry(
                    cgm_enums.Point(600, 600), cgm_enums.PolygonSetLineTypeEnum.VISIBLE
                ),
                cgm_enums.PolygonSetEntry(
                    cgm_enums.Point(800, 800), cgm_enums.PolygonSetLineTypeEnum.VISIBLE
                ),
                cgm_enums.PolygonSetEntry(
                    cgm_enums.Point(1000, 600),
                    cgm_enums.PolygonSetLineTypeEnum.CLOSE_VISIBLE,
                ),
            ]
        ),
    ]

    svg = stream_commands(*data)

    assert (
        str(svg[1][0].path)
        == "M 800 1000 L 1100 500 L 500 500 Z M 600 600 L 800 800 L 1000 600 Z"
    )
    assert (
        str(svg[1][1].path)
        == "M 800 1000 M 1100 500 L 500 500 L 800 1000 M 600 600 L 800 800 L 1000 600 L 600 600 Z"
    )


def test_defaults_replacement():
    """Check that the metafile defaults replacement is applied to every page"""
    svg = stream_commands(
        cgm_parse.MetafileDefaultsReplacement(
            data=(
                cgm_parse.VDCExtent(
                    first_corner=cgm_enums.Point(x=0, y=0),
                    second_corner=cgm_enums.Point(x=4095, y=4095),
                ),
            )
        ),
        cgm_parse.BeginPicture(""),
        cgm_parse.EndPicture(),
        cgm_parse.BeginPicture(""),
        cgm_parse.EndPicture(),
    )
    # Check that we now have two pages with the specified width/height
    assert len(svg.namedview) == 2
    assert isinstance(svg.namedview[0], inkex.Page)
    assert svg.namedview[0].width == 4095
    assert svg.namedview[0].height == 4095
    assert isinstance(svg.namedview[1], inkex.Page)
    assert svg.namedview[1].width == 4095
    assert svg.namedview[1].height == 4095


def test_rectangle():
    """Draw a rectangle (fill + edge) with indexed colours"""
    svg = stream_commands(
        cgm_parse.ColourTable(
            colour_index=cgm_enums.IndexColour(index=0),
            colours=[
                cgm_enums.DirectColour(v1=255, v2=255, v3=255, v4=0),
                cgm_enums.DirectColour(v1=255, v2=0, v3=0, v4=0),
                cgm_enums.DirectColour(v1=255, v2=0, v3=255, v4=0),
                cgm_enums.DirectColour(v1=255, v2=255, v3=0, v4=0),
            ],
        ),
        cgm_parse.BeginPicture(""),
        cgm_parse.FillColour(cgm_enums.IndexColour(index=2)),
        cgm_parse.EdgeColour(cgm_enums.IndexColour(index=3)),
        cgm_parse.Rectangle(
            p1=cgm_enums.Point(x=10, y=20), p2=cgm_enums.Point(x=30, y=50)
        ),
        cgm_parse.EdgeVisibility(visibility=cgm_enums.EdgeVisibilityEnum.ON),
        cgm_parse.InteriorStyle(interior_style=cgm_enums.InteriorStyleEnum.SOLID),
        cgm_parse.Rectangle(
            p1=cgm_enums.Point(x=30, y=40), p2=cgm_enums.Point(x=70, y=50)
        ),
    )

    assert len(svg[1]) == 2
    assert isinstance(svg[1][0], inkex.Rectangle)
    assert svg[1][0].left == 10
    assert svg[1][0].top == 20
    assert svg[1][0].width == 20
    assert svg[1][0].height == 30

    assert svg[1][0].style("fill") == None
    assert svg[1][0].style("stroke") == None
    assert isinstance(svg[1][1], inkex.Rectangle)
    assert svg[1][1].style("fill") == inkex.Color([255, 0, 255])
    assert svg[1][1].style("stroke") == inkex.Color([255, 255, 0])


def test_circle():
    """Draw a circle, assert fill and stroke"""
    svg = stream_commands(
        cgm_parse.BeginPicture(""),
        cgm_parse.FillColour(cgm_enums.DirectColour(255, 255, 0)),
        cgm_parse.InteriorStyle(interior_style=cgm_enums.InteriorStyleEnum.SOLID),
        cgm_parse.EdgeColour(cgm_enums.DirectColour(0, 127, 127)),
        cgm_parse.EdgeVisibility(visibility=cgm_enums.EdgeVisibilityEnum.ON),
        cgm_parse.Circle(cgm_enums.Point(x=10, y=20), 10),
    )

    assert len(svg[1]) == 1
    circle = svg[1][0]
    assert isinstance(circle, inkex.Circle)
    assert circle.center == 10 + 20j
    assert circle.radius == 10
    assert circle.style("fill") == inkex.Color([255, 255, 0])
    assert circle.style("stroke") == inkex.Color([0, 127, 127])


def test_polyline():
    """Draw a polyline, assert no fill and stroke taken from Edge config"""
    svg = stream_commands(
        cgm_parse.BeginPicture(""),
        cgm_parse.FillColour(cgm_enums.DirectColour(255, 255, 0)),
        cgm_parse.InteriorStyle(interior_style=cgm_enums.InteriorStyleEnum.SOLID),
        cgm_parse.EdgeColour(cgm_enums.DirectColour(0, 127, 127)),
        cgm_parse.LineColour(cgm_enums.DirectColour(0, 30, 40)),
        cgm_parse.EdgeVisibility(visibility=cgm_enums.EdgeVisibilityEnum.ON),
        cgm_parse.Polyline(
            points=[
                cgm_enums.Point(x=580, y=286),
                cgm_enums.Point(x=630, y=329),
                cgm_enums.Point(x=636, y=325),
                cgm_enums.Point(x=630, y=329),
            ]
        ),
        cgm_parse.DisjointPolyline(
            points=[
                cgm_enums.Point(x=390, y=388),
                cgm_enums.Point(x=388, y=386),
                cgm_enums.Point(x=369, y=391),
                cgm_enums.Point(x=369, y=398),
            ]
        ),
    )

    assert len(svg[1]) == 2
    polyline = svg[1][0]
    assert isinstance(polyline, inkex.PathElement)
    assert polyline.path == inkex.Path("M 580, 286 L 630, 329 L 636 325 L 630 329")
    assert polyline.style("fill") == None
    assert polyline.style("stroke") == inkex.Color([0, 30, 40])

    disjoint = svg[1][1]
    assert isinstance(disjoint, inkex.PathElement)
    assert disjoint.path == inkex.Path("M 390 388 L 388 386 M 369 391 L 369 398")
    assert disjoint.style("fill") == None
    assert disjoint.style("stroke") == inkex.Color([0, 30, 40])


@pytest.mark.parametrize(
    "linestyle, expected",
    [
        (cgm_enums.LineTypeEnum.DASH, [9, 9]),
        (cgm_enums.LineTypeEnum.SOLID, []),
        (cgm_enums.LineTypeEnum.PRIVATE, []),
        (cgm_enums.LineTypeEnum.DASH_DOT, [12, 6, 3, 6]),
        (cgm_enums.LineTypeEnum.DOT, [3, 3]),
        (cgm_enums.LineTypeEnum.DASH_DOT_DOT, [12, 6, 3, 6, 3, 6]),
    ],
)
def test_line_styles(linestyle, expected):
    svg = stream_commands(
        cgm_parse.BeginPicture(""),
        cgm_parse.EdgeColour(cgm_enums.DirectColour(0, 127, 127)),
        cgm_parse.EdgeWidth(3),
        cgm_parse.EdgeType(linestyle),
        cgm_parse.EdgeVisibility(visibility=cgm_enums.EdgeVisibilityEnum.ON),
        cgm_parse.Circle(cgm_enums.Point(x=10, y=20), 10),
    )
    assert len(svg[1]) == 1
    assert svg[1][0].style("stroke-dasharray") == expected


@pytest.mark.parametrize(
    "command, expected",
    [
        (
            cgm_parse.Polybezier(
                cgm_enums.PolybezierContinuityEnum.CONTINUOUS,
                [
                    cgm_enums.Point(5, 5),
                    cgm_enums.Point(5, 10),
                    cgm_enums.Point(10, 10),
                    cgm_enums.Point(10, 5),
                    cgm_enums.Point(10, 0),
                    cgm_enums.Point(15, 0),
                    cgm_enums.Point(15, 5),
                ],
            ),
            f"M 5 5 C 5 10 10 10 10 5 C 10 0 15 0 15 5",
        ),
        (
            cgm_parse.Polybezier(
                cgm_enums.PolybezierContinuityEnum.DISCONTINUOUS,
                [
                    cgm_enums.Point(5, 5),
                    cgm_enums.Point(5, 10),
                    cgm_enums.Point(10, 10),
                    cgm_enums.Point(10, 5),
                    cgm_enums.Point(10, 2),
                    cgm_enums.Point(10, 0),
                    cgm_enums.Point(15, 0),
                    cgm_enums.Point(15, 5),
                ],
            ),
            f"M 5 5 C 5 10 10 10 10 5 M 10 2 C 10 0 15 0 15 5",
        ),
    ],
)
def test_polybezier(command, expected):
    svg = stream_commands(
        cgm_parse.BeginPicture(""),
        cgm_parse.LineColour(cgm_enums.DirectColour(0, 127, 127)),
        cgm_parse.LineWidth(3),
        cgm_parse.LineType(cgm_enums.LineTypeEnum.SOLID),
        command,
    )
    assert len(svg[1]) == 1
    assert int(svg[1][0].style("stroke-width")) == 3
    assert svg[1][0].path == inkex.Path(expected)


@pytest.mark.parametrize(
    "command, expected",
    [
        (
            cgm_parse.Ellipse(
                cgm_enums.Point(5, 5), cgm_enums.Point(5, 10), cgm_enums.Point(10, 10)
            ),
            f"M 0 0 a 8.09017 3.09017 58.2825 1 1 5 10 a 8.09017 3.09017 58.2825 0 1 -5 -10 z",
        ),
        (
            cgm_parse.CircularArc3Point(
                cgm_enums.Point(5, 5), cgm_enums.Point(5, 10), cgm_enums.Point(10, 10)
            ),
            f"m 10 10 a 3.53553 3.53553 0 0 1 -5 0 a 3.53553 3.53553 0 0 1 -2.66454e-15 -5",
        ),
        (
            cgm_parse.CircularArc3PointClose(
                cgm_enums.Point(5, 5),
                cgm_enums.Point(5, 10),
                cgm_enums.Point(8, 11),
                cgm_enums.ArcClosureEnum.CHORD,
            ),
            f"m 8 11 a 3.53553 3.53553 0 0 1 -3.66228 -1.91886 a 3.53553 3.53553 0 0 1 0.662278 -4.08114 z",
        ),
        (
            cgm_parse.CircularArc3PointClose(
                cgm_enums.Point(5, 5),
                cgm_enums.Point(5, 10),
                cgm_enums.Point(8, 11),
                cgm_enums.ArcClosureEnum.PIE,
            ),
            f"m 8 11 a 3.53553 3.53553 0 0 1 -3.66228 -1.91886 a 3.53553 3.53553 0 0 1 0.662278 -4.08114 l 2.5 2.5 z",
        ),
        (
            cgm_parse.CircularArcCentre(cgm_enums.Point(5, 5), 3, 6, 0, 5, 5),
            f"m 7.23607 9.47214 a 5 5 0 0 1 -2.23607 0.527864",
        ),
        (
            cgm_parse.CircularArcCentreClose(
                cgm_enums.Point(5, 5), 3, 6, 0, 5, 5, cgm_enums.ArcClosureEnum.PIE
            ),
            f"m 7.23607 9.47214 a 5 5 0 0 1 -2.23607 0.527864 l 0 -5 z",
        ),
    ],
)
def test_ellipse_arc(command, expected):
    svg = stream_commands(cgm_parse.BeginPicture(""), command)
    assert len(svg[1]) == 1
    assert svg[1][0].path == inkex.Path(expected), str(svg[1][0].path)


def test_text():
    svg = stream_commands(
        cgm_parse.BeginPicture(""),
        cgm_parse.FontList(fonts=["Times-Roman", "Helvetica"]),
        cgm_parse.TextFontIndex(index=2),
        cgm_parse.CharacterHeight(10),
        cgm_parse.CharacterOrientation(0, 10, 10, 0),
        cgm_parse.TextColour(cgm_enums.DirectColour(255, 120, 5)),
        cgm_parse.TextAlignment(
            cgm_enums.TextHorizontalAlignmentEnum.CENTER,
            cgm_enums.TextVerticalAlignmentEnum.HALF,
            0,
            0,
        ),
        cgm_parse.CharacterExpansionFactor(1.5),
        cgm_parse.CharacterSpacing(1.5),
        cgm_parse.Text(
            point=cgm_enums.Point(x=105.0, y=290.0),
            flag=cgm_enums.TextFinalFlag.FINAL,
            string="Text in Helvetica",
        ),
    )
    assert len(svg[1]) == 1
    text = svg[1][0]
    assert len(text) == 1
    assert isinstance(text, inkex.TextElement)
    assert isinstance(text[0], inkex.Tspan)
    assert text[0].text == "Text in Helvetica"
    assert text[0].style("font-family") == "Helvetica"
    assert float(text.get("x")) == 105
    assert float(text.get("y")) == -290
    assert text[0].style("font-size") == 10
    assert text.style("font-size") == 10
    assert text.style("text-anchor") == "middle"
    assert text[0].style("fill") == inkex.Color([255, 120, 5])
    assert text[0].style("font-stretch") == "150.0%"
    assert text.get("dy") == "0.4em"
    assert text[0].style("letter-spacing") == "1.5"
    assert text.transform == inkex.Transform((1, 0, 0, -1, 0, 0))
