from fontTools.ttLib import newTable
from fontTools.ttLib.tables._k_e_r_n import (
    KernTable_format_0, KernTable_format_unkown)
from fontTools.misc.textTools import deHexStr
from fontTools.misc.testTools import FakeFont, getXML, parseXML
import itertools
import pytest


KERN_VER_0_FMT_0_DATA = deHexStr(
    '0000 '            #  0: version=0
    '0001 '            #  2: nTables=1
    '0000 '            #  4: version=0 (bogus field, unused)
    '0020 '            #  6: length=32
    '00 '              #  8: format=0
    '01 '              #  9: coverage=1
    '0003 '            # 10: nPairs=3
    '000C '            # 12: searchRange=12
    '0001 '            # 14: entrySelector=1
    '0006 '            # 16: rangeShift=6
    '0004 000C FFD8 '  # 18: l=4, r=12, v=-40
    '0004 001C 0028 '  # 24: l=4, r=28, v=40
    '0005 0028 FFCE '  # 30: l=5, r=40, v=-50
)
assert len(KERN_VER_0_FMT_0_DATA) == 36

KERN_VER_0_FMT_0_XML = [
    '<version value="0"/>',
    '<kernsubtable coverage="1" format="0">',
    '  <pair l="E" r="M" v="-40"/>',
    '  <pair l="E" r="c" v="40"/>',
    '  <pair l="F" r="o" v="-50"/>',
    '</kernsubtable>',
]

KERN_VER_1_FMT_0_DATA = deHexStr(
    '0001 0000 '       #  0: version=1
    '0000 0001 '       #  4: nTables=1
    '0000 0022 '       #  8: length=34
    '00 '              # 12: coverage=0
    '00 '              # 13: format=0
    '0000 '            # 14: tupleIndex=0
    '0003 '            # 16: nPairs=3
    '000C '            # 18: searchRange=12
    '0001 '            # 20: entrySelector=1
    '0006 '            # 22: rangeShift=6
    '0004 000C FFD8 '  # 24: l=4, r=12, v=-40
    '0004 001C 0028 '  # 30: l=4, r=28, v=40
    '0005 0028 FFCE '  # 36: l=5, r=40, v=-50
)
assert len(KERN_VER_1_FMT_0_DATA) == 42

KERN_VER_1_FMT_0_XML = [
    '<version value="1.0"/>',
    '<kernsubtable coverage="0" format="0" tupleIndex="0">',
    '  <pair l="E" r="M" v="-40"/>',
    '  <pair l="E" r="c" v="40"/>',
    '  <pair l="F" r="o" v="-50"/>',
    '</kernsubtable>',
]

KERN_VER_0_FMT_UNKNOWN_DATA = deHexStr(
    '0000 '            #  0: version=0
    '0002 '            #  2: nTables=2
    '0000 '            #  4: version=0
    '000A '            #  6: length=10
    '04 '              #  8: format=4  (format 4 doesn't exist)
    '01 '              #  9: coverage=1
    '1234 5678 '       # 10: garbage...
    '0000 '            # 14: version=0
    '000A '            # 16: length=10
    '05 '              # 18: format=5  (format 5 doesn't exist)
    '01 '              # 19: coverage=1
    '9ABC DEF0 '       # 20: garbage...
)
assert len(KERN_VER_0_FMT_UNKNOWN_DATA) == 24

KERN_VER_0_FMT_UNKNOWN_XML = [
    '<version value="0"/>',
    '<kernsubtable format="4">',
    "  <!-- unknown 'kern' subtable format -->",
    '  0000000A 04011234',
    '  5678             ',
    '</kernsubtable>',
    '<kernsubtable format="5">',
    "<!-- unknown 'kern' subtable format -->",
    '  0000000A 05019ABC',
    '  DEF0             ',
    '</kernsubtable>',
]

KERN_VER_1_FMT_UNKNOWN_DATA = deHexStr(
    '0001 0000 '       #  0: version=1
    '0000 0002 '       #  4: nTables=2
    '0000 000C '       #  8: length=12
    '00 '              # 12: coverage=0
    '04 '              # 13: format=4  (format 4 doesn't exist)
    '0000 '            # 14: tupleIndex=0
    '1234 5678'        # 16: garbage...
    '0000 000C '       # 20: length=12
    '00 '              # 24: coverage=0
    '05 '              # 25: format=5  (format 5 doesn't exist)
    '0000 '            # 26: tupleIndex=0
    '9ABC DEF0 '       # 28: garbage...
)
assert len(KERN_VER_1_FMT_UNKNOWN_DATA) == 32

KERN_VER_1_FMT_UNKNOWN_XML = [
    '<version value="1"/>',
    '<kernsubtable format="4">',
    "  <!-- unknown 'kern' subtable format -->",
    '  0000000C 00040000',
    '  12345678         ',
    '</kernsubtable>',
    '<kernsubtable format="5">',
    "  <!-- unknown 'kern' subtable format -->",
    '  0000000C 00050000',
    '  9ABCDEF0         ',
    '</kernsubtable>',
]

