from fontTools.ttLib import TTFont
from fontTools.misc.testTools import getXML
from axisregistry import (
    build_name_table,
    build_filename,
    build_fvar_instances,
    build_stat,
    build_variations_ps_name,
    _fvar_dflts,
    _fvar_instance_collisions,
)
import pytest
import os

CWD = os.path.dirname(__file__)
DATA_DIR = os.path.join(CWD, "data")

roboto_flex_fp = os.path.join(
    DATA_DIR,
    "RobotoFlex[GRAD,XOPQ,XTRA,YOPQ,YTAS,YTDE,YTFI,YTLC,YTUC,opsz,slnt,wdth,wght].ttf",
)
mavenpro_fp = os.path.join(DATA_DIR, "MavenPro-Regular.ttf")
opensans_roman_fp = os.path.join(DATA_DIR, "OpenSans[wdth,wght].ttf")
opensans_italic_fp = os.path.join(DATA_DIR, "OpenSans-Italic[wdth,wght].ttf")
opensans_cond_roman_fp = os.path.join(DATA_DIR, "OpenSansCondensed[wght].ttf")
opensans_cond_italic_fp = os.path.join(DATA_DIR, "OpenSansCondensed-Italic[wght].ttf")
wonky_fp = os.path.join(DATA_DIR, "Wonky[wdth,wght].ttf")
playfair_fp = os.path.join(DATA_DIR, "Playfair[opsz,wdth,wght].ttf")
wavefont_fp = os.path.join(DATA_DIR, "Wavefont[ROND,YALN,wght].ttf")


@pytest.fixture
def static_font():
    return TTFont(mavenpro_fp)


def _test_names(ttFont, expected):
    name_table = ttFont["name"]
    for k, v in expected.items():
        record = name_table.getName(*k)
        if not record:
            assert record == v, f"{k} record is incorrect, expected {v} got {record}"
        else:
            record_string = record.toUnicode()
            assert (
                record_string == v
            ), f"{k} record is incorrect, expected {v} got {record}"


@pytest.mark.parametrize(
    "fp, family_name, style_name, siblings, expected",
    [
        # Maven Pro Regular
        (
            mavenpro_fp,
            "Maven Pro",
            "Regular",
            [],
            {
                (1, 3, 1, 0x409): "Maven Pro",
                (2, 3, 1, 0x409): "Regular",
                (3, 3, 1, 0x409): "2.003;NONE;MavenPro-Regular",
                (4, 3, 1, 0x409): "Maven Pro Regular",
                (6, 3, 1, 0x409): "MavenPro-Regular",
                (16, 3, 1, 0x409): None,
                (17, 3, 1, 0x409): None,
            },
        ),
        # Maven Pro Italic
        (
            mavenpro_fp,
            "Maven Pro",
            "Italic",
            [],
            {
                (1, 3, 1, 0x409): "Maven Pro",
                (2, 3, 1, 0x409): "Italic",
                (3, 3, 1, 0x409): "2.003;NONE;MavenPro-Italic",
                (4, 3, 1, 0x409): "Maven Pro Italic",
                (6, 3, 1, 0x409): "MavenPro-Italic",
                (16, 3, 1, 0x409): None,
                (17, 3, 1, 0x409): None,
            },
        ),
        # Maven Pro Bold
        (
            mavenpro_fp,
            "Maven Pro",
            "Bold",
            [],
            {
                (1, 3, 1, 0x409): "Maven Pro",
                (2, 3, 1, 0x409): "Bold",
                (3, 3, 1, 0x409): "2.003;NONE;MavenPro-Bold",
                (4, 3, 1, 0x409): "Maven Pro Bold",
                (6, 3, 1, 0x409): "MavenPro-Bold",
                (16, 3, 1, 0x409): None,
                (17, 3, 1, 0x409): None,
            },
        ),
        # Maven Pro Bold Italic
        (
            mavenpro_fp,
            "Maven Pro",
            "Bold Italic",
            [],
            {
                (1, 3, 1, 0x409): "Maven Pro",
                (2, 3, 1, 0x409): "Bold Italic",
                (3, 3, 1, 0x409): "2.003;NONE;MavenPro-BoldItalic",
                (4, 3, 1, 0x409): "Maven Pro Bold Italic",
                (6, 3, 1, 0x409): "MavenPro-BoldItalic",
                (16, 3, 1, 0x409): None,
                (17, 3, 1, 0x409): None,
            },
        ),
        # Maven Pro Black
        (
            mavenpro_fp,
            "Maven Pro",
            "Black",
            [],
            {
                (1, 3, 1, 0x409): "Maven Pro Black",
                (2, 3, 1, 0x409): "Regular",
                (3, 3, 1, 0x409): "2.003;NONE;MavenPro-Black",
                (4, 3, 1, 0x409): "Maven Pro Black",
                (6, 3, 1, 0x409): "MavenPro-Black",
                (16, 3, 1, 0x409): "Maven Pro",
                (17, 3, 1, 0x409): "Black",
            },
        ),
        # Maven Pro Black Italic
        (
            mavenpro_fp,
            "Maven Pro",
            "Black Italic",
            [],
            {
                (1, 3, 1, 0x409): "Maven Pro Black",
                (2, 3, 1, 0x409): "Italic",
                (3, 3, 1, 0x409): "2.003;NONE;MavenPro-BlackItalic",
                (4, 3, 1, 0x409): "Maven Pro Black Italic",
                (6, 3, 1, 0x409): "MavenPro-BlackItalic",
                (16, 3, 1, 0x409): "Maven Pro",
                (17, 3, 1, 0x409): "Black Italic",
            },
        ),
        # Maven Pro ExtraLight Italic
        (
            mavenpro_fp,
            "Maven Pro",
            "ExtraLight Italic",
            [],
            {
                (1, 3, 1, 0x409): "Maven Pro ExtraLight",
                (2, 3, 1, 0x409): "Italic",
                (3, 3, 1, 0x409): "2.003;NONE;MavenPro-ExtraLightItalic",
                (4, 3, 1, 0x409): "Maven Pro ExtraLight Italic",
                (6, 3, 1, 0x409): "MavenPro-ExtraLightItalic",
                (16, 3, 1, 0x409): "Maven Pro",
                (17, 3, 1, 0x409): "ExtraLight Italic",
            },
        ),
        # check non-weight styles get appended to family name
        # Maven Pro UltraExpanded Regular
        (
            mavenpro_fp,
            "Maven Pro",
            "UltraExpanded Regular",
            [],
            {
                (1, 3, 1, 0x409): "Maven Pro UltraExpanded",
                (2, 3, 1, 0x409): "Regular",
                (3, 3, 1, 0x409): "2.003;NONE;MavenProUltraExpanded-Regular",
                (4, 3, 1, 0x409): "Maven Pro UltraExpanded Regular",
                (6, 3, 1, 0x409): "MavenProUltraExpanded-Regular",
                (16, 3, 1, 0x409): None,
                (17, 3, 1, 0x409): None,
            },
        ),
        # Maven Pro Condensed ExtraLight Italic
        (
            mavenpro_fp,
            "Maven Pro",
            "Condensed ExtraLight Italic",
            [],
            {
                (1, 3, 1, 0x409): "Maven Pro Condensed ExtraLight",
                (2, 3, 1, 0x409): "Italic",
                (3, 3, 1, 0x409): "2.003;NONE;MavenProCondensed-ExtraLightItalic",
                (4, 3, 1, 0x409): "Maven Pro Condensed ExtraLight Italic",
                (6, 3, 1, 0x409): "MavenProCondensed-ExtraLightItalic",
                (16, 3, 1, 0x409): "Maven Pro Condensed",
                (17, 3, 1, 0x409): "ExtraLight Italic",
            },
        ),
        ## VFs
        # Open Sans Roman
        (
            opensans_roman_fp,
            "Open Sans",
            None,
            [opensans_italic_fp, opensans_cond_roman_fp, opensans_cond_italic_fp],
            {
                (1, 3, 1, 0x409): "Open Sans",
                (2, 3, 1, 0x409): "Regular",
                (3, 3, 1, 0x409): "3.000;GOOG;OpenSans-Regular",
                (4, 3, 1, 0x409): "Open Sans Regular",
                (6, 3, 1, 0x409): "OpenSans-Regular",
                (16, 3, 1, 0x409): None,
                (17, 3, 1, 0x409): None,
                (25, 3, 1, 0x409): "OpenSans",
            },
        ),
        # Open Sans Italic
        (
            opensans_italic_fp,
            "Open Sans",
            None,
            [opensans_roman_fp, opensans_cond_roman_fp, opensans_cond_italic_fp],
            {
                (1, 3, 1, 0x409): "Open Sans",
                (2, 3, 1, 0x409): "Italic",
                (3, 3, 1, 0x409): "3.000;GOOG;OpenSans-Italic",
                (4, 3, 1, 0x409): "Open Sans Italic",
                (6, 3, 1, 0x409): "OpenSans-Italic",
                (16, 3, 1, 0x409): None,
                (17, 3, 1, 0x409): None,
                (25, 3, 1, 0x409): "OpenSansItalic",
            },
        ),
        # Open Sans Cond Roman
        (
            opensans_cond_roman_fp,
            "Open Sans Condensed",
            None,
            [opensans_roman_fp, opensans_italic_fp, opensans_cond_italic_fp],
            {
                (1, 3, 1, 0x409): "Open Sans Condensed",
                (2, 3, 1, 0x409): "Regular",
                (3, 3, 1, 0x409): "3.000;GOOG;OpenSansCondensed-Regular",
                (4, 3, 1, 0x409): "Open Sans Condensed Regular",
                (6, 3, 1, 0x409): "OpenSansCondensed-Regular",
                (16, 3, 1, 0x409): None,
                (17, 3, 1, 0x409): None,
                (25, 3, 1, 0x409): "OpenSansCondensed",
            },
        ),
        # Open Sans Cond Italic
        (
            opensans_cond_italic_fp,
            "Open Sans Condensed",
            None,
            [opensans_roman_fp, opensans_italic_fp, opensans_cond_roman_fp],
            {
                (1, 3, 1, 0x409): "Open Sans Condensed",
                (2, 3, 1, 0x409): "Italic",
                (3, 3, 1, 0x409): "3.000;GOOG;OpenSansCondensed-Italic",
                (4, 3, 1, 0x409): "Open Sans Condensed Italic",
                (6, 3, 1, 0x409): "OpenSansCondensed-Italic",
                (16, 3, 1, 0x409): None,
                (17, 3, 1, 0x409): None,
                (25, 3, 1, 0x409): "OpenSansCondensedItalic",
            },
        ),
        # Bad names
        (
            mavenpro_fp,
            "Maven Pro",
            "Fat",  # this should get appended to the family name
            [],
            {
                (1, 3, 1, 0x409): "Maven Pro Fat",
                (2, 3, 1, 0x409): "Regular",
                (3, 3, 1, 0x409): "2.003;NONE;MavenProFat-Regular",
                (4, 3, 1, 0x409): "Maven Pro Fat Regular",
                (6, 3, 1, 0x409): "MavenProFat-Regular",
                (16, 3, 1, 0x409): None,
                (17, 3, 1, 0x409): None,
            },
        ),
        (
            wonky_fp,
            "Wonky",  # name exists in axis reg!
            None,
            [],
            {
                (1, 3, 1, 0x409): "Wonky",
                (2, 3, 1, 0x409): "Regular",
                (3, 3, 1, 0x409): "3.000;GOOG;Wonky-Regular",
                (4, 3, 1, 0x409): "Wonky Regular",
                (6, 3, 1, 0x409): "Wonky-Regular",
                (16, 3, 1, 0x409): None,
                (17, 3, 1, 0x409): None,
            },
        ),
        # Test opsz particle is kept.
        # Fixes https://github.com/googlefonts/axisregistry/issues/130
        (
            playfair_fp,
            "Playfair",
            None,
            [],
            {
                (1, 3, 1, 0x409): "Playfair 5pt SemiExpanded Light",
                (2, 3, 1, 0x409): "Regular",
                (3, 3, 1, 0x409): "2.000;FTH;Playfair-5ptSemiExpandedLight",
                (4, 3, 1, 0x409): "Playfair 5pt SemiExpanded Light",
                (6, 3, 1, 0x409): "Playfair-5ptSemiExpandedLight",
                (16, 3, 1, 0x409): "Playfair",
                (17, 3, 1, 0x409): "5pt SemiExpanded Light",
            },
        ),
    ],
)
def test_name_table(fp, family_name, style_name, siblings, expected):
    font = TTFont(fp)
    siblings = [TTFont(fp) for fp in siblings]
    build_name_table(font, family_name, style_name, siblings)
    _test_names(font, expected)


