import re
from itertools import product


__all__ = ['convert']


small_greek = [
    r'\alpha \beta \gamma \delta \epsilon \zeta \eta \theta \iota \kappa \lambda \mu \nu '  # tex
    r'\xi \omicron \pi \rho \varsigma \sigma \tau \ypsilon \phi \chi \psi \omega',
    '\u03b1 \u03b2 \u03b3 \u03b4 \u03b5 \u03b6 \u03b7 \u03b8 \u03b9 \u03ba \u03bb \u03bc \u03bd '  # unicode (for html)
    '\u03be \u03bf \u03c0 \u03c1 \u03c2 \u03c3 \u03c4 \u03c5 \u03c6 \u03c7 \u03c8 \u03c9',
    r'\f{Symbol}a\f{} \f{Symbol}b\f{} \f{Symbol}g\f{} \f{Symbol}d\f{} \f{Symbol}e\f{} \f{Symbol}z\f{} '  # grace
    r'\f{Symbol}h\f{} \f{Symbol}q\f{} \f{Symbol}i\f{} \f{Symbol}k\f{} \f{Symbol}l\f{} \f{Symbol}\f{} '
    r'\f{Symbol}n\f{} \f{Symbol}x\f{} \f{Symbol}o\f{} \f{Symbol}p\f{} \f{Symbol}r\f{} \f{Symbol}s\f{} '
    r'\f{Symbol}s\f{} \f{Symbol}t\f{} \f{Symbol}u\f{} \f{Symbol}f\f{} \f{Symbol}c\f{} \f{Symbol}y\f{} '
    r'\f{Symbol}w\f{}',
    'alpha beta gamma delta epsilon zeta eta theta iota kappa lambda mu nu '  # plain
    'xi omicron pi rho sigma sigma tau ypsilon phi chi psi omega',
]
big_greek = [
    r'\Alpha \Beta \Gamma \Delta \Epsilon \Zeta \Eta \Theta \Iota \Kappa \Lambda \Mu '  # tex
    r'\Nu \Xi \Omicron \Pi \Rho \Sigma \Tau \Ypsilon \Phi \Chi \Psi \Omega',
    '\u0391 \u0392 \u0393 \u0394 \u0395 \u0396 \u0397 \u0398 \u0399 \u039a \u039b \u039c '  # unicode (for html)
    '\u039d \u039e \u039f \u03a0 \u03a1 \u03a3 \u03a4 \u03a5 \u03a6 \u03a7 \u03a8 \u03a9',
    r'\f{Symbol}A\f{} \f{Symbol}B\f{} \f{Symbol}G\f{} \f{Symbol}D\f{} \f{Symbol}E\f{} \f{Symbol}Z\f{} '  # grace
    r'\f{Symbol}H\f{} \f{Symbol}Q\f{} \f{Symbol}I\f{} \f{Symbol}K\f{} \f{Symbol}L\f{} \f{Symbol}M\f{} '
    r'\f{Symbol}N\f{} \f{Symbol}X\f{} \f{Symbol}O\f{} \f{Symbol}P\f{} \f{Symbol}R\f{} \f{Symbol}S\f{} '
    r'\f{Symbol}T\f{} \f{Symbol}Y\f{} \f{Symbol}F\f{} \f{Symbol}C\f{} \f{Symbol}U\f{} \f{Symbol}W\f{}',
    'Alpha Beta Gamma Delta Epsilon Zeta Eta Theta Iota Kappa Lambda Mu '  # plain
    'Nu Xi Omicron Pi Rho Sigma Tau Ypsilon Phi Chi Psi Omega',
]
special_chars = [
    r'\infty \int \sum \langle \rangle \pm \perp \para \leftarrow \rightarrow \leftrightarrow \cdot \hbar \n',
    '\u221e \u222b \u2211 \u27e8 \u27e9 \u00b1 \u27c2 \u2225 \u21d0 \u21d2 \u21d4 \u00b7 \u0127 <br>',
    r'\f{Symbol}¥\f{} \f{Symbol}ò\f{} \f{Symbol}å\f{} \f{Symbol}á\f{} \f{Symbol}ñ\f{} \f{Symbol}±\f{} '
    r'\f{Symbol}^\f{} \f{Symbol}||\f{} \f{Symbol}¬\f{} \f{Symbol}®\f{} \f{Symbol}«\f{} \f{Symbol}×\f{Symbol} '
    r'h\h{-0.6}\v{0.3}-\v{-0.3}\h{0.3} \n',
    r'infty int sum < > \+- perp para <- -> <-> \* hbar \s',
]
funcs = [
    r'\exp \log \ln \sin \cos \tan',
    'exp log ln sin cos tan',
    'exp log ln sin cos tan',
    'exp log ln sin cos tan',
]
delims = [
    [(r'_{', r'}'), (r'<sub>', r'</sub>'), (r'\\s', r'\\N'), (r'_{', r'}')],
    [(r'\^{', r'}'), (r'<sup>', r'</sup>'), (r'\\S', r'\\N'), (r'\^{', r'}')]
]

patterns = [[None]*4 for _ in range(4)]

for i, j in product(range(4), range(4)):
    if i == j:
        continue

    replace_dict = {}
    all_chars = [small_greek, big_greek, special_chars]
    if (i == 0) or (j == 0):
        all_chars.append(funcs)

    for char_list in all_chars:
        replace_dict.update({re.escape(a): b for a, b in zip(char_list[i].split(), char_list[j].split())})

    patterns[i][j] = (replace_dict, re.compile('|'.join(replace_dict.keys())))   # conversion table and regexp pattern


def _replace_chars(text, src, dest):
    replacement, pattern = patterns[src][dest]

    return pattern.sub(lambda m: replacement[re.escape(m.group(0))], text)


def _replace_delims(text, src, dest):
    for delim in delims:
        text = re.sub(rf'{delim[src][0]}(.*?){delim[src][1]}',
                      rf'{delim[dest][0]}\g<1>{delim[dest][1]}',
                      text)
    return text


def _replace_agr_controls(text: str):
    return re.sub(r'\\[hvzfx]\{(\d*.?\d+.?)?\}', '', text)


def convert(text: str, old: str = 'tex', new: str = 'html', brackets: bool = True):
    t = {'latex': 0, 'tex': 0, 'html': 1,
         'agr': 2, 'plain': 3, 'str': 3}

    idx_in = t[old]
    idx_out = t[new]

    if idx_out == 1:
        text = _replace_chars(text, idx_in, idx_out)
        if brackets:
            text = _replace_delims(text, idx_in, idx_out)
    else:
        text = _replace_delims(text, idx_in, idx_out)
        text = _replace_chars(text, idx_in, idx_out)

    if idx_out in [0, 3]:
        text = text.replace(r'\^', '^')

    if idx_out == 3 and not brackets:
        text = text.replace('{', '').replace('}', '')

    if idx_in == 2:
        text = _replace_agr_controls(text)

    return text

