from fontbakery.callable import check
from fontbakery.callable import condition
from fontbakery.checkrunner import Section, PASS, FAIL, WARN
from fontbakery.fonts_profile import profile_factory
from tests.test_general import (
    font_family,
    font_style,
    is_italic,
    com_roboto_fonts_check_italic_angle,
    com_roboto_fonts_check_fs_type,
    com_roboto_fonts_check_vendorid,
    com_roboto_fonts_check_digit_widths,
)

profile = profile_factory(default_section=Section("Roboto web v3"))

ROBOTO_PROFILE_CHECKS = [
    "com.roboto.fonts/check/vertical_metrics",
    "com.roboto.fonts/check/oblique_bits_not_set",
    "com.roboto.fonts/check/unique_id",
    "com.roboto.fonts/check/hinting",
    "com.roboto.fonts/check/italic_angle",
    "com.roboto.fonts/check/fs_type",
    "com.roboto.fonts/check/vendorid",
    "com.roboto.fonts/check/digit_widths",
]


@check(
    id="com.roboto.fonts/check/vertical_metrics",
)
def com_roboto_fonts_check_vertical_metrics(ttFont):
    """Check vertical metrics are correct"""
    failed = []
    expected = {
        # android requires this, and web fonts expect this
        ("head", "yMin"): -555,
        ("head", "yMax"): 2163,
        # test hhea ascent, descent, and lineGap to be equal to Roboto v1 values
        ("hhea", "descent"): -500,
        ("hhea", "ascent"): 1900,
        ("hhea", "lineGap"): 0,
        # test OS/2 vertical metrics to be equal to old OS/2 win values
        # since fsSelection bit 7 is now enabled
        ("OS/2", "sTypoDescender"): -512,
        ("OS/2", "sTypoAscender"): 1536,
        ("OS/2", "sTypoLineGap"): 102,
        ("OS/2", "usWinDescent"): 512,
        ("OS/2", "usWinAscent"): 1946,
    }
    for (table, k), v in expected.items():
        font_val = getattr(ttFont[table], k)
        if font_val != v:
            failed.append((table, k, v, font_val))
    if not failed:
        yield PASS, "Fonts have correct vertical metrics"
    else:
        msg = "\n".join(
            [
                f"- {tbl}.{k} is {font_val} it should be {v}"
                for tbl, k, v, font_val in failed
            ]
        )
        yield FAIL, f"Fonts have incorrect vertical metrics:\n{msg}"


@check(
    id="com.roboto.fonts/check/oblique_bits_not_set",
)
def com_roboto_fonts_check_oblique_bits_not_set(ttFont):
    """Check oblique bits are not set in fonts"""
    if ttFont['OS/2'].fsSelection & (1 << 9) != 0:
        yield FAIL, "fsSelection bit 9 (Oblique) must not be enabled"
    else:
        yield PASS, "fsSelection bit 9 is disabled"


@check(
    id="com.roboto.fonts/check/unique_id",
)
def com_roboto_fonts_check_unique_id(ttFont):
    """Check font unique id is correct"""
    family_name = font_family(ttFont)
    style = font_style(ttFont)
    if style != "Regular":
        expected = f"{family_name} {style}"
    else:
        expected = f"{family_name}"
    font_unique_id = ttFont['name'].getName(3, 3, 1, 1033).toUnicode()
    if font_unique_id == expected:
        yield PASS, "Unique ID is correct"
    else:
        yield FAIL, f"Unique ID, '{font_unique_id}' is incorrect. It should be '{expected}'"



@check(
    id="com.roboto.fonts/check/hinting",
)
def com_roboto_fonts_check_hinting(ttFont):
    """Check glyphs have hinting"""
    missing_hints = []
    # we can ignore these according to Mike D
    # https://github.com/TypeNetwork/Roboto/issues/70#issuecomment-641221200
    ignore = ['.notdef', 'uni0488', 'uni0489', 'uniFFFC', 'uniFFFD']
    for glyph_name in ttFont.getGlyphOrder():
        glyph = ttFont['glyf'][glyph_name]
        if glyph.numberOfContours <= 0:
            continue
        if len(glyph.program.bytecode) <= 0 and glyph_name not in ignore:
            missing_hints.append(glyph_name)
    if missing_hints:
        yield FAIL, f"Following glyphs are missing hinting {missing_hints}"
    else:
        yield PASS, "All glyphs are hinted"


profile.auto_register(globals())
profile.test_expected_checks(ROBOTO_PROFILE_CHECKS, exclusive=True)