@pytest.mark.parametrize(
    "font_fp, dflt_coords, expected",
    [
        # Condensed variants
        (
            opensans_cond_roman_fp,
            {},
            [
                ("Thin", {"wght": 100.0}),
                ("ExtraLight", {"wght": 200.0}),
                ("Light", {"wght": 300.0}),
                ("Regular", {"wght": 400.0}),
                ("Medium", {"wght": 500.0}),
                ("SemiBold", {"wght": 600.0}),
                ("Bold", {"wght": 700.0}),
                ("ExtraBold", {"wght": 800.0}),
            ],
        ),
        (
            opensans_cond_italic_fp,
            {},
            [
                ("Thin Italic", {"wght": 100.0}),
                ("ExtraLight Italic", {"wght": 200.0}),
                ("Light Italic", {"wght": 300.0}),
                ("Italic", {"wght": 400.0}),
                ("Medium Italic", {"wght": 500.0}),
                ("SemiBold Italic", {"wght": 600.0}),
                ("Bold Italic", {"wght": 700.0}),
                ("ExtraBold Italic", {"wght": 800.0}),
            ],
        ),
        # Multi axis + roman & ital with dflt coords
        (
            roboto_flex_fp,
            {},
            [
                ("Thin", {"wght": 100.0}),
                ("ExtraLight", {"wght": 200.0}),
                ("Light", {"wght": 300.0}),
                ("Regular", {"wght": 400.0}),
                ("Medium", {"wght": 500.0}),
                ("SemiBold", {"wght": 600.0}),
                ("Bold", {"wght": 700.0}),
                ("ExtraBold", {"wght": 800.0}),
                ("Black", {"wght": 900.0}),
                ("Thin Italic", {"wght": 100.0, "slnt": -10}),
                ("ExtraLight Italic", {"wght": 200.0, "slnt": -10}),
                ("Light Italic", {"wght": 300.0, "slnt": -10}),
                ("Italic", {"wght": 400.0, "slnt": -10}),
                ("Medium Italic", {"wght": 500.0, "slnt": -10}),
                ("SemiBold Italic", {"wght": 600.0, "slnt": -10}),
                ("Bold Italic", {"wght": 700.0, "slnt": -10}),
                ("ExtraBold Italic", {"wght": 800.0, "slnt": -10}),
                ("Black Italic", {"wght": 900.0, "slnt": -10}),
            ],
        ),
        # multi axis with METADATA.pb registry_default_overrides
        # https://github.com/google/fonts/blob/main/ofl/robotoflex/METADATA.pb
        (
            roboto_flex_fp,
            {
                "XOPQ": 96.0,
                "XTRA": 468.0,
                "YOPQ": 79.0,
                "YTDE": -203.0,
                "YTFI": 738.0,
                "YTLC": 514.0,
                "YTUC": 712.0,
            },
            [
                ("Thin", {"wght": 100.0}),
                ("ExtraLight", {"wght": 200.0}),
                ("Light", {"wght": 300.0}),
                ("Regular", {"wght": 400.0}),
                ("Medium", {"wght": 500.0}),
                ("SemiBold", {"wght": 600.0}),
                ("Bold", {"wght": 700.0}),
                ("ExtraBold", {"wght": 800.0}),
                ("Black", {"wght": 900.0}),
                ("Thin Italic", {"wght": 100.0, "slnt": -10}),
                ("ExtraLight Italic", {"wght": 200.0, "slnt": -10}),
                ("Light Italic", {"wght": 300.0, "slnt": -10}),
                ("Italic", {"wght": 400.0, "slnt": -10}),
                ("Medium Italic", {"wght": 500.0, "slnt": -10}),
                ("SemiBold Italic", {"wght": 600.0, "slnt": -10}),
                ("Bold Italic", {"wght": 700.0, "slnt": -10}),
                ("ExtraBold Italic", {"wght": 800.0, "slnt": -10}),
                ("Black Italic", {"wght": 900.0, "slnt": -10}),
            ],
        ),
    ],
)
def test_fvar_instances(font_fp, dflt_coords, expected):
    font = TTFont(font_fp)
    builder = build_fvar_instances(font, dflt_coords)
    fvar = font["fvar"]
    wght_axis = next((a for a in fvar.axes if a.axisTag == "wght"), None)
    wght_min = wght_axis.minValue
    wght_max = wght_axis.maxValue
    instances = fvar.instances
    if not dflt_coords:
        dflt_coords = {k: v["value"] for k, v in _fvar_dflts(font).items()}

    assert len(instances) == len(expected)
    for inst, (expected_name, coords) in zip(instances, expected):
        inst_name = font["name"].getName(inst.subfamilyNameID, 3, 1, 0x409).toUnicode()
        assert inst_name == expected_name
        for tag, val in coords.items():
            dflt_coords[tag] = val
        assert inst.coordinates == dflt_coords


