# Copyright (c) 2020, Manfred Moitzi
# License: MIT License
import pytest
import ezdxf
from ezdxf.render.trace import TraceBuilder, LinearTrace, CurvedTrace
from ezdxf.math import BSpline, Vec2, BoundingBox


def test_trace_builder_init():
    t = TraceBuilder()
    assert len(t) == 0


def test_add_station_2d():
    t = LinearTrace()
    t.add_station((0, 0), 1, 2)
    assert len(t) == 1
    assert t[0].vertex == (0, 0)
    assert t[0].start_width == 1
    assert t[0].end_width == 2
    t.add_station((1, 0), 1)
    assert len(t) == 2
    assert t[1].vertex == (1, 0)
    assert t[1].start_width == 1
    assert t[1].end_width == 1


def test_add_station_3d():
    # z-axis is ignored
    t = LinearTrace()
    t.add_station((0, 0, 0), 1, 2)
    assert t[0].vertex == (0, 0)


def test_add_spline_segment():
    t = CurvedTrace.from_spline(
        BSpline.from_fit_points([(1, 0), (3, 1), (5, -1), (6, 0)]),
        start_width=2,
        end_width=1,
        segments=10,
    )
    assert len(t) == 11


def test_square_face():
    t = LinearTrace()
    t.add_station((0, 0), 1, 1)
    t.add_station((1, 0), 0, 0)
    face = list(t.faces())[0]
    assert face[0].isclose(Vec2(0, +0.5))
    assert face[1].isclose(Vec2(0, -0.5))
    assert face[2].isclose(Vec2(1, -0.5))
    assert face[3].isclose(Vec2(1, +0.5))


def test_closed_linear_path():
    t = LinearTrace()
    t.add_station((0, 0), 1, 1)
    t.add_station((1, 0), 1, 1)
    t.add_station((1, 1), 1, 1)
    t.add_station((0, 1), 1, 1)
    t.add_station((0, 0), 1, 1)
    faces = list(t.faces())
    assert len(faces) == 4
    assert faces[0] == (
        Vec2(0.5, 0.5),
        Vec2(-0.5, -0.5),
        Vec2(1.5, -0.5),
        Vec2(0.5, 0.5),
    )
    assert faces[3] == (
        Vec2(0.5, 0.5),
        Vec2(-0.5, 1.5),
        Vec2(-0.5, -0.5),
        Vec2(0.5, 0.5),
    )


def test_two_straight_faces():
    t = LinearTrace()
    t.add_station((0, 0), 1, 1)
    t.add_station((2, 0), 1, 1)
    t.add_station((4, 0), 1, 1)
    face1, face2 = list(t.faces())
    assert face1[0].isclose(Vec2(0, +0.5))
    assert face1[1].isclose(Vec2(0, -0.5))
    assert face1[2].isclose(Vec2(2, -0.5))
    assert face1[3].isclose(Vec2(2, +0.5))
    assert face2[0].isclose(Vec2(2, +0.5))
    assert face2[1].isclose(Vec2(2, -0.5))
    assert face2[2].isclose(Vec2(4, -0.5))
    assert face2[3].isclose(Vec2(4, +0.5))


def test_two_angled_faces():
    t = LinearTrace()
    t.add_station((0, 0), 1, 0.5)
    t.add_station((2, 0), 1, 1)
    t.add_station((4, 2), 1, 1)
    face1, face2 = list(t.faces())
    assert face1[0].isclose(Vec2(0, +0.5))
    assert face1[1].isclose(Vec2(0, -0.5))
    assert face1[2].isclose(Vec2(2.5224077499274835, -0.18469903125906456))
    assert face1[3].isclose(Vec2(1.5936828611675133, 0.3007896423540608))
    assert face2[2].isclose(Vec2(4.353553390593274, 1.6464466094067263))
    assert face2[3].isclose(Vec2(3.646446609406726, 2.353553390593274))


def test_linear_trace_polygon():
    t = LinearTrace()
    t.add_station((0, 0), 1, 1)
    t.add_station((2, 0), 1, 1)
    t.add_station((4, 0), 1, 1)
    polygon = t.polygon()
    assert len(polygon) == 6
    assert polygon[0].isclose(Vec2(0, -0.5))
    assert polygon[-1].isclose(Vec2(0, +0.5))


def test_virtual_entities_added_to_entity_database():
    doc = ezdxf.new()
    msp = doc.modelspace()
    t = LinearTrace()
    t.add_station((0, 0), 1, 1)
    t.add_station((1, 0), 0, 0)

    dxfattribs = {"layer": "TEST"}
    solid = list(t.virtual_entities("SOLID", dxfattribs, doc))[0]
    assert solid.DXFTYPE == "SOLID"
    assert solid.dxf.layer == "TEST"
    assert solid.dxf.handle in doc.entitydb
    msp.add_entity(solid)

    trace = list(t.virtual_entities("TRACE", dxfattribs, doc))[0]
    assert trace.DXFTYPE == "TRACE"
    assert trace.dxf.layer == "TEST"
    assert trace.dxf.handle in doc.entitydb
    msp.add_entity(trace)

    face = list(t.virtual_entities("3DFACE", dxfattribs, doc))[0]
    assert face.DXFTYPE == "3DFACE"
    assert face.dxf.layer == "TEST"
    assert face.dxf.handle in doc.entitydb
    msp.add_entity(face)

    assert len(msp) == 3


def test_issue_191():
    from ezdxf.entities import factory

    e = factory.new(
        "LWPOLYLINE",
        dxfattribs={
            "flags": 1,
        },
    )
    e.set_points(
        [
            (421846.9857097387, -36908.41493252141, 0.0, 50.0, 0.52056705),
            (421846.9857097387, -36908.41493252139, 0.0, 50.0, 0.52056705),
        ]
    )
    trace = TraceBuilder.from_polyline(e, segments=64)
    assert len(trace) == 1
    assert len(list(trace.faces())) == 32


def test_nearly_horizontal_parallel_lines_in_linear_trace_builder():
    width = 30
    trace = LinearTrace()
    trace.add_station((100, 0), width, width)
    trace.add_station((100_000, 0), width, width)
    trace.add_station((0, 0.001), width, width)
    bbox = BoundingBox()
    for face in trace.faces():
        bbox.extend(face)
    assert bbox.extmin.x > -100


def test_nearly_vertical_parallel_lines_in_linear_trace_builder():
    width = 30
    trace = LinearTrace()
    trace.add_station((0, 100), width, width)
    trace.add_station((0, 100_000), width, width)
    trace.add_station((0.001, 0), width, width)
    bbox = BoundingBox()
    for face in trace.faces():
        bbox.extend(face)
    assert bbox.extmin.y > -100


if __name__ == "__main__":
    pytest.main([__file__])
