from fontTools.ttLib.tables import otTables
from fontTools.otlLib.builder import buildStatTable
from fontTools.varLib import instancer

import pytest


def test_pruningUnusedNames(varfont):
    varNameIDs = instancer.names.getVariationNameIDs(varfont)

    assert varNameIDs == set(range(256, 297 + 1))

    fvar = varfont["fvar"]
    stat = varfont["STAT"].table

    with instancer.names.pruningUnusedNames(varfont):
        del fvar.axes[0]  # Weight (nameID=256)
        del fvar.instances[0]  # Thin (nameID=258)
        del stat.DesignAxisRecord.Axis[0]  # Weight (nameID=256)
        del stat.AxisValueArray.AxisValue[0]  # Thin (nameID=258)

    assert not any(n for n in varfont["name"].names if n.nameID in {256, 258})

    with instancer.names.pruningUnusedNames(varfont):
        del varfont["fvar"]
        del varfont["STAT"]

    assert not any(n for n in varfont["name"].names if n.nameID in varNameIDs)
    assert "ltag" not in varfont


def _test_name_records(varfont, expected, isNonRIBBI, platforms=[0x409]):
    nametable = varfont["name"]
    font_names = {
        (r.nameID, r.platformID, r.platEncID, r.langID): r.toUnicode()
        for r in nametable.names
    }
    for k in expected:
        if k[-1] not in platforms:
            continue
        assert font_names[k] == expected[k]

    font_nameids = set(i[0] for i in font_names)
    if isNonRIBBI:
        assert 16 in font_nameids
        assert 17 in font_nameids

    if "fvar" not in varfont:
        assert 25 not in font_nameids


@pytest.mark.parametrize(
    "limits, expected, isNonRIBBI",
    [
        # Regular
        (
            {"wght": 400},
            {
                (1, 3, 1, 0x409): "Test Variable Font",
                (2, 3, 1, 0x409): "Regular",
                (3, 3, 1, 0x409): "2.001;GOOG;TestVariableFont-Regular",
                (6, 3, 1, 0x409): "TestVariableFont-Regular",
            },
            False,
        ),
        # Regular Normal (width axis Normal isn't included since it is elided)
        (
            {"wght": 400, "wdth": 100},
            {
                (1, 3, 1, 0x409): "Test Variable Font",
                (2, 3, 1, 0x409): "Regular",
                (3, 3, 1, 0x409): "2.001;GOOG;TestVariableFont-Regular",
                (6, 3, 1, 0x409): "TestVariableFont-Regular",
            },
            False,
        ),
        # Black
        (
            {"wght": 900},
            {
                (1, 3, 1, 0x409): "Test Variable Font Black",
                (2, 3, 1, 0x409): "Regular",
                (3, 3, 1, 0x409): "2.001;GOOG;TestVariableFont-Black",
                (6, 3, 1, 0x409): "TestVariableFont-Black",
                (16, 3, 1, 0x409): "Test Variable Font",
                (17, 3, 1, 0x409): "Black",
            },
            True,
        ),
        # Thin
        (
            {"wght": 100},
            {
                (1, 3, 1, 0x409): "Test Variable Font Thin",
                (2, 3, 1, 0x409): "Regular",
                (3, 3, 1, 0x409): "2.001;GOOG;TestVariableFont-Thin",
                (6, 3, 1, 0x409): "TestVariableFont-Thin",
                (16, 3, 1, 0x409): "Test Variable Font",
                (17, 3, 1, 0x409): "Thin",
            },
            True,
        ),
        # Thin Condensed
        (
            {"wght": 100, "wdth": 79},
            {
                (1, 3, 1, 0x409): "Test Variable Font Thin Condensed",
                (2, 3, 1, 0x409): "Regular",
                (3, 3, 1, 0x409): "2.001;GOOG;TestVariableFont-ThinCondensed",
                (6, 3, 1, 0x409): "TestVariableFont-ThinCondensed",
                (16, 3, 1, 0x409): "Test Variable Font",
                (17, 3, 1, 0x409): "Thin Condensed",
            },
            True,
        ),
        # Condensed with unpinned weights
        (
            {"wdth": 79, "wght": (400, 900)},
            {
                (1, 3, 1, 0x409): "Test Variable Font Condensed",
                (2, 3, 1, 0x409): "Regular",
                (3, 3, 1, 0x409): "2.001;GOOG;TestVariableFont-Condensed",
                (6, 3, 1, 0x409): "TestVariableFont-Condensed",
                (16, 3, 1, 0x409): "Test Variable Font",
                (17, 3, 1, 0x409): "Condensed",
            },
            True,
        ),
        # Restrict weight and move default, new minimum (500) > old default (400)
        (
            {"wght": (500, 900)},
            {
                (1, 3, 1, 0x409): "Test Variable Font Medium",
                (2, 3, 1, 0x409): "Regular",
                (3, 3, 1, 0x409): "2.001;GOOG;TestVariableFont-Medium",
                (6, 3, 1, 0x409): "TestVariableFont-Medium",
                (16, 3, 1, 0x409): "Test Variable Font",
                (17, 3, 1, 0x409): "Medium",
            },
            True,
        ),
    ],
)
def test_updateNameTable_with_registered_axes_ribbi(
    varfont, limits, expected, isNonRIBBI
):
    instancer.names.updateNameTable(varfont, limits)
    _test_name_records(varfont, expected, isNonRIBBI)