def dump(table, ttFont=None):
    return "\n".join(getXML(table.toXML, ttFont))


@pytest.mark.parametrize(
    "fp, sibling_fps",
    [
        (roboto_flex_fp, []),
        (
            opensans_roman_fp,
            [opensans_italic_fp, opensans_cond_roman_fp, opensans_cond_italic_fp],
        ),
        (
            opensans_italic_fp,
            [opensans_roman_fp, opensans_cond_roman_fp, opensans_cond_italic_fp],
        ),
        (
            opensans_cond_roman_fp,
            [opensans_roman_fp, opensans_italic_fp, opensans_cond_italic_fp],
        ),
        (
            opensans_cond_italic_fp,
            [opensans_roman_fp, opensans_italic_fp, opensans_cond_roman_fp],
        ),
        (wonky_fp, []),
        # don't add a linkedValue for Regular to Bold since Bold doesn't exist
        # Fixes https://github.com/googlefonts/axisregistry/issues/104
        (wavefont_fp, []),
    ],
)
def test_stat(fp, sibling_fps):
    font = TTFont(fp)
    siblings = [TTFont(f) for f in sibling_fps]
    build_stat(font, siblings)
    stat_fp = fp.replace(".ttf", "_STAT.ttx")

    ## output good files
    # with open(stat_fp, "w") as doc:
    #    got = dump(font["STAT"], font)
    #    doc.write(got)

    with open(stat_fp) as doc:
        expected = doc.read()
        got = dump(font["STAT"], font)
        assert got == expected


