from fontTools.pens.t2CharStringPen import T2CharStringPen
import unittest


class T2CharStringPenTest(unittest.TestCase):
    def __init__(self, methodName):
        unittest.TestCase.__init__(self, methodName)

    def assertAlmostEqualProgram(self, expected, actual):
        self.assertEqual(len(expected), len(actual))
        for i1, i2 in zip(expected, actual):
            if isinstance(i1, str):
                self.assertIsInstance(i2, str)
                self.assertEqual(i1, i2)
            else:
                self.assertAlmostEqual(i1, i2)

    def test_draw_h_v_lines(self):
        pen = T2CharStringPen(100, {})
        pen.moveTo((0, 0))
        pen.lineTo((10, 0))
        pen.lineTo((10, 10))
        pen.lineTo((0, 10))
        pen.closePath()  # no-op
        pen.moveTo((10, 10))
        pen.lineTo((10, 20))
        pen.lineTo((0, 20))
        pen.lineTo((0, 10))
        pen.closePath()
        charstring = pen.getCharString(None, None)

        self.assertEqual(
            [
                100,
                0,
                "hmoveto",
                10,
                10,
                -10,
                "hlineto",
                10,
                "hmoveto",
                10,
                -10,
                -10,
                "vlineto",
                "endchar",
            ],
            charstring.program,
        )

    def test_draw_lines(self):
        pen = T2CharStringPen(100, {})
        pen.moveTo((5, 5))
        pen.lineTo((25, 15))
        pen.lineTo((35, 35))
        pen.lineTo((15, 25))
        pen.closePath()  # no-op
        charstring = pen.getCharString(None, None)

        self.assertEqual(
            [100, 5, 5, "rmoveto", 20, 10, 10, 20, -20, -10, "rlineto", "endchar"],
            charstring.program,
        )

    def test_draw_h_v_curves(self):
        pen = T2CharStringPen(100, {})
        pen.moveTo((0, 0))
        pen.curveTo((10, 0), (20, 10), (20, 20))
        pen.curveTo((20, 30), (10, 40), (0, 40))
        pen.endPath()  # no-op
        charstring = pen.getCharString(None, None)

        self.assertEqual(
            [
                100,
                0,
                "hmoveto",
                10,
                10,
                10,
                10,
                10,
                -10,
                10,
                -10,
                "hvcurveto",
                "endchar",
            ],
            charstring.program,
        )

    def test_draw_curves(self):
        pen = T2CharStringPen(100, {})
        pen.moveTo((95, 25))
        pen.curveTo((115, 44), (115, 76), (95, 95))
        pen.curveTo((76, 114), (44, 115), (25, 95))
        pen.endPath()  # no-op
        charstring = pen.getCharString(None, None)

        self.assertEqual(
            [
                100,
                95,
                25,
                "rmoveto",
                20,
                19,
                0,
                32,
                -20,
                19,
                -19,
                19,
                -32,
                1,
                -19,
                -20,
                "rrcurveto",
                "endchar",
            ],
            charstring.program,
        )

    def test_draw_more_curves(self):
        pen = T2CharStringPen(100, {})
        pen.moveTo((10, 10))
        pen.curveTo((20, 10), (50, 10), (60, 10))
        pen.curveTo((60, 20), (60, 50), (60, 60))
        pen.curveTo((50, 50), (40, 60), (30, 60))
        pen.curveTo((40, 50), (30, 40), (30, 30))
        pen.curveTo((30, 25), (25, 19), (20, 20))
        pen.curveTo((15, 20), (9, 25), (10, 30))
        pen.curveTo((7, 25), (6, 15), (10, 10))
        pen.endPath()  # no-op
        charstring = pen.getCharString(None, None)

        self.assertEqual(
            [
                100,
                10,
                10,
                "rmoveto",
                10,
                30,
                0,
                10,
                "hhcurveto",
                10,
                0,
                30,
                10,
                "vvcurveto",
                -10,
                -10,
                -10,
                10,
                -10,
                "hhcurveto",
                10,
                -10,
                -10,
                -10,
                -10,
                "vvcurveto",
                -5,
                -5,
                -6,
                -5,
                1,
                "vhcurveto",
                -5,
                -6,
                5,
                5,
                1,
                "hvcurveto",
                -3,
                -5,
                -1,
                -10,
                4,
                -5,
                "rrcurveto",
                "endchar",
            ],
            charstring.program,
        )

    def test_default_width(self):
        pen = T2CharStringPen(None, {})
        charstring = pen.getCharString(None, None)
        self.assertEqual(["endchar"], charstring.program)

    def test_no_round(self):
        pen = T2CharStringPen(100.1, {}, roundTolerance=0.0)
        pen.moveTo((0, 0))
        pen.curveTo((10.1, 0.1), (19.9, 9.9), (20.49, 20.49))
        pen.curveTo((20.49, 30.49), (9.9, 39.9), (0.1, 40.1))
        pen.closePath()
        charstring = pen.getCharString(None, None)

        self.assertAlmostEqualProgram(
            [
                100,  # we always round the advance width
                0,
                "hmoveto",
                10.1,
                0.1,
                9.8,
                9.8,
                0.59,
                10.59,
                "rrcurveto",
                10,
                -10.59,
                9.41,
                -9.8,
                0.2,
                "vhcurveto",
                "endchar",
            ],
            charstring.program,
        )

    def test_round_all(self):
        pen = T2CharStringPen(100.1, {}, roundTolerance=0.5)
        pen.moveTo((0, 0))
        pen.curveTo((10.1, 0.1), (19.9, 9.9), (20.49, 20.49))
        pen.curveTo((20.49, 30.5), (9.9, 39.9), (0.1, 40.1))
        pen.closePath()
        charstring = pen.getCharString(None, None)

        self.assertEqual(
            [
                100,
                0,
                "hmoveto",
                10,
                10,
                10,
                10,
                11,
                -10,
                9,
                -10,
                "hvcurveto",
                "endchar",
            ],
            charstring.program,
        )

    def test_round_some(self):
        pen = T2CharStringPen(100, {}, roundTolerance=0.2)
        pen.moveTo((0, 0))
        # the following two are rounded as within the tolerance
        pen.lineTo((10.1, 0.1))
        pen.lineTo((19.9, 9.9))
        # this one is not rounded as it exceeds the tolerance
        pen.lineTo((20.49, 20.49))
        pen.closePath()
        charstring = pen.getCharString(None, None)

        self.assertAlmostEqualProgram(
            [
                100,
                0,
                "hmoveto",
                10,
                "hlineto",
                10,
                10,
                0.49,
                10.49,
                "rlineto",
                "endchar",
            ],
            charstring.program,
        )

    def test_invalid_tolerance(self):
        self.assertRaisesRegex(
            ValueError,
            "Rounding tolerance must be positive",
            T2CharStringPen,
            None,
            {},
            roundTolerance=-0.1,
        )


if __name__ == "__main__":
    import sys

    sys.exit(unittest.main())
