import re


def parse_element_string(elements_str, stoich=False):
    """ Parse element query string with macros. Has to parse braces
    too, and throw an error if brackets are unmatched.

    e.g.
        Parameters: '[VII][Fe,Ru,Os][I]'
        Returns: ['[VII]', '[Fe,Ru,Os]', '[I]']

    e.g.2
        Parameters: '[VII]2[Fe,Ru,Os][I]'
        Returns: ['[VII]2', '[Fe,Ru,Os]', '[I]']

    Parameters:
        elements_str: str, chemical formula, including macros.

    Keyword arguments:
        stoich: bool, parse as a stoichiometry, i.e. check for numbers

    Raises:
        RuntimeError: if the composition contains unmatched brackets.

    Returns:
        list: split list of elements contained in input

    """
    valid = False
    for char in elements_str:
        if char not in ['[', ']', '{', '}', ',', ':'] and not char.isalnum():
            raise RuntimeError('Illegal character {} detected in query.'.format(char))
    valid = False
    for char in elements_str:
        if char.isupper():
            valid = True
            break
    if not valid:
        raise RuntimeError('Composition must contain at least one upper case character.')
    elements = [elem for elem in re.split(r'([A-Z][a-z]*)', elements_str) if elem]
    if stoich:
        tmp_stoich = elements
        for ind, strng in enumerate(elements):
            if not any(char.isdigit() for char in strng):
                tmp_stoich[ind] = [strng]
            else:
                tmp_stoich[ind] = [elem for elem in re.split(r'([0-9]+)', strng) if elem]
        elements = [item for sublist in tmp_stoich for item in sublist]
    # split macros
    while '[' in elements or '{' in elements or '][' in elements or '}{' in elements or ']{' in elements or '}[' in elements:
        tmp_stoich = list(elements)
        cleaned = True
        while cleaned:
            for ind, tmp in enumerate(tmp_stoich):
                if tmp == '][':
                    del tmp_stoich[ind]
                    tmp_stoich.insert(ind, '[')
                    tmp_stoich.insert(ind, ']')
                    cleaned = True
                elif tmp == '}{':
                    del tmp_stoich[ind]
                    tmp_stoich.insert(ind, '{')
                    tmp_stoich.insert(ind, '}')
                    cleaned = True
                elif tmp == ']{':
                    del tmp_stoich[ind]
                    tmp_stoich.insert(ind, '{')
                    tmp_stoich.insert(ind, ']')
                    cleaned = True
                elif tmp == '}[':
                    del tmp_stoich[ind]
                    tmp_stoich.insert(ind, '[')
                    tmp_stoich.insert(ind, '}')
                    cleaned = True
                elif ind == len(tmp_stoich) - 1:
                    cleaned = False
        for ind, tmp in enumerate(tmp_stoich):
            if tmp == '[':
                end_bracket = False
                while not end_bracket:
                    if tmp_stoich[ind + 1] == ']':
                        end_bracket = True
                    tmp_stoich[ind] += tmp_stoich[ind + 1]
                    del tmp_stoich[ind + 1]
        for ind, tmp in enumerate(tmp_stoich):
            if tmp == '{':
                end_bracket = False
                while not end_bracket:
                    if tmp_stoich[ind + 1] == '}':
                        end_bracket = True
                    tmp_stoich[ind] += tmp_stoich[ind + 1]
                    del tmp_stoich[ind + 1]
        if ']' in tmp_stoich:
            tmp_stoich.remove(']')
        if '}' in tmp_stoich:
            tmp_stoich.remove('}')
        if '' in tmp_stoich:
            tmp_stoich.remove('')

        elements = tmp_stoich

    for elem in elements:
        if '}[' in elem or ']{' in elem:
            raise RuntimeError('Unmatched brackets in query string')

    return elements