from collections import OrderedDict, defaultdict
from typing import DefaultDict, Dict, Optional, Tuple

OPENING_BRACE = "{"
CLOSING_BRACE = "}"
BRACES = "{}"

OPENING_BRACKET = "["
CLOSING_BRACKET = "]"
BRACKETS = "[]"

OPENING_PARENTHESIS = "("
CLOSING_PARENTHESIS = ")"
PARENTHESES = "()"

SUBSUP = "_^"
SUBSCRIPT = "_"
SUPERSCRIPT = "^"
APOSTROPHE = "'"
PRIME = r"\prime"
DPRIME = r"\dprime"

LEFT = r"\left"
RIGHT = r"\right"

ABOVE = r"\above"
ABOVEWITHDELIMS = r"\abovewithdelims"
ATOP = r"\atop"
ATOPWITHDELIMS = r"\atopwithdelims"
BINOM = r"\binom"
BRACE = r"\brace"
BRACK = r"\brack"
CFRAC = r"\cfrac"
CHOOSE = r"\choose"
DBINOM = r"\dbinom"
DFRAC = r"\dfrac"
FRAC = r"\frac"
GENFRAC = r"\genfrac"
OVER = r"\over"
TBINOM = r"\tbinom"
TFRAC = r"\tfrac"

ROOT = r"\root"
SQRT = r"\sqrt"

OVERSET = r"\overset"
UNDERSET = r"\underset"

ACUTE = r"\acute"
BAR = r"\bar"
BREVE = r"\breve"
CHECK = r"\check"
DOT = r"\dot"
DDOT = r"\ddot"
DDDOT = r"\dddot"
DDDDOT = r"\ddddot"
GRAVE = r"\grave"
HAT = r"\hat"
MATHRING = r"\mathring"
OVERLEFTARROW = r"\overleftarrow"
OVERLEFTRIGHTARROW = r"\overleftrightarrow"
OVERLINE = r"\overline"
OVERPAREN = r"\overparen"
OVERRIGHTARROW = r"\overrightarrow"
TILDE = r"\tilde"
UNDERLEFTARROW = r"\underleftarrow"
UNDERLINE = r"\underline"
UNDERPAREN = r"\underparen"
UNDERRIGHTARROW = r"\underrightarrow"
UNDERLEFTRIGHTARROW = r"\underleftrightarrow"
VEC = r"\vec"
WIDEHAT = r"\widehat"
WIDETILDE = r"\widetilde"

HREF = r"\href"
TEXT = r"\text"
TEXTBF = r"\textbf"
TEXTIT = r"\textit"
TEXTRM = r"\textrm"
TEXTSF = r"\textsf"
TEXTTT = r"\texttt"

BEGIN = r"\begin"
END = r"\end"

LIMITS = r"\limits"
INTEGRAL = r"\int"
SUMMATION = r"\sum"
LIMIT = (r"\lim", r"\sup", r"\inf", r"\max", r"\min")

OPERATORNAME = r"\operatorname"

LBRACE = r"\{"

FUNCTIONS = (
    r"\arccos",
    r"\arcsin",
    r"\arctan",
    r"\cos",
    r"\cosh",
    r"\cot",
    r"\coth",
    r"\csc",
    r"\deg",
    r"\dim",
    r"\exp",
    r"\hom",
    r"\ker",
    r"\ln",
    r"\lg",
    r"\log",
    r"\sec",
    r"\sin",
    r"\sinh",
    r"\tan",
    r"\tanh",
)
DETERMINANT = r"\det"
GCD = r"\gcd"
INTOP = r"\intop"
INJLIM = r"\injlim"
LIMINF = r"\liminf"
LIMSUP = r"\limsup"
PR = r"\Pr"
PROJLIM = r"\projlim"

HDASHLINE = r"\hdashline"
HLINE = r"\hline"
HFIL = r"\hfil"

CASES = r"\cases"
DISPLAYLINES = r"\displaylines"
SUBSTACK = r"\substack"
MATRICES = (
    r"\matrix",
    r"\matrix*",
    r"\pmatrix",
    r"\pmatrix*",
    r"\bmatrix",
    r"\bmatrix*",
    r"\Bmatrix",
    r"\Bmatrix*",
    r"\vmatrix",
    r"\vmatrix*",
    r"\Vmatrix",
    r"\Vmatrix*",
    r"\array",
    SUBSTACK,
    CASES,
    DISPLAYLINES,
)

BACKSLASH = "\\"
CARRIAGE_RETURN = r"\cr"

COLON = r"\:"
COMMA = r"\,"
DOUBLEBACKSLASH = r"\\"
ENSPACE = r"\enspace"
EXCLAMATION = r"\!"
GREATER_THAN = r"\>"
HSKIP = r"\hskip"
HSPACE = r"\hspace"
KERN = r"\kern"
MKERN = r"\mkern"
MSKIP = r"\mskip"
MSPACE = r"\mspace"
NEGTHINSPACE = r"\negthinspace"
NEGMEDSPACE = r"\negmedspace"
NEGTHICKSPACE = r"\negthickspace"
NOBREAKSPACE = r"\nobreakspace"
SPACE = r"\space"
THINSPACE = r"\thinspace"
QQUAD = r"\qquad"
QUAD = r"\quad"
SEMICOLON = r"\;"

BLACKBOARD_BOLD = r"\Bbb"
BOLD_SYMBOL = r"\boldsymbol"
MIT = r"\mit"
OLDSTYLE = r"\oldstyle"
SCR = r"\scr"
TT = r"\tt"

MATH = r"\math"
MATHBB = r"\mathbb"
MATHBF = r"\mathbf"
MATHCAL = r"\mathcal"
MATHFRAK = r"\mathfrak"
MATHIT = r"\mathit"
MATHRM = r"\mathrm"
MATHSCR = r"\mathscr"
MATHSF = r"\mathsf"
MATHTT = r"\mathtt"

BOXED = r"\boxed"
FBOX = r"\fbox"
HBOX = r"\hbox"
MBOX = r"\mbox"

COLOR = r"\color"
DISPLAYSTYLE = r"\displaystyle"
TEXTSTYLE = r"\textstyle"
SCRIPTSTYLE = r"\scriptstyle"
SCRIPTSCRIPTSTYLE = r"\scriptscriptstyle"
STYLE = r"\style"

HPHANTOM = r"\hphantom"
PHANTOM = r"\phantom"
VPHANTOM = r"\vphantom"

IDOTSINT = r"\idotsint"
LATEX = r"\LaTeX"
TEX = r"\TeX"

SIDESET = r"\sideset"

SKEW = r"\skew"


def font_factory(default: Optional[str], replacement: Dict[str, Optional[str]]) -> DefaultDict[str, Optional[str]]:
    fonts = defaultdict(lambda: default, replacement)
    return fonts


LOCAL_FONTS: Dict[str, DefaultDict[str, Optional[str]]] = {
    BLACKBOARD_BOLD: font_factory("double-struck", {"fence": None}),
    BOLD_SYMBOL: font_factory("bold", {"mi": "bold-italic", "mtext": None}),
    MATHBB: font_factory("double-struck", {"fence": None}),
    MATHBF: font_factory("bold", {"fence": None}),
    MATHCAL: font_factory("script", {"fence": None}),
    MATHFRAK: font_factory("fraktur", {"fence": None}),
    MATHIT: font_factory("italic", {"fence": None}),
    MATHRM: font_factory(None, {"mi": "normal"}),
    MATHSCR: font_factory("script", {"fence": None}),
    MATHSF: font_factory(None, {"mi": "sans-serif"}),
    MATHTT: font_factory("monospace", {"fence": None}),
    MIT: font_factory("italic", {"fence": None, "mi": None}),
    OLDSTYLE: font_factory("normal", {"fence": None}),
    SCR: font_factory("script", {"fence": None}),
    TT: font_factory("monospace", {"fence": None}),
}

OLD_STYLE_FONTS: Dict[str, DefaultDict[str, Optional[str]]] = {
    r"\rm": font_factory(None, {"mi": "normal"}),
    r"\bf": font_factory(None, {"mi": "bold"}),
    r"\it": font_factory(None, {"mi": "italic"}),
    r"\sf": font_factory(None, {"mi": "sans-serif"}),
    r"\tt": font_factory(None, {"mi": "monospace"}),
}

GLOBAL_FONTS = {
    **OLD_STYLE_FONTS,
    r"\cal": font_factory("script", {"fence": None}),
    r"\frak": font_factory("fraktur", {"fence": None}),
}

