import unittest

from bdflib import model, effects


class TestEmbolden(unittest.TestCase):
    def _build_test_font(self) -> model.Font:
        f = model.Font(b"TestFont", 12, 100, 100)
        f.new_glyph_from_data(b"TestGlyph", [0b10, 0b01], 0, 0, 2, 2, 3, 1)

        return f

    def test_basic_operation(self) -> None:
        f = self._build_test_font()
        f2 = effects.embolden(f)

        self.assertNotEqual(f, f2)

        g = f2[1]
        self.assertEqual(g.bbX, 0)
        self.assertEqual(g.bbY, 0)
        self.assertEqual(g.bbW, 3)
        self.assertEqual(g.bbH, 2)
        self.assertEqual(g.data, [0b110, 0b011])

    def test_maintaining_spacing(self) -> None:
        f = effects.embolden(self._build_test_font(), True)

        g = f[1]

        self.assertEqual(g.advance, 4)

    def test_without_maintaining_spacing(self) -> None:
        f = effects.embolden(self._build_test_font(), False)

        g = f[1]

        self.assertEqual(g.advance, 3)


class TestExpand(unittest.TestCase):
    def _build_test_font(self) -> model.Font:
        f = model.Font(b"TestFont", 4, 72, 72)
        f.new_glyph_from_data(
            name=b"o",
            data=[
                0b010,
                0b101,
                0b010,
            ],
            bbX=0,
            bbY=0,
            bbW=3,
            bbH=3,
            advance=4,
            codepoint=ord("o"),
        )
        f.new_glyph_from_data(
            name=b"!",
            data=[
                0b1,
                0b1,
                0b0,
                0b1,
            ],
            bbX=1,
            bbY=0,
            bbW=1,
            bbH=4,
            advance=4,
            codepoint=ord("!"),
        )
        f.new_glyph_from_data(
            name=b"-",
            data=[
                0b111,
            ],
            bbX=0,
            bbY=1,
            bbW=3,
            bbH=1,
            advance=4,
            codepoint=ord("-"),
        )
        f.new_glyph_from_data(
            name=b"j",
            data=[
                0b001,
                0b001,
                0b101,
                0b010,
            ],
            bbX=0,
            bbY=-1,
            bbW=3,
            bbH=4,
            advance=4,
            codepoint=ord("j"),
        )

        return f

    def test_expand_vertically_on_baseline(self) -> None:
        f = effects.expand(self._build_test_font(), 1, 2)

        # This is supposed to be for correcting the aspect ratio,
        # so the point size should be the same,
        # but the vertical resolution should be doubled.
        self.assertEqual(f.ptSize, 4)
        self.assertEqual(f.xdpi, 72)
        self.assertEqual(f.ydpi, 72 * 2)

        # The glyphs should also be modified.
        g = f[ord("o")]
        self.assertEqual(
            g.data,
            [
                0b010,
                0b010,
                0b101,
                0b101,
                0b010,
                0b010,
            ],
        )
        self.assertEqual(g.bbX, 0)
        self.assertEqual(g.bbY, 0)
        self.assertEqual(g.bbW, 3)
        self.assertEqual(g.bbH, 6)
        self.assertEqual(g.advance, 4)

    def test_expand_vertically_above_baseline(self) -> None:
        """
        Glyphs above the baseline should be raised further.
        """
        f = effects.expand(self._build_test_font(), 1, 2)
        g = f[ord("-")]
        self.assertEqual(
            g.data,
            [
                0b111,
                0b111,
            ],
        )
        self.assertEqual(g.bbX, 0)
        self.assertEqual(g.bbY, 2)
        self.assertEqual(g.bbW, 3)
        self.assertEqual(g.bbH, 2)
        self.assertEqual(g.advance, 4)

    def test_expand_vertically_below_baseline(self) -> None:
        """
        Glyphs below the baseline should be lowered further.
        """
        f = effects.expand(self._build_test_font(), 1, 2)
        g = f[ord("j")]
        self.assertEqual(
            g.data,
            [
                0b001,
                0b001,
                0b001,
                0b001,
                0b101,
                0b101,
                0b010,
                0b010,
            ],
        )
        self.assertEqual(g.bbX, 0)
        self.assertEqual(g.bbY, -2)
        self.assertEqual(g.bbW, 3)
        self.assertEqual(g.bbH, 8)
        self.assertEqual(g.advance, 4)

    def test_expand_vertically_factor_three(self) -> None:
        f = effects.expand(self._build_test_font(), 1, 3)

        # This is supposed to be for correcting the aspect ratio,
        # so the point size should be the same,
        # but the vertical resolution should be tripled.
        self.assertEqual(f.ptSize, 4)
        self.assertEqual(f.xdpi, 72)
        self.assertEqual(f.ydpi, 72 * 3)

        # The glyphs should also be modified.
        g = f[ord("o")]
        self.assertEqual(
            g.data,
            [
                0b010,
                0b010,
                0b010,
                0b101,
                0b101,
                0b101,
                0b010,
                0b010,
                0b010,
            ],
        )
        self.assertEqual(g.bbX, 0)
        self.assertEqual(g.bbY, 0)
        self.assertEqual(g.bbW, 3)
        self.assertEqual(g.bbH, 9)
        self.assertEqual(g.advance, 4)

    def test_expand_vertically_factor_zero(self) -> None:
        """
        Vertical factor must be above zero.
        """
        with self.assertRaises(ValueError):
            effects.expand(self._build_test_font(), 1, 0)
        with self.assertRaises(ValueError):
            effects.expand(self._build_test_font(), 1, -1)

    def test_reject_negative_vertical_factor(self) -> None:
        """
        Scaling vertically by -1 flips glyph images
        """
        with self.assertRaises(ValueError):
            effects.expand(self._build_test_font(), 1, -1)

    def test_expand_horizontally_at_left(self) -> None:
        f = effects.expand(self._build_test_font(), 2, 1)

        # The horizontal resolution should be doubled.
        self.assertEqual(f.ptSize, 4)
        self.assertEqual(f.xdpi, 72 * 2)
        self.assertEqual(f.ydpi, 72)

        # The glyphs should also be modified.
        g = f[ord("o")]
        self.assertEqual(
            g.data,
            [
                0b001100,
                0b110011,
                0b001100,
            ],
        )
        self.assertEqual(g.bbX, 0)
        self.assertEqual(g.bbY, 0)
        self.assertEqual(g.bbW, 6)
        self.assertEqual(g.bbH, 3)
        self.assertEqual(g.advance, 8)

    def test_expand_horizontally_after_left(self) -> None:
        f = effects.expand(self._build_test_font(), 2, 1)

        # The horizontal resolution should be doubled.
        self.assertEqual(f.ptSize, 4)
        self.assertEqual(f.xdpi, 72 * 2)
        self.assertEqual(f.ydpi, 72)

        # The glyphs should also be modified.
        g = f[ord("!")]
        self.assertEqual(
            g.data,
            [
                0b11,
                0b11,
                0b00,
                0b11,
            ],
        )
        self.assertEqual(g.bbX, 2)
        self.assertEqual(g.bbY, 0)
        self.assertEqual(g.bbW, 2)
        self.assertEqual(g.bbH, 4)
        self.assertEqual(g.advance, 8)

    def test_expand_horizontally_factor_zero(self) -> None:
        """
        Vertical factor must be above zero.
        """
        with self.assertRaises(ValueError):
            effects.expand(self._build_test_font(), 0, 1)
        with self.assertRaises(ValueError):
            effects.expand(self._build_test_font(), -1, 1)


