import unittest

from shapely.geometry import LineString, Polygon
from shapely.ops import unary_union
from matplotlib.pyplot import plot, subplot, show, cla, clf, xlim, ylim, title
from matplotlib.axes import *
from camlib import *
from copy import deepcopy


def mkstorage(paths):
    def get_pts(o):
        return [o.coords[0], o.coords[-1]]
    storage = FlatCAMRTreeStorage()
    storage.get_points = get_pts
    for p in paths:
        storage.insert(p)
    return storage


def plotg2(geo, solid_poly=False, color="black", linestyle='solid'):

    try:
        for sub_geo in geo:
            plotg2(sub_geo, solid_poly=solid_poly, color=color, linestyle=linestyle)
    except TypeError:
        if type(geo) == Polygon:
            if solid_poly:
                patch = PolygonPatch(geo,
                                     #facecolor="#BBF268",
                                     facecolor=color,
                                     edgecolor="#006E20",
                                     alpha=0.5,
                                     zorder=2)
                ax = subplot(111)
                ax.add_patch(patch)
            else:
                x, y = geo.exterior.coords.xy
                plot(x, y, color=color, linestyle=linestyle)
                for ints in geo.interiors:
                    x, y = ints.coords.xy
                    plot(x, y, color=color, linestyle=linestyle)

        if type(geo) == LineString or type(geo) == LinearRing:
            x, y = geo.coords.xy
            plot(x, y, color=color, linestyle=linestyle)

        if type(geo) == Point:
            x, y = geo.coords.xy
            plot(x, y, 'o')


class PaintTestCase(unittest.TestCase):
    # def __init__(self):
    #     super(PaintTestCase, self).__init__()
    #     self.boundary = None
    #     self.descr = None

    def plot_summary_A(self, paths, tooldia, result, msg):
        plotg2(self.boundary, solid_poly=True, color="green")
        plotg2(paths, color="red")
        plotg2([r.buffer(tooldia / 2) for r in result], solid_poly=True, color="blue")
        plotg2(result, color="black", linestyle='dashed')
        title(msg)
        xlim(0, 5)
        ylim(0, 5)
        show()


class PaintConnectTest(PaintTestCase):
    """
    Simple rectangular boundary and paths inside.
    """

    def setUp(self):
        self.boundary = Polygon([[0, 0], [0, 5], [5, 5], [5, 0]])

    def test_jump(self):
        print("Test: WALK Expected")
        paths = [
            LineString([[0.5, 2], [2, 4.5]]),
            LineString([[2, 0.5], [4.5, 2]])
        ]
        for p in paths:
            print(p)

        tooldia = 1.0

        print("--")
        result = Geometry.paint_connect(mkstorage(deepcopy(paths)), self.boundary, tooldia)

        result = list(result.get_objects())
        for r in result:
            print(r)

        self.assertEqual(len(result), 1)

        # self.plot_summary_A(paths, tooldia, result, "WALK expected.")

    def test_no_jump1(self):
        print("Test: FLY Expected")
        paths = [
            LineString([[0, 2], [2, 5]]),
            LineString([[2, 0], [5, 2]])
        ]
        for p in paths:
            print(p)

        tooldia = 1.0

        print("--")
        result = Geometry.paint_connect(mkstorage(deepcopy(paths)), self.boundary, tooldia)

        result = list(result.get_objects())
        for r in result:
            print(r)

        self.assertEqual(len(result), len(paths))

        # self.plot_summary_A(paths, tooldia, result, "FLY Expected")

    def test_no_jump2(self):
        print("Test: FLY Expected")
        paths = [
            LineString([[0.5, 2], [2, 4.5]]),
            LineString([[2, 0.5], [4.5, 2]])
        ]
        for p in paths:
            print(p)

        tooldia = 1.1

        print("--")
        result = Geometry.paint_connect(mkstorage(deepcopy(paths)), self.boundary, tooldia)

        result = list(result.get_objects())
        for r in result:
            print(r)

        self.assertEqual(len(result), len(paths))

        # self.plot_summary_A(paths, tooldia, result, "FLY Expected")


class PaintConnectTest2(PaintTestCase):
    """
    Boundary with an internal cutout.
    """

    def setUp(self):
        self.boundary = Polygon([[0, 0], [0, 5], [5, 5], [5, 0]])
        self.boundary = self.boundary.difference(
            Polygon([[2, 1], [3, 1], [3, 4], [2, 4]])
        )

    def test_no_jump3(self):
        print("TEST: No jump expected")
        paths = [
            LineString([[0.5, 1], [1.5, 3]]),
            LineString([[4, 1], [4, 4]])
        ]
        for p in paths:
            print(p)

        tooldia = 1.0

        print("--")
        result = Geometry.paint_connect(mkstorage(deepcopy(paths)), self.boundary, tooldia)

        result = list(result.get_objects())
        for r in result:
            print(r)

        self.assertEqual(len(result), len(paths))

        # self.plot_summary_A(paths, tooldia, result, "FLY Expected")


class PaintConnectTest3(PaintTestCase):
    """
    Tests with linerings among elements.
    """

    def setUp(self):
        self.boundary = Polygon([[0, 0], [0, 5], [5, 5], [5, 0]])
        print("TEST w/ LinearRings")

    def test_jump2(self):
        print("Test: WALK Expected")
        paths = [
            LineString([[0.5, 2], [2, 4.5]]),
            LineString([[2, 0.5], [4.5, 2]]),
            self.boundary.buffer(-0.5).exterior
        ]
        for p in paths:
            print(p)

        tooldia = 1.0

        print("--")
        result = Geometry.paint_connect(mkstorage(deepcopy(paths)), self.boundary, tooldia)

        result = list(result.get_objects())
        for r in result:
            print(r)

        self.assertEqual(len(result), 1)

        # self.plot_summary_A(paths, tooldia, result, "WALK Expected")


if __name__ == '__main__':
    unittest.main()