def test_updatetNameTable_axis_order(varfont):
    axes = [
        dict(
            tag="wght",
            name="Weight",
            values=[
                dict(value=400, name="Regular"),
            ],
        ),
        dict(
            tag="wdth",
            name="Width",
            values=[
                dict(value=75, name="Condensed"),
            ],
        ),
    ]
    nametable = varfont["name"]
    buildStatTable(varfont, axes)
    instancer.names.updateNameTable(varfont, {"wdth": 75, "wght": 400})
    assert nametable.getName(17, 3, 1, 0x409).toUnicode() == "Regular Condensed"

    # Swap the axes so the names get swapped
    axes[0], axes[1] = axes[1], axes[0]

    buildStatTable(varfont, axes)
    instancer.names.updateNameTable(varfont, {"wdth": 75, "wght": 400})
    assert nametable.getName(17, 3, 1, 0x409).toUnicode() == "Condensed Regular"


@pytest.mark.parametrize(
    "limits, expected, isNonRIBBI",
    [
        # Regular | Normal
        (
            {"wght": 400},
            {
                (1, 3, 1, 0x409): "Test Variable Font",
                (2, 3, 1, 0x409): "Normal",
            },
            False,
        ),
        # Black | Negreta
        (
            {"wght": 900},
            {
                (1, 3, 1, 0x409): "Test Variable Font Negreta",
                (2, 3, 1, 0x409): "Normal",
                (16, 3, 1, 0x409): "Test Variable Font",
                (17, 3, 1, 0x409): "Negreta",
            },
            True,
        ),
        # Black Condensed | Negreta Zhuštěné
        (
            {"wght": 900, "wdth": 79},
            {
                (1, 3, 1, 0x409): "Test Variable Font Negreta Zhuštěné",
                (2, 3, 1, 0x409): "Normal",
                (16, 3, 1, 0x409): "Test Variable Font",
                (17, 3, 1, 0x409): "Negreta Zhuštěné",
            },
            True,
        ),
    ],
)
def test_updateNameTable_with_multilingual_names(varfont, limits, expected, isNonRIBBI):
    name = varfont["name"]
    # langID 0x405 is the Czech Windows langID
    name.setName("Test Variable Font", 1, 3, 1, 0x405)
    name.setName("Normal", 2, 3, 1, 0x405)
    name.setName("Normal", 261, 3, 1, 0x405)  # nameID 261=Regular STAT entry
    name.setName("Negreta", 266, 3, 1, 0x405)  # nameID 266=Black STAT entry
    name.setName("Zhuštěné", 279, 3, 1, 0x405)  # nameID 279=Condensed STAT entry

    instancer.names.updateNameTable(varfont, limits)
    _test_name_records(varfont, expected, isNonRIBBI, platforms=[0x405])


def test_updateNameTable_missing_axisValues(varfont):
    with pytest.raises(ValueError, match="Cannot find Axis Values {'wght': 200}"):
        instancer.names.updateNameTable(varfont, {"wght": 200})


def test_updateNameTable_missing_stat(varfont):
    del varfont["STAT"]
    with pytest.raises(
        ValueError, match="Cannot update name table since there is no STAT table."
    ):
        instancer.names.updateNameTable(varfont, {"wght": 400})