KERN_VER_0_FMT_0_OVERFLOWING_DATA = deHexStr(
    '0000 '  #  0: version=0
    '0001 '  #  2: nTables=1
    '0000 '  #  4: version=0 (bogus field, unused)
    '0274 '  #  6: length=628 (bogus value for 66164 % 0x10000)
    '00 '    #  8: format=0
    '01 '    #  9: coverage=1
    '2B11 '  # 10: nPairs=11025
    'C000 '  # 12: searchRange=49152
    '000D '  # 14: entrySelector=13
    '4266 '  # 16: rangeShift=16998
) + deHexStr(' '.join(
    '%04X %04X %04X' % (a, b, 0)
    for (a, b) in itertools.product(range(105), repeat=2)
))


@pytest.fixture
def font():
    return FakeFont(list("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                         "abcdefghijklmnopqrstuvwxyz"))

@pytest.fixture
def overflowing_font():
    return FakeFont(["glyph%i" % i for i in range(105)])


class KernTableTest(object):

    @pytest.mark.parametrize(
        "data, version",
        [
            (KERN_VER_0_FMT_0_DATA, 0),
            (KERN_VER_1_FMT_0_DATA, 1.0),
        ],
        ids=["version_0", "version_1"]
    )
    def test_decompile_single_format_0(self, data, font, version):
        kern = newTable("kern")
        kern.decompile(data, font)

        assert kern.version == version
        assert len(kern.kernTables) == 1

        st = kern.kernTables[0]
        assert st.apple is (version == 1.0)
        assert st.format == 0
        # horizontal kerning in OT kern is coverage 0x01, while in
        # AAT kern it's the default (0)
        assert st.coverage == (0 if st.apple else 1)
        assert st.tupleIndex == (0 if st.apple else None)
        assert len(st.kernTable) == 3
        assert st.kernTable == {
            ('E', 'M'): -40,
            ('E', 'c'): 40,
            ('F', 'o'): -50
        }

    @pytest.mark.parametrize(
        "version, expected",
        [
            (0, KERN_VER_0_FMT_0_DATA),
            (1.0, KERN_VER_1_FMT_0_DATA),
        ],
        ids=["version_0", "version_1"]
    )
    def test_compile_single_format_0(self, font, version, expected):
        kern = newTable("kern")
        kern.version = version
        apple = version == 1.0
        st = KernTable_format_0(apple)
        kern.kernTables = [st]
        st.coverage = (0 if apple else 1)
        st.tupleIndex = 0 if apple else None
        st.kernTable = {
            ('E', 'M'): -40,
            ('E', 'c'): 40,
            ('F', 'o'): -50
        }
        data = kern.compile(font)
        assert data == expected

    @pytest.mark.parametrize(
        "xml, version",
        [
            (KERN_VER_0_FMT_0_XML, 0),
            (KERN_VER_1_FMT_0_XML, 1.0),
        ],
        ids=["version_0", "version_1"]
    )
    def test_fromXML_single_format_0(self, xml, font, version):
        kern = newTable("kern")
        for name, attrs, content in parseXML(xml):
            kern.fromXML(name, attrs, content, ttFont=font)

        assert kern.version == version
        assert len(kern.kernTables) == 1

        st = kern.kernTables[0]
        assert st.apple is (version == 1.0)
        assert st.format == 0
        assert st.coverage == (0 if st.apple else 1)
        assert st.tupleIndex == (0 if st.apple else None)
        assert len(st.kernTable) == 3
        assert st.kernTable == {
            ('E', 'M'): -40,
            ('E', 'c'): 40,
            ('F', 'o'): -50
        }

    @pytest.mark.parametrize(
        "version, expected",
        [
            (0, KERN_VER_0_FMT_0_XML),
            (1.0, KERN_VER_1_FMT_0_XML),
        ],
        ids=["version_0", "version_1"]
    )
    def test_toXML_single_format_0(self, font, version, expected):
        kern = newTable("kern")
        kern.version = version
        apple = version == 1.0
        st = KernTable_format_0(apple)
        kern.kernTables = [st]
        st.coverage = 0 if apple else 1
        st.tupleIndex = 0 if apple else None
        st.kernTable = {
            ('E', 'M'): -40,
            ('E', 'c'): 40,
            ('F', 'o'): -50
        }
        xml = getXML(kern.toXML, font)
        assert xml == expected

    @pytest.mark.parametrize(
        "data, version, header_length, st_length",
        [
            (KERN_VER_0_FMT_UNKNOWN_DATA, 0, 4, 10),
            (KERN_VER_1_FMT_UNKNOWN_DATA, 1.0, 8, 12),
        ],
        ids=["version_0", "version_1"]
    )
    def test_decompile_format_unknown(
            self, data, font, version, header_length, st_length):
        kern = newTable("kern")
        kern.decompile(data, font)

        assert kern.version == version
        assert len(kern.kernTables) == 2

        st_data = data[header_length:]
        st0 = kern.kernTables[0]
        assert st0.format == 4
        assert st0.data == st_data[:st_length]
        st_data = st_data[st_length:]

        st1 = kern.kernTables[1]
        assert st1.format == 5
        assert st1.data == st_data[:st_length]

    @pytest.mark.parametrize(
        "version, st_length, expected",
        [
            (0, 10, KERN_VER_0_FMT_UNKNOWN_DATA),
            (1.0, 12, KERN_VER_1_FMT_UNKNOWN_DATA),
        ],
        ids=["version_0", "version_1"]
    )
    def test_compile_format_unknown(self, version, st_length, expected):
        kern = newTable("kern")
        kern.version = version
        kern.kernTables = []

        for unknown_fmt, kern_data in zip((4, 5), ("1234 5678", "9ABC DEF0")):
            if version > 0:
                coverage = 0
                header_fmt = deHexStr(
                    "%08X %02X %02X %04X" % (
                        st_length, coverage, unknown_fmt, 0))
            else:
                coverage = 1
                header_fmt = deHexStr(
                    "%04X %04X %02X %02X" % (
                        0, st_length, unknown_fmt, coverage))
            st = KernTable_format_unkown(unknown_fmt)
            st.data = header_fmt + deHexStr(kern_data)
            kern.kernTables.append(st)

        data = kern.compile(font)
        assert data == expected

    @pytest.mark.parametrize(
        "xml, version, st_length",
        [
            (KERN_VER_0_FMT_UNKNOWN_XML, 0, 10),
            (KERN_VER_1_FMT_UNKNOWN_XML, 1.0, 12),
        ],
        ids=["version_0", "version_1"]
    )
    def test_fromXML_format_unknown(self, xml, font, version, st_length):
        kern = newTable("kern")
        for name, attrs, content in parseXML(xml):
            kern.fromXML(name, attrs, content, ttFont=font)

        assert kern.version == version
        assert len(kern.kernTables) == 2

        st0 = kern.kernTables[0]
        assert st0.format == 4
        assert len(st0.data) == st_length

        st1 = kern.kernTables[1]
        assert st1.format == 5
        assert len(st1.data) == st_length

    @pytest.mark.parametrize(
        "version", [0, 1.0], ids=["version_0", "version_1"])
    def test_toXML_format_unknown(self, font, version):
        kern = newTable("kern")
        kern.version = version
        st = KernTable_format_unkown(4)
        st.data = b"ABCD"
        kern.kernTables = [st]

        xml = getXML(kern.toXML, font)

        assert xml == [
            '<version value="%s"/>' % version,
            '<kernsubtable format="4">',
            '  <!-- unknown \'kern\' subtable format -->',
            '  41424344   ',
            '</kernsubtable>',
        ]

    def test_getkern(self):
        table = newTable("kern")
        table.version = 0
        table.kernTables = []

        assert table.getkern(0) is None

        st0 = KernTable_format_0()
        table.kernTables.append(st0)

        assert table.getkern(0) is st0
        assert table.getkern(4) is None

        st1 = KernTable_format_unkown(4)
        table.kernTables.append(st1)


class KernTable_format_0_Test(object):

    def test_decompileBadGlyphId(self, font):
        subtable = KernTable_format_0()
        subtable.decompile(
            b'\x00' + b'\x00' + b'\x00' + b'\x1a' + b'\x00' + b'\x00' +
            b'\x00' + b'\x02' + b'\x00' * 6 +
            b'\x00' + b'\x01' + b'\x00' + b'\x03' + b'\x00' + b'\x01' +
            b'\x00' + b'\x01' + b'\xFF' + b'\xFF' + b'\x00' + b'\x02',
            font)
        assert subtable[("B", "D")] == 1
        assert subtable[("B", "glyph65535")] == 2

    def test_compileOverflowingSubtable(self, overflowing_font):
        font = overflowing_font
        kern = newTable("kern")
        kern.version = 0
        st = KernTable_format_0(0)
        kern.kernTables = [st]
        st.coverage = 1
        st.tupleIndex = None
        st.kernTable = {
            (a, b): 0
            for (a, b) in itertools.product(
                font.getGlyphOrder(), repeat=2)
        }
        assert len(st.kernTable) == 11025
        data = kern.compile(font)
        assert data == KERN_VER_0_FMT_0_OVERFLOWING_DATA

    def test_decompileOverflowingSubtable(self, overflowing_font):
        font = overflowing_font
        data = KERN_VER_0_FMT_0_OVERFLOWING_DATA
        kern = newTable("kern")
        kern.decompile(data, font)

        st = kern.kernTables[0]
        assert st.kernTable == {
            (a, b): 0
            for (a, b) in itertools.product(
                font.getGlyphOrder(), repeat=2)
        }


if __name__ == "__main__":
    import sys
    sys.exit(pytest.main(sys.argv))
