#!/usr/bin/env python3

from lxml import etree
from utils.misc import downloadWithProgressBar, UnicodeXMLURL, InlineAxisOperatorsURL
import json
import re
from utils import mathfont

NonBreakingSpace = 0x00A0


def parseHexaNumber(string):
    return int("0x%s" % string, 16)


def parseHexaSequence(string):
    return tuple(map(parseHexaNumber, string[1:].split("-")))


def parseSpaces(value, entry, names):
    for name in names:
        attributeValue = entry.get(name)
        if attributeValue is not None:
            value[name] = int(attributeValue)


def parseProperties(value, entry, names):
    attributeValue = entry.get("properties")
    if attributeValue is not None:
        for name in names:
            if attributeValue.find(name) >= 0:
                value[name] = True


def buildKeyAndValueFrom(characters, form):
    # Concatenate characters and form to build the key.
    key = ""
    for c in characters:
        key += chr(c)
    key += " " + form
    # But save characters as an individual property for easier manipulation in
    # this Python script.
    value = {
        "characters": characters,
    }
    return key, value


# Retrieve the spec files.
inlineAxisOperatorsTXT = downloadWithProgressBar(InlineAxisOperatorsURL)
unicodeXML = downloadWithProgressBar(UnicodeXMLURL)

# Extract the operator dictionary.
xsltTransform = etree.XSLT(etree.parse("./operator-dictionary.xsl"))

# Put the operator dictionary into a Python structure.
inlineAxisOperators = {}
with open(inlineAxisOperatorsTXT, mode="r") as f:
    for line in f:
        hexaString = re.match(r"^U\+([0-9A-F]+)", line).group(1)
        inlineAxisOperators[parseHexaNumber(hexaString)] = True

operatorDictionary = {}
root = xsltTransform(etree.parse(unicodeXML)).getroot()
for entry in root:
    characters = parseHexaSequence(entry.get("unicode"))
    assert characters != (NonBreakingSpace)
    key, value = buildKeyAndValueFrom(characters, entry.get("form"))
    # There is no dictionary-specified minsize/maxsize values, so no need to
    # parse them.
    # The fence, separator and priority properties don't have any effect on math
    # layout, so they are not added to the JSON file.
    parseSpaces(value, entry, ["lspace", "rspace"])
    parseProperties(value, entry, ["stretchy", "symmetric", "largeop",
                                   "movablelimits", "accent"])
    if (len(characters) == 1 and characters[0] in inlineAxisOperators):
        value["horizontal"] = True
    operatorDictionary[key] = value

# Create entries for the non-breaking space in all forms in order to test the
# default for operators outside the official dictionary.
for form in ["infix", "prefix", "suffix"]:
    key, value = buildKeyAndValueFrom(tuple([NonBreakingSpace]), form)
    operatorDictionary[key] = value

# Create a WOFF font with glyphs for all the operator strings.
font = mathfont.create("operators", "Copyright (c) 2019 Igalia S.L.")

# Set parameters for largeop and stretchy tests.
font.math.DisplayOperatorMinHeight = 2 * mathfont.em
font.math.MinConnectorOverlap = mathfont.em // 2

# Set parameters for accent tests so that we only have large gap when
# overscript is an accent.
font.math.UpperLimitBaselineRiseMin = 0
font.math.StretchStackTopShiftUp = 0
font.math.AccentBaseHeight = 2 * mathfont.em
font.math.OverbarVerticalGap = 0

mathfont.createSizeVariants(font, True)

# Ensure a glyph exists for the combining characters that are handled specially
# in the specification:
# U+0338 COMBINING LONG SOLIDUS OVERLAY
# U+20D2 COMBINING LONG VERTICAL LINE OVERLAY
for combining_character in [0x338, 0x20D2]:
    mathfont.createSquareGlyph(font, combining_character)

for key in operatorDictionary:
    value = operatorDictionary[key]
    for c in value["characters"]:
        if c in font:
            continue
        if c == NonBreakingSpace:
            g = font.createChar(c)
            mathfont.drawRectangleGlyph(g, mathfont.em, mathfont.em // 3, 0)
        else:
            mathfont.createSquareGlyph(font, c)
        mathfont.createStretchy(font, c, c in inlineAxisOperators)
mathfont.save(font)

# Generate the python file.
for key in operatorDictionary:
    del operatorDictionary[key]["characters"]  # Remove this temporary value.
JSON = {
    "comment": "This file was automatically generated by operator-dictionary.py. Do not edit.",
    "dictionary": operatorDictionary
}
with open('../support/operator-dictionary.json', 'w') as fp:
    json.dump(JSON, fp, sort_keys=True, ensure_ascii=True)