@pytest.mark.parametrize(
    "limits, expected, isNonRIBBI",
    [
        # Regular | Normal
        (
            {"wght": 400},
            {
                (1, 3, 1, 0x409): "Test Variable Font",
                (2, 3, 1, 0x409): "Italic",
                (6, 3, 1, 0x409): "TestVariableFont-Italic",
            },
            False,
        ),
        # Black Condensed Italic
        (
            {"wght": 900, "wdth": 79},
            {
                (1, 3, 1, 0x409): "Test Variable Font Black Condensed",
                (2, 3, 1, 0x409): "Italic",
                (6, 3, 1, 0x409): "TestVariableFont-BlackCondensedItalic",
                (16, 3, 1, 0x409): "Test Variable Font",
                (17, 3, 1, 0x409): "Black Condensed Italic",
            },
            True,
        ),
    ],
)
def test_updateNameTable_vf_with_italic_attribute(
    varfont, limits, expected, isNonRIBBI
):
    font_link_axisValue = varfont["STAT"].table.AxisValueArray.AxisValue[5]
    # Unset ELIDABLE_AXIS_VALUE_NAME flag
    font_link_axisValue.Flags &= ~instancer.names.ELIDABLE_AXIS_VALUE_NAME
    font_link_axisValue.ValueNameID = 294  # Roman --> Italic

    instancer.names.updateNameTable(varfont, limits)
    _test_name_records(varfont, expected, isNonRIBBI)


def test_updateNameTable_format4_axisValues(varfont):
    # format 4 axisValues should dominate the other axisValues
    stat = varfont["STAT"].table

    axisValue = otTables.AxisValue()
    axisValue.Format = 4
    axisValue.Flags = 0
    varfont["name"].setName("Dominant Value", 297, 3, 1, 0x409)
    axisValue.ValueNameID = 297
    axisValue.AxisValueRecord = []
    for tag, value in (("wght", 900), ("wdth", 79)):
        rec = otTables.AxisValueRecord()
        rec.AxisIndex = next(
            i for i, a in enumerate(stat.DesignAxisRecord.Axis) if a.AxisTag == tag
        )
        rec.Value = value
        axisValue.AxisValueRecord.append(rec)
    stat.AxisValueArray.AxisValue.append(axisValue)

    instancer.names.updateNameTable(varfont, {"wdth": 79, "wght": 900})
    expected = {
        (1, 3, 1, 0x409): "Test Variable Font Dominant Value",
        (2, 3, 1, 0x409): "Regular",
        (16, 3, 1, 0x409): "Test Variable Font",
        (17, 3, 1, 0x409): "Dominant Value",
    }
    _test_name_records(varfont, expected, isNonRIBBI=True)


def test_updateNameTable_elided_axisValues(varfont):
    stat = varfont["STAT"].table
    # set ELIDABLE_AXIS_VALUE_NAME flag for all axisValues
    for axisValue in stat.AxisValueArray.AxisValue:
        axisValue.Flags |= instancer.names.ELIDABLE_AXIS_VALUE_NAME

    stat.ElidedFallbackNameID = 266  # Regular --> Black
    instancer.names.updateNameTable(varfont, {"wght": 400})
    # Since all axis values are elided, the elided fallback name
    # must be used to construct the style names. Since we
    # changed it to Black, we need both a typoSubFamilyName and
    # the subFamilyName set so it conforms to the RIBBI model.
    expected = {(2, 3, 1, 0x409): "Regular", (17, 3, 1, 0x409): "Black"}
    _test_name_records(varfont, expected, isNonRIBBI=True)


def test_updateNameTable_existing_subfamily_name_is_not_regular(varfont):
    # Check the subFamily name will be set to Regular when we update a name
    # table to a non-RIBBI style and the current subFamily name is a RIBBI
    # style which isn't Regular.
    varfont["name"].setName("Bold", 2, 3, 1, 0x409)  # subFamily Regular --> Bold

    instancer.names.updateNameTable(varfont, {"wght": 100})
    expected = {(2, 3, 1, 0x409): "Regular", (17, 3, 1, 0x409): "Thin"}
    _test_name_records(varfont, expected, isNonRIBBI=True)


def test_name_irrelevant_axes(varfont):
    # Cannot update name table if not on a named axis value location
    with pytest.raises(ValueError) as excinfo:
        location = {"wght": 400, "wdth": 90}
        instance = instancer.instantiateVariableFont(
            varfont, location, updateFontNames=True
        )
    assert "Cannot find Axis Values" in str(excinfo.value)

    # Now let's make the wdth axis "irrelevant" to naming (no axis values)
    varfont["STAT"].table.AxisValueArray.AxisValue.pop(6)
    varfont["STAT"].table.AxisValueArray.AxisValue.pop(4)
    location = {"wght": 400, "wdth": 90}
    instance = instancer.instantiateVariableFont(
        varfont, location, updateFontNames=True
    )