COMMANDS_WITH_ONE_PARAMETER = (
    ACUTE,
    BAR,
    BLACKBOARD_BOLD,
    BOLD_SYMBOL,
    BOXED,
    BREVE,
    CHECK,
    DOT,
    DDOT,
    DDDOT,
    DDDDOT,
    GRAVE,
    HAT,
    HPHANTOM,
    MATHRING,
    MIT,
    OLDSTYLE,
    OVERLEFTARROW,
    OVERLEFTRIGHTARROW,
    OVERLINE,
    OVERPAREN,
    OVERRIGHTARROW,
    PHANTOM,
    SCR,
    TILDE,
    TT,
    UNDERLEFTARROW,
    UNDERLINE,
    UNDERPAREN,
    UNDERRIGHTARROW,
    UNDERLEFTRIGHTARROW,
    VEC,
    VPHANTOM,
    WIDEHAT,
    WIDETILDE,
)
COMMANDS_WITH_TWO_PARAMETERS = (
    BINOM,
    CFRAC,
    DBINOM,
    DFRAC,
    FRAC,
    OVERSET,
    TBINOM,
    TFRAC,
    UNDERSET,
)

BIG: Dict[str, Tuple[str, dict]] = {
    # command: (mathml_equivalent, attributes)
    r"\Bigg": ("mo", OrderedDict([("minsize", "2.470em"), ("maxsize", "2.470em")])),
    r"\bigg": ("mo", OrderedDict([("minsize", "2.047em"), ("maxsize", "2.047em")])),
    r"\Big": ("mo", OrderedDict([("minsize", "1.623em"), ("maxsize", "1.623em")])),
    r"\big": ("mo", OrderedDict([("minsize", "1.2em"), ("maxsize", "1.2em")])),
}

MSTYLE_SIZES: Dict[str, Tuple[str, dict]] = {
    # command: (mathml_equivalent, attributes)
    r"\Huge": ("mstyle", {"mathsize": "2.49em"}),
    r"\huge": ("mstyle", {"mathsize": "2.07em"}),
    r"\LARGE": ("mstyle", {"mathsize": "1.73em"}),
    r"\Large": ("mstyle", {"mathsize": "1.44em"}),
    r"\large": ("mstyle", {"mathsize": "1.2em"}),
    r"\normalsize": ("mstyle", {"mathsize": "1em"}),
    r"\scriptsize": ("mstyle", {"mathsize": "0.7em"}),
    r"\small": ("mstyle", {"mathsize": "0.85em"}),
    r"\tiny": ("mstyle", {"mathsize": "0.5em"}),
    r"\Tiny": ("mstyle", {"mathsize": "0.6em"}),
}

STYLES: Dict[str, Tuple[str, dict]] = {
    DISPLAYSTYLE: ("mstyle", {"displaystyle": "true", "scriptlevel": "0"}),
    TEXTSTYLE: ("mstyle", {"displaystyle": "false", "scriptlevel": "0"}),
    SCRIPTSTYLE: ("mstyle", {"displaystyle": "false", "scriptlevel": "1"}),
    SCRIPTSCRIPTSTYLE: ("mstyle", {"displaystyle": "false", "scriptlevel": "2"}),
}