@pytest.mark.parametrize(
    "fp, expected",
    [
        (mavenpro_fp, "MavenPro-Regular.ttf"),
        (opensans_roman_fp, "OpenSans[wdth,wght].ttf"),
        (opensans_italic_fp, "OpenSans-Italic[wdth,wght].ttf"),
        (opensans_cond_roman_fp, "OpenSansCondensed[wght].ttf"),
        (opensans_cond_italic_fp, "OpenSansCondensed-Italic[wght].ttf"),
    ],
)
def test_filename(fp, expected):
    font = TTFont(fp)
    assert build_filename(font) == expected


@pytest.mark.parametrize(
    "fp, sibling_fps, result",
    [
        (roboto_flex_fp, [], False),
        (
            opensans_roman_fp,
            [opensans_italic_fp, opensans_cond_roman_fp, opensans_cond_italic_fp],
            True,
        ),
        (
            opensans_italic_fp,
            [opensans_roman_fp, opensans_cond_roman_fp, opensans_cond_italic_fp],
            True,
        ),
        (
            opensans_cond_roman_fp,
            [opensans_roman_fp, opensans_italic_fp, opensans_cond_italic_fp],
            True,
        ),
        (
            opensans_cond_italic_fp,
            [opensans_roman_fp, opensans_italic_fp, opensans_cond_roman_fp],
            True,
        ),
        (opensans_roman_fp, [opensans_cond_roman_fp], True),
        (opensans_roman_fp, [opensans_italic_fp], False),
        (wonky_fp, [], False),
    ],
)
def test_fvar_instance_collisions(fp, sibling_fps, result):
    ttFont = TTFont(fp)
    siblings = [TTFont(fp) for fp in sibling_fps]
    assert _fvar_instance_collisions(ttFont, siblings) == result


@pytest.mark.parametrize(
    "fp, result",
    [
        (roboto_flex_fp, "RobotoFlex"),
        (opensans_roman_fp, "OpenSans"),
        (opensans_italic_fp, "OpenSansItalic"),
        (opensans_cond_roman_fp, "OpenSansCondensed"),
        (opensans_cond_italic_fp, "OpenSansCondensedItalic"),
        (wonky_fp, "Wonky"),
    ],
)
def test_build_variations_ps_name(fp, result):
    ttFont = TTFont(fp)
    build_variations_ps_name(ttFont)
    variation_ps_name = ttFont["name"].getName(25, 3, 1, 0x409).toUnicode()
    assert variation_ps_name == result


@pytest.mark.parametrize(
    "fp, style_name, result",
    [
        (mavenpro_fp, "Regular", 400),
        (mavenpro_fp, "Italic", 400),
        (mavenpro_fp, "Black Italic", 900),
        (mavenpro_fp, "ExtraBold Italic", 800),
        (mavenpro_fp, "ExtraBold", 800),
        (mavenpro_fp, "Bold", 700),
        (mavenpro_fp, "Bold Italic", 700),
        (mavenpro_fp, "Thin Italic", 100),
        (mavenpro_fp, "Thin", 100),
    ],
)
def test_us_weight_class(fp, style_name, result):
    ttFont = TTFont(fp)
    build_name_table(ttFont, style_name=style_name)
    assert ttFont["OS/2"].usWeightClass == result