class TestMerge(unittest.TestCase):
    def test_basic_operation(self) -> None:
        base_font = model.Font(b"BaseFont", 12, 100, 100)
        base_font.new_glyph_from_data(b"base1", [0b10, 0b01], 0, 0, 2, 2, 3, 1)
        base_font.new_glyph_from_data(b"base2", [0b01, 0b10], 0, 0, 2, 2, 3, 2)

        cust_font = model.Font(b"CustomFont", 12, 100, 100)
        cust_font.new_glyph_from_data(b"cust2", [0b1, 0b1], 0, 0, 2, 2, 3, 2)
        cust_font.new_glyph_from_data(b"cust3", [0b1, 0b1], 0, 0, 2, 2, 3, 3)

        # Start by merging the custom font on top of the base font.
        merged1 = effects.merge(base_font, cust_font)

        # We should get an entirely new font.
        self.assertNotEqual(merged1, base_font)
        self.assertNotEqual(merged1, cust_font)

        # The new font should have cust* characters in preference to base
        # characters.
        self.assertEqual(merged1[1].name, b"base1")
        self.assertEqual(merged1[2].name, b"cust2")
        self.assertEqual(merged1[3].name, b"cust3")

        # If we merge things the other way around...
        merged2 = effects.merge(cust_font, base_font)

        # ...the new font should prefer base* characters.
        self.assertEqual(merged2[1].name, b"base1")
        self.assertEqual(merged2[2].name, b"base2")
        self.assertEqual(merged2[3].name, b"cust3")
