'''
The macros module:

* reads in macro files
* parses the macro files in the macros subfolder
* stores them in a macro expansion dictionary macrodict with macro: expansion items as  key:value items
* stores  macros generated by generatemacros.generatemacros in the macrodict dictionary
* provides the function expandmacros of type ``str -> str`` to convert a string into a different string by expanding the macros in the input string

'''

import os.path as op
import re
from typing import Dict, List, TextIO

from sastadev.conf import settings
from sastadev.generatemacros import generatemacros

idpat = r'([A-z_][A-z0-9_]*)'
eqpat = r'='
exprpat = r'"""(.*?)"""'
whitespaces = r'\s+'

macrocallpat = r'(%.+?%)'
macrocallre = re.compile(macrocallpat)

macropat = idpat + whitespaces + eqpat + whitespaces + exprpat

macrore = re.compile(macropat, re.S)


def macrostrs2dict(teststrings: List[str]) -> Dict[str, str]:
    macrodict = {}
    for tstr in teststrings:
        macromatches = macrore.finditer(tstr)
        for macromatch in macromatches:
            macroname = macromatch.group(1)
            macroexpr = macromatch.group(2)
            macrodict[macroname] = macroexpr

    return macrodict


def readmacros(macrofile: TextIO, macrodict: Dict[str, str]) -> Dict[str, str]:
    macrotext = macrofile.read()
    macromatches = macrore.finditer(macrotext)
    for macromatch in macromatches:
        macroname = macromatch.group(1)
        macroexpr = macromatch.group(2)
        if macroname in macrodict:
            settings.LOGGER.warning('Duplicate macro {} encountered. Ignored'.format(macroname))
        else:
            macrodict[macroname] = macroexpr
    return macrodict


def expandmacros(expr: str) -> str:
    result = expandmacrosdict(expr, macrodict)
    return result


def expandmacrosdict(expr: str, macrodict: Dict[str, str]) -> str:
    newexpr = expr
    thematch = macrocallre.search(newexpr)
    while thematch:
        macrocall = thematch.group(1)
        macroname = macrocall[1:-1]
        if macroname in macrodict:
            thismacrocallre = re.compile(macrocall)
            newexpr = thismacrocallre.sub(macrodict[macroname], newexpr)
            thematch = macrocallre.search(newexpr)
        else:
            settings.LOGGER.error('Unknown macro call encountered: {}.'.format(macroname))
            break
    return newexpr


macrodir = op.join(settings.SD_DIR, 'data', 'macros')
macrofilenames = [op.join(macrodir, 'sastamacros1.txt'), op.join(macrodir, 'sastamacros2.txt'), op.join(macrodir, 'newimperatives.txt')]

macrodict = generatemacros()
for macrofilename in macrofilenames:
    macrofile = open(macrofilename, 'r', encoding='utf8')
    macrodict = readmacros(macrofile, macrodict)