CONVERSION_MAP: Dict[str, Tuple[str, dict]] = {
    # command: (mathml_equivalent, attributes)
    # tables
    **{matrix: ("mtable", {}) for matrix in MATRICES},
    DISPLAYLINES: ("mtable", {"rowspacing": "0.5em", "columnspacing": "1em", "displaystyle": "true"}),
    # subscripts/superscripts
    SUBSCRIPT: ("msub", {}),
    SUPERSCRIPT: ("msup", {}),
    SUBSUP: ("msubsup", {}),
    # fractions
    BINOM: ("mfrac", {"linethickness": "0"}),
    CFRAC: ("mfrac", {}),
    DBINOM: ("mfrac", {"linethickness": "0"}),
    DFRAC: ("mfrac", {}),
    FRAC: ("mfrac", {}),
    GENFRAC: ("mfrac", {}),
    TBINOM: ("mfrac", {"linethickness": "0"}),
    TFRAC: ("mfrac", {}),
    # over/under
    ACUTE: ("mover", {}),
    BAR: ("mover", {}),
    BREVE: ("mover", {}),
    CHECK: ("mover", {}),
    DOT: ("mover", {}),
    DDOT: ("mover", {}),
    DDDOT: ("mover", {}),
    DDDDOT: ("mover", {}),
    GRAVE: ("mover", {}),
    HAT: ("mover", {}),
    LIMITS: ("munderover", {}),
    MATHRING: ("mover", {}),
    OVERLEFTARROW: ("mover", {}),
    OVERLEFTRIGHTARROW: ("mover", {}),
    OVERLINE: ("mover", {}),
    OVERPAREN: ("mover", {}),
    OVERRIGHTARROW: ("mover", {}),
    TILDE: ("mover", {}),
    OVERSET: ("mover", {}),
    UNDERLEFTARROW: ("munder", {}),
    UNDERLINE: ("munder", {}),
    UNDERPAREN: ("munder", {}),
    UNDERRIGHTARROW: ("munder", {}),
    UNDERLEFTRIGHTARROW: ("munder", {}),
    UNDERSET: ("munder", {}),
    VEC: ("mover", {}),
    WIDEHAT: ("mover", {}),
    WIDETILDE: ("mover", {}),
    # spaces
    COLON: ("mspace", {"width": "0.222em"}),
    COMMA: ("mspace", {"width": "0.167em"}),
    DOUBLEBACKSLASH: ("mspace", {"linebreak": "newline"}),
    ENSPACE: ("mspace", {"width": "0.5em"}),
    EXCLAMATION: ("mspace", {"width": "negativethinmathspace"}),
    GREATER_THAN: ("mspace", {"width": "0.222em"}),
    HSKIP: ("mspace", {}),
    HSPACE: ("mspace", {}),
    KERN: ("mspace", {}),
    MKERN: ("mspace", {}),
    MSKIP: ("mspace", {}),
    MSPACE: ("mspace", {}),
    NEGTHINSPACE: ("mspace", {"width": "negativethinmathspace"}),
    NEGMEDSPACE: ("mspace", {"width": "negativemediummathspace"}),
    NEGTHICKSPACE: ("mspace", {"width": "negativethickmathspace"}),
    THINSPACE: ("mspace", {"width": "thinmathspace"}),
    QQUAD: ("mspace", {"width": "2em"}),
    QUAD: ("mspace", {"width": "1em"}),
    SEMICOLON: ("mspace", {"width": "0.278em"}),
    # enclose
    BOXED: ("menclose", {"notation": "box"}),
    FBOX: ("menclose", {"notation": "box"}),
    # operators
    **BIG,
    **MSTYLE_SIZES,
    **{limit: ("mo", {}) for limit in LIMIT},
    LEFT: ("mo", OrderedDict([("stretchy", "true"), ("fence", "true"), ("form", "prefix")])),
    RIGHT: ("mo", OrderedDict([("stretchy", "true"), ("fence", "true"), ("form", "postfix")])),
    # styles
    COLOR: ("mstyle", {}),
    **STYLES,
    # others
    SQRT: ("msqrt", {}),
    ROOT: ("mroot", {}),
    HREF: ("mtext", {}),
    TEXT: ("mtext", {}),
    TEXTBF: ("mtext", {"mathvariant": "bold"}),
    TEXTIT: ("mtext", {"mathvariant": "italic"}),
    TEXTRM: ("mtext", {}),
    TEXTSF: ("mtext", {"mathvariant": "sans-serif"}),
    TEXTTT: ("mtext", {"mathvariant": "monospace"}),
    HBOX: ("mtext", {}),
    MBOX: ("mtext", {}),
    HPHANTOM: ("mphantom", {}),
    PHANTOM: ("mphantom", {}),
    VPHANTOM: ("mphantom", {}),
    SIDESET: ("mrow", {}),
    SKEW: ("mrow", {}),
}


DIACRITICS: Dict[str, Tuple[str, Dict[str, str]]] = {
    ACUTE: ("&#x000B4;", {}),
    BAR: ("&#x000AF;", {"stretchy": "true"}),
    BREVE: ("&#x002D8;", {}),
    CHECK: ("&#x002C7;", {}),
    DOT: ("&#x002D9;", {}),
    DDOT: ("&#x000A8;", {}),
    DDDOT: ("&#x020DB;", {}),
    DDDDOT: ("&#x020DC;", {}),
    GRAVE: ("&#x00060;", {}),
    HAT: ("&#x0005E;", {"stretchy": "false"}),
    MATHRING: ("&#x002DA;", {}),
    OVERLEFTARROW: ("&#x02190;", {}),
    OVERLEFTRIGHTARROW: ("&#x02194;", {}),
    OVERLINE: ("&#x02015;", {"accent": "true"}),
    OVERPAREN: ("&#x023DC;", {}),
    OVERRIGHTARROW: ("&#x02192;", {}),
    TILDE: ("&#x0007E;", {"stretchy": "false"}),
    UNDERLEFTARROW: ("&#x02190;", {}),
    UNDERLEFTRIGHTARROW: ("&#x02194;", {}),
    UNDERLINE: ("&#x02015;", {"accent": "true"}),
    UNDERPAREN: ("&#x023DD;", {}),
    UNDERRIGHTARROW: ("&#x02192;", {}),
    VEC: ("&#x02192;", {"stretchy": "true"}),
    WIDEHAT: ("&#x0005E;", {}),
    WIDETILDE: ("&#x0007E;", {}),
}
