import pickle
import re
import warnings
from os import listdir
from os.path import isfile, join


# Functions
def split_semicolon(string):
    # Trying to use a rarely used character ({) for the temporal replacement
    if string.count(";"):
        # In the case where there's quotes in the text, we should ignore the exclamations inside
        if not string.count("'") and not string.count("\""):
            return string.split(";")
        else:
            # Looking for
            scan_quotation = [string[:i].count("'") for i, c in enumerate(string)]
            scan_quotation2 = [string[:i].count("\"") for i, c in enumerate(string)]
            splitted_text = [c for c in string]
            splitted_text = ["{" if c == ";" and (scan_quotation[i] % 2 == 1 or scan_quotation2[i] % 2 == 1) else c for
                             i, c in
                             enumerate(splitted_text)]
            string = "".join(splitted_text)
            return string.split(";")

    raise AssertionError("string must contain semi-colon")


def strip_call_arguments(_line):
    # Description: Given a string with a comma separated arguments,
    # extract the list of arguments getting rid of the parenthesis
    # Input: String with subroutine declaration
    # Output: List with arguments
    op = [i for i, x in enumerate(_line) if x == "("]
    cp = [i for i, x in enumerate(_line) if x == ")"]
    _line = _line[op[0] + 1:cp[-1]]
    op = [i for i, x in enumerate(_line) if x == "("]
    cp = [i for i, x in enumerate(_line) if x == ")"]
    commas = [i for i, x in enumerate(_line) if x == ","]
    splitted_line = [char for char in _line]
    for n in commas:
        if len([x for x in op if x < n]) == len([x for x in cp if x < n]):
            splitted_line[n] = "#"
    _line = "".join(splitted_line)
    return [_x.strip() for _x in _line.split("#")]


def add_val_to_argument(string, _field):
    num_arguments, intervals = find_argument_intervals(string)
    for i in [_field]:
        num_arguments, intervals = find_argument_intervals(string)
        interval = intervals[i]
        start = interval[0]
        end = interval[1]
        if string[start] == "/":
            start += 1
        if string[end - 1] == "/":
            end -= 1
        first_segment = string[:start]
        intermediate_segment = string[start:end]
        last_segment = string[end:]
        string = "%s %s%%val %s" % (first_segment, intermediate_segment, last_segment)
    return string


def in_which_block(_line, _routine, vault=None):
    # Routine to know if we are entering or exiting a routine
    # If the line corresponds to the start of a routine, returns the name of this routine
    # If the line corresponds to the end of a routine, returns None
    # If is neither one of these cases, returns the same routine.
    _line = _line.strip().lower()
    _line = remove_literals(_line)
    if _line.count("subroutine"):
        if _line.strip().lower().find("end") == 0:
            return None
        else:
            match = re.search("subroutine *(\w*) *\(*", _line, flags=re.IGNORECASE)
            block_name = match.groups(0)[0].strip()
            if vault:
                procedure = vault.get_procedure(block_name)
                return procedure
            else:
                return block_name
    elif _line.count("function"):
        if _line.find("end") == 0:
            return None
        else:
            match = re.search("function *(\w+) *\(*", _line, flags=re.IGNORECASE)
            block_name = match.groups(0)[0].strip()
            if vault:
                procedure = vault.get_procedure(block_name)
                return procedure
            else:
                return block_name
    return _routine


def literal_mask(string):

    splitted_string = [c for c in string]
    length = len(splitted_string)
    mask = []
    inside_literal = False
    marker = None
    for i in range(length):
        if splitted_string[i] == "\"":
            if not inside_literal:
                inside_literal = True
                marker = "\""
                mask.append(False)
            else:
                if marker == "\"":
                    inside_literal = False
                    marker = None
                    mask.append(False)
                elif marker == "'":
                    mask.append(True)
                else:
                    raise AssertionError("Should not happen!")
        elif splitted_string[i] == "'":
            if not inside_literal:
                inside_literal = True
                marker = "'"
                mask.append(False)
            else:
                if marker == "'":
                    inside_literal = False
                    marker = None
                    mask.append(False)
                elif marker == "\"":
                    mask.append(True)
                else:
                    raise AssertionError("Should not happen!")
        else:
            if not inside_literal:
                mask.append(False)
            else:
                mask.append(True)

    return mask


def remove_comments(string):
    # Trying to use a rarely used character ({) for the temporal replacement
    if string.count("!"):
        # In the case where there's quotes in the text, we should ignore the exclamations inside
        if not string.count("'") and not string.count('"'):
            string = string[:string.find("!")]
        else:
            mask = literal_mask(string)
            # Looking for
            splitted_string = [c for c in string]
            splitted_string = ["{" if c == "!" and mask[i] else c for i, c in enumerate(splitted_string)]
            string = "".join(splitted_string)

            if string.find("!") >= 0:
                string = string[:string.find("!")]
            string = string.replace("{", "!")

    return string


def find_argument_intervals(string, _field=None):
    # Given a line with a call to a function, give the index intervals of the different arguments.

    # Mask to avoid scanning inside strings
    mask = literal_mask(string)

    # Find all opening parenthesis
    op = [i for i, x in enumerate(string) if x == "(" and not mask[i]]
    # Find all closing parenthesis
    cp = [i for i, x in enumerate(string) if x == ")" and not mask[i]]

    if not op and not cp:
        return 0, None

    # Keep only arguments part
    arguments_text = string[op[0] + 1:cp[-1]]
    # Find remaining parenthesis
    start = op[0] + 1
    end = cp[-1]
    op = [i for i, x in enumerate(arguments_text) if x == "("]
    cp = [i for i, x in enumerate(arguments_text) if x == ")"]
    # Find commas

    splitted_line = [c for c in arguments_text]

    commas = []
    mask = literal_mask(arguments_text)
    for i, c in enumerate(splitted_line):
        # Had to add this to avoid problems with commas inside strings"
        if c == ",":
            if not mask[i]:
                commas.append(i)

    # For existing commas:
    argument_separating_commas = []
    for n in commas:
        # If comma index n is outside parenthesis (number of openings equal number of closings) substitute
        if len([x for x in op if x < n]) == len([x for x in cp if x < n]):
            splitted_line[n] = "#"
            argument_separating_commas.append(n)

    # Displacing the argument_separating_commas to match the original line
    argument_separating_commas = [_x + start + 1 for _x in argument_separating_commas]
    # Adding first and last elements
    argument_separating_commas = [start] + argument_separating_commas + [end + 1]

    # Constructing intervals
    intervals = [(argument_separating_commas[i], argument_separating_commas[i + 1] - 1) for i in
                 range(len(argument_separating_commas) - 1)]
    if _field is not None:
        if len(intervals) <= _field:
            return len(intervals), None
        else:
            return len(intervals), intervals[_field]
    else:
        return len(intervals), intervals


def is_keyword_argument(argument):
    pattern = "^[^()'\"]*="
    if re.search(pattern, argument):
        return True
    else:
        return False


def add_function_to_argument(string, field_index, _function, number_of_fields=None):
    # Given a line with a call, add a call to a function in a specific field by index.
    num_arguments, interval = find_argument_intervals(string, field_index)
    if num_arguments <= field_index:
        return string
    if number_of_fields:
        if number_of_fields is not num_arguments:
            return string
    first_segment = string[:interval[0]]
    intermediate_segment = string[interval[0]:interval[1]]
    last_segment = string[interval[1]:]

    # The arguments containing a literal are discarded (Probably a bad workaround)
    if intermediate_segment.count("'"):
        # return _line
        raise ValueError("Probably the argument should not contain quotes at this point")

    # The arguments that already contain a call to the function are discarded (Probably a bad workaround)
    if re.search("%s *\(" % _function, intermediate_segment):
        # raise ValueError("Probably the argument should not contain %s at this point" % _function)
        return string

    # If there are named arguments, add the function to the actual argument
    # (if the equal sign is inside a parenthesis, this should not be done)
    named_argument_workaround = False

    if intermediate_segment.count("="):
        if intermediate_segment.count("("):
            if intermediate_segment.find("=") < intermediate_segment.find("("):
                named_argument_workaround = True
        else:
            named_argument_workaround = True

    if named_argument_workaround:
        parts = intermediate_segment.split("=")
        intermediate_segment = "%s=%s( %s )" % (parts[0], _function, parts[1])
    else:
        intermediate_segment = "%s( %s )" % (_function, intermediate_segment)
    string = "%s%s%s" % (first_segment, intermediate_segment, last_segment)
    return string


def add_rpe_to_argument(string, field_index, number_of_fields=None):
    string = add_function_to_argument(string, field_index, "rpe", number_of_fields)
    return string


def add_rpe_var_to_arguments(string):
    num_arguments, intervals = find_argument_intervals(string)
    for i in range(num_arguments):
        num_arguments, intervals = find_argument_intervals(string)
        interval = intervals[i]
        start = interval[0]
        end = interval[1]
        if string[start] == "/":
            start += 1
        if string[end - 1] == "/":
            end -= 1
        first_segment = string[:start]
        intermediate_segment = string[start:end]
        last_segment = string[end:]
        string = "%s rpe_var( 52, %s ) %s" % (first_segment, intermediate_segment, last_segment)
    return string


def add_real_to_argument(string, field_index, number_of_fields=None):
    string = add_function_to_argument(string, field_index, "real", number_of_fields)
    return string


def find_kind_of_parameter_exception(arguments):
    if not arguments.count(","):
        type_of_exception = "regular"
    else:
        if arguments.count("=") > 1:
            type_of_exception = "multiple_variables"
        elif arguments.count("rpe_var("):
            type_of_exception = "already_fixed"
        elif arguments.count("(/"):
            type_of_exception = "array_initialization"
        else:
            raise TypeError("Unexpected kind of exception")
    return type_of_exception


def remove_brackets_content(string):
    op = [i for i, x in enumerate(string) if x == "("]
    cp = [i for i, x in enumerate(string) if x == ")"]
    _new_line = []
    for n in range(len(string)):
        # If comma index n is outside parenthesis (number of openings equal number of closings) substitute
        if len([x for x in op if x <= n]) == len([x for x in cp if x < n]):
            _new_line += [string[n]]
    return "".join(_new_line)


def separate_line_container_and_arguments(string):
    # Given a line containing a call to a write function, it returns the list of the arguments and the
    # remaining of the line without the arguments.
    pattern = "^[^\!]* +write\([^()]*\) (.*)"

    if re.search("WRITE *\(", string):
        wc = close_brackets(string[string.find("WRITE"):])

        line = string.replace(wc, "WRITE()")
    else:
        line = string
        pass
    matches = re.search(pattern, line, re.IGNORECASE)
    to_replace = matches.groups(0)[0]

    # Prepare line container
    if to_replace.strip():
        line_container = string.replace(to_replace, "###TOREPLACE###")
    else:
        line_container = string
        return line_container, []

    arguments = to_replace.strip()

    if not arguments:
        return line_container, []

    # Fix array constructors
    if arguments.find("(/") == 0:
        arguments = arguments[2:-2].strip()
        line_container = line_container.replace("###TOREPLACE###", "(/ ###TOREPLACE### /)")

    if all_is_inside_parenthesis(arguments):
        arguments = arguments[1:-1].strip()
        line_container = line_container.replace("###TOREPLACE###", "( ###TOREPLACE### )")

    arguments = split_read_arguments(arguments)
    return line_container, arguments

    # Possibly useful in the future
    # implicit_for_pattern = "WRITE(.*, *\w* *=[^,]*, *[\w0-9-+]* *\))"
    # matches = re.search(implicit_for_pattern, string, re.IGNORECASE)
    # if matches:
    #     implicit_for_part = "".join(
    #         list(reversed(close_inverse_brackets("".join(list(reversed(matches.groups()[0])))))))
    #     return []
    #
    # implicit_for_pattern_with_step = "WRITE(.*, *\w* *=[^,]*, *[\w0-9-+]* *, *[\w0-9-+]* *\))"
    # matches = re.search(implicit_for_pattern_with_step, string, re.IGNORECASE)
    # if matches:
    #     implicit_for_part = "".join(
    #         list(reversed(close_inverse_brackets("".join(list(reversed(matches.groups()[0])))))))
    #
    #     return []


def split_read_arguments(string):
    # Given a line containing a call to a write function, it returns the list of the arguments
    # pattern = "^[^\!]* +read\([^()]*\) *(.*)"
    # matches = re.search(pattern, string, re.IGNORECASE)
    arguments = string

    scan_quotation = [arguments[:i].count("'") for i, c in enumerate(arguments)]
    scan_quotation2 = [arguments[:i].count("\"") for i, c in enumerate(arguments)]
    scan_op_brackets = [arguments[:i].count("(") for i, c in enumerate(arguments)]
    scan_cl_brackets = [arguments[:i].count(")") for i, c in enumerate(arguments)]
    scan_brackets = [i - j for i, j in zip(scan_op_brackets, scan_cl_brackets)]
    splitted_text = [c for c in arguments]
    splitted_text = [
        "#" if c == "," and (scan_quotation[i] % 2 == 0 and scan_quotation2[i] % 2 == 0) and not scan_brackets[i] else c
        for i, c in enumerate(splitted_text)]
    arguments = "".join(splitted_text)
    arguments = [a for a in arguments.split("#") if a.strip()]
    return arguments


def resulting_type(type_a, type_b):
    types = ["rpe_var", "complex", "real", "integer", "char", "logical"]
    for t in types:
        if type_a == t:
            return t
        if type_b == t:
            return t


def combine_types(list_of_types):
    to_return = list_of_types[0]
    for t in list_of_types:
        to_return = resulting_type(to_return, t)
    return to_return


def calls_to_routines(string):
    pattern = "[ \t]call +"
    matches = [(m.start(0), m.end(0)) for m in re.finditer(pattern, string, flags=re.I)]
    if matches:
        pieces = [string[start:].strip() for start, end in matches]
        return [close_brackets(piece) for piece in pieces if piece.count("(")]
    else:
        return None


def remove_literals(string):
    mask = literal_mask(string)
    modified_string = "".join(
        [c if not mask[i] else " " for i, c in enumerate(string)])
    return modified_string


def calls_to_functions(string, vault=None):
    from .Classes import Function, Interface
    to_return = calls_to_procedure(string, procedure_type=Function, vault=vault)
    if not to_return:
        to_return = calls_to_procedure(string, procedure_type=Interface, vault=vault)
    return to_return


def calls_to_procedure(string, procedure_type, vault=None):
    from .Classes import Function
    # line without literals to avoid parenthesis inside strings and other ugly stuff
    no_literal_line = remove_literals(string)
    to_return = []
    if re.search(".*function .*", string, re.IGNORECASE):
        return to_return
    if re.search(".*subroutine.*", string, re.IGNORECASE):
        return to_return
    pattern = "(\w+) *\("
    matches = [m for m in re.finditer(pattern, no_literal_line, flags=re.I)]
    if matches:
        if vault:
            for m in matches:
                fun_name = m.groups(0)[0]
                fun = vault.get_procedure(fun_name, procedure_type=procedure_type)
                # TODO: Think a way to avoid having nf90 exceptions inside routines
                if fun or (procedure_type == Function and fun_name.lower().find("nf90_") == 0):
                    function_call = close_brackets(string[m.start(1):])
                    to_return.append(function_call)
        else:
            for m in matches:
                function_call = close_brackets(string[m.start(1):])
                to_return.append(function_call)

    return to_return


def find_called_routine(string):
    pattern = "^[^\!']*[\t ]*call +([^\(]*)"
    match = re.search(pattern, string, flags=re.I)
    if match:
        return match.groups()[0].strip().lower()
    else:
        return None


def find_called_function(string):
    return string[:string.find("(")].strip()


def find_call_arguments(string):
    n_arg, ints = find_argument_intervals(string)
    if n_arg == 0:
        return None
    if ints:
        arguments = [string[interval[0]:interval[1]].strip() for interval in ints]
        return [x for x in arguments if x]
    else:
        return None


def all_is_inside_parenthesis(string):
    if string.strip()[0] == "(":
        try:
            if "c%s" % string.strip() == close_brackets("c%s" % string.strip()):
                return True
            else:
                return False
        except ValueError:
            return False
    else:
        return False


def clean_contents(contents, vault=None):
    if vault:
        intrinsics = vault.intrinsics
    else:
        intrinsics = ["reshape", "min", "max", "sum", "log", "maxval", "minval", "abs", "size", "nint"]

    if re.match("\(/(.*)/\)", contents):
        contents = re.match("\(/(.*)/\)", contents).groups(1)[0]

    if contents[0] == "(":
        if "dummy%s" % contents == close_brackets("dummy%s" % contents):
            contents = contents[1:-1]

    if re.match("^([^\()]*) *\)+ *$", contents):
        contents = re.match("^([^\()]*) *\)+ *$", contents).groups(1)[0]

    if re.match(".*\) *\(.*\)", contents):
        match = re.match("(.*\)) *\(.*\)", contents)
        contents = match.groups(0)[0]

    pattern = "(\w+) *\("
    m = [_m for _m in re.finditer(pattern, contents)]
    if m:
        for mat in reversed(m):
            prefix = contents[:mat.start(1)]
            part_to_modify = contents[mat.start(1):]
            to_replace = close_brackets(part_to_modify)
            replacement = to_replace[:to_replace.find("(")]
            if replacement in intrinsics and is_call_to_function(contents):
                continue
            contents = "%s%s" % (prefix, part_to_modify.replace(to_replace, replacement))
    if contents.count("(/"):
        # Is temporal array
        contents = contents.replace("(/", "")
        contents = contents.replace("/)", "")
    return contents


def is_integer_with_precision(string):
    pattern = "^[0-9]*_\w+$"
    if re.search(pattern, string):
        return True
    else:
        return False


def is_call_to_function(contents):
    pattern = "\w+ *\("
    m = re.match(pattern, contents)
    if m:
        if contents == close_brackets(contents):
            arguments = find_call_arguments(contents)
            # This thing above is only a bad workaround that can work to identify an array instead of a function.
            # Not general enough TODO: Make it general.
            for argument in arguments:
                if argument == ":":
                    return False
            return True
    return False


def type_of_contents(contents, routine=None, vault=None):
    # TODO: Simplify this function
    # I should do a first filter at this point distinguishing if its a string, a simple variable ("\w+")
    # if its a function/array (is_call_to_function is True), is a keyword argument, or other exceptions
    if not vault:
        vault = load_vault()

    # if all_is_inside_parenthesis(contents):
    #     contents = contents.strip()[1:-1]

    # Case with call to a function with array arguments after
    match = re.search("(\w* *\([^=]*\)) *(\(.*\))", contents)
    if match:
        contents = match.group(1)

    if contents.strip()[0] in ["'", '"']:
        return "char"

    comparison_operators = ["<", "==", ">"]
    for comparison_operator in comparison_operators:
        if contents.count(comparison_operator):
            if len(split_elements_in_comparison(contents)) > 1:
                return "logical"

    contents = contents.replace(" ", "").lower().strip()

    if is_call_to_function(contents):
        fun_name = find_called_function(contents)
        if fun_name in vault.intrinsics:
            if fun_name in ["reshape", "min", "max", "sum", "log", "maxval", "minval", "abs", "sqrt"]:
                return type_of_contents(find_call_arguments(contents)[0], routine=routine, vault=vault)
            elif fun_name in ["int", "nint", "count", "size", "mod", "umask", "len", "len_trim"]:
                return "integer"
            elif fun_name in ["real", "float"]:
                return "real"
            elif fun_name in ["cmplx"]:
                return "complex"
            elif fun_name in ["trim", "ctime", "adjustl"]:
                return "char"
            elif fun_name in ["null"]:
                return "pointer"

            else:
                raise ValueError("This intrinsic should be considered too: %s" % fun_name)
        else:
            from .Classes import Interface
            fun = vault.get_function(fun_name)
            if fun:
                return fun.type
            fun = vault.get_procedure(fun_name, Interface)
            if fun:
                return fun.procedures[0].type

            # warnings.warn("If its a function should be on the library or an intrinsic, isn't it? %s|nLooking if its a variable... " % contents)
            # return type_of_contents(fun_name, routine=routine, vault=vault)

    if is_keyword_argument(contents):
        # Removing before first =
        contents = contents[contents.find("=") + 1:].strip()
        return type_of_contents(contents, routine=routine, vault=vault)

    contents = clean_contents(contents, vault)

    if has_operations(contents):
        split_elements = split_elements_in_operations(contents)
        types = [type_of_contents(element, routine, vault) for element in split_elements]
        return combine_types(types)
    else:
        if re.search("^[0-9\.]*e$", contents):
            return "real"

        if contents.find("nf90_") == 0:
            return "integer"
        if contents.find("set_nf90_") == 0:
            return "integer"

        if contents.count("'") or contents.count("\""):
            return "char"
        if contents.count(","):
            # If there are commas at this point, it must correspond with a temporal array
            # Array elements should have the same type, so we will assume that all the elements
            # have the same type than the first element
            first_element = split_attributes(contents)[0]
            return type_of_contents(first_element, vault=vault)
        if contents.count(","):
            raise ValueError("Argument should not contain commas at this point %s" % contents)

        if contents.count("(") or contents.count(")"):
            warnings.warn("WARNING: Argument should not contain brackets at this point % s" % contents)
            contents = contents.replace("(", "")
            contents = contents.replace(")", "")

        if contents.count(".false.") or contents.count(".true."):
            return "logical"
        if contents.count("."):
            return "real"
        if contents.isdigit():
            return "integer"
        if is_integer_with_precision(contents):
            return "integer"

        if vault:
            if re.search("^\w+$", contents):
                coincidences = [v for v in vault.get_variable(contents)]
                if len(coincidences) == 1:
                    return coincidences[0].type
                elif len(coincidences) > 1:
                    ctypes = [v.type for v in coincidences]
                    if elements_are_equal(ctypes):
                        return ctypes[0]

            if routine:
                from .Classes import Routine, ModuleMain, Function
                if isinstance(routine, Routine) or isinstance(routine, ModuleMain) or isinstance(routine, Function):
                    procedure = routine
                else:
                    procedure = vault.get_procedure(routine)

                for variable in procedure.variables:
                    if contents == variable.name.lower():
                        return variable.type
                for variable in procedure.accessible_variables():
                    if contents == variable.name.lower():
                        return variable.type

                # Try if there's a function with this name
                fun = vault.get_function(contents)
                if fun:
                    return fun.type

        if contents.count("%"):
            return type_of_derived_type_element(contents, routine=routine, vault=vault)

        if contents.count(">") or contents.count("<") or contents.count("=="):
            return "logical"

        if vault:
            if contents.count("mpi_"):
                return "integer"
            elif contents == "rpe":
                return "rpe_var"
            else:
                warnings.warn("Not able to find type! :(  |%s|" % contents)
        else:
            pass

        warnings.warn("Did not find type of: %s" % contents)
        # raise AssertionError("Did not find type of: %s" % contents)


def retrieve_variable(contents, routine=None, vault=None):
    # TODO: Simplify this function
    # I should do a first filter at this point distinguishing if its a string, a simple variable ("\w+")
    # if its a function/array (is_call_to_function is True), is a keyword argument, or other exceptions
    if not vault:
        vault = load_vault()

    contents = contents.replace(" ", "").lower().strip()

    contents = clean_contents(contents, vault)

    if is_keyword_argument(contents):
        # Removing before first =
        contents = contents[contents.find("=") + 1:].strip()
        return retrieve_variable(contents, routine=routine, vault=vault)

    if re.search("^\w+$", contents):
        coincidences = [v for v in vault.get_variable(contents)]
        if len(coincidences) == 1:
            return coincidences[0]

    if routine:
        from .Classes import Routine, ModuleMain, Function
        if isinstance(routine, Routine) or isinstance(routine, ModuleMain) or isinstance(routine, Function):
            procedure = routine
        else:
            procedure = vault.get_procedure(routine)

        for variable in procedure.variables:
            if contents == variable.name.lower():
                return variable
        for variable in procedure.accessible_variables():
            if contents == variable.name.lower():
                return variable

    if contents.count("%"):
        contents = contents.split("%")[0]
        return retrieve_variable(contents, routine=routine, vault=vault)

    raise AssertionError("Did not find this variable: %s" % contents)


def type_of_derived_type_element(contents, routine=None, derived_type=None, vault=None):
    if not vault:
        vault = load_vault()

    struct = contents[:contents.find("%")]
    var = contents[contents.find("%") + 1:]
    if var == "val":
        return "real"

    if not derived_type:
        derived_type = type_of_contents(struct, routine=routine, vault=vault)

    if var.count("%"):

        struct = var[:var.find("%")]
        derived_type = type_of_derived_type_element(struct, routine=routine, derived_type=derived_type, vault=vault)
        var = var[var.find("%") + 1:]
        if var == "val":
            return "real"
        dt = vault.get_derived_type(derived_type)
        try:
            v = dt.get_variable(var)
        except AttributeError:
            pass

        try:
            return v.type
        except AttributeError:
            pass
    else:
        if vault:
            dt = vault.get_derived_type(derived_type)
            try:
                return dt.get_variable(var).type
            except AttributeError:
                pass

        return "unknown"


def load_source_files(path):
    from .Classes import ReadSourceFile
    filelist = [ReadSourceFile(join(path, filename)) for filename in listdir(path) if filename.count(".f90")]
    filedict = {}
    for f in filelist:
        filedict[f.filename] = f
    return filedict


def load_vault(filename="vault.pkl"):
    try:
        return pickle.load(open(filename, "rb"))
    except IOError:
        return None


def load_intrinsics():
    # Hardcoded path to file.
    with open("AdditionalFiles/fortran_intrinsics") as f:
        return [re.search("(\w+)", l).groups(0)[0].lower() for l in f]


def load_list_of_files_to_keep_unmodified():
    # Hardcoded path to file.
    filename = "AdditionalFiles/list_of_files_to_keep_unmodified.txt"
    if isfile(filename):
        print("Loading list of exceptions from %s\n\n" % filename )
        with open("AdditionalFiles/list_of_files_to_keep_unmodified.txt") as f:
            return [l.strip() for l in f if l.strip() and l.strip()[0] != "#"]
    else:
        return None


def close_brackets(string):
    started = False
    bracket_counter = 0
    mask = literal_mask(string)

    for _i, _c in enumerate(string):
        if _c == "(" and not mask[_i]:
            bracket_counter += 1
            if not started:
                started = True
        elif _c == ")" and not mask[_i]:
            bracket_counter -= 1
            if not started:
                raise ValueError("This should not happen, check arguments %s" % string)
        if bracket_counter < 0:
            raise ValueError("This should not happen, check arguments %s" % string)
        if started and bracket_counter == 0:
            return string[:_i + 1]

    if string.count("(") + string.count(")") == 0:
        return string
    raise ValueError("This should not happen, check arguments")


def elements_are_equal(iterator):
    iterator = iter(iterator)
    try:
        first = next(iterator)
    except StopIteration:
        return True
    return all(first == rest for rest in iterator)


def split_declaration(declaration):
    # Description: Given a declaration line, this function gives back the attributes and the declared arguments
    # Input:
    #       -declaration: string corresponding to a variable declaration
    # Output:
    #       _attributes: string corresponding to the declaration attributes
    #       _arguments: string containing the declaration arguments
    _attributes, _arguments = declaration.split("::")
    _attributes = _attributes.strip()
    _arguments = split_declaration_arguments(_arguments)
    return _attributes, _arguments


def split_attributes(attributes):
    # Description: Given a string with declaration attributes separates the different attributes and
    # gives back a list of attributes
    # Input:
    #       -attributes: string with attributes
    # Output:
    #       -attributes_list: list containg strings with the different attributes
    attributes_list = replace_comma_outside_parenthesis_by_hashtag(attributes)
    attributes_list = [l.strip().replace("#", ",") for l in attributes_list.split(",")]
    return attributes_list


def which_type(attributes):
    # Description: Given a list of attributes, this function returns the type of the variables
    # Input: List of attributes
    # Output: String with type
    if isinstance(attributes, str):
        attributes = split_attributes(attributes)
    assert isinstance(attributes, list)
    possible_types = ["real", "integer", "type", "char", "logical", "complex", "double precision", "useintrinsic",
                      "public"]
    t0 = attributes[0]
    var_type = None
    for ty in possible_types:
        if t0.lower().find(ty) == 0:
            var_type = ty
    if not var_type:
        return None
    if var_type == "type":
        # It may be the case in which it is a type declaration instead of a variable declaration of a type
        # This workaround is to discard this cases.
        if not t0.count("("):
            return None
        var_type = t0[t0.find("(") + 1:-1].strip()
    if var_type == "public":
        return None

    return var_type.lower()


def which_dimension(attributes):
    # Description: Given a list of attributes, this function returns the dimension of the variables
    # Input: List of attributes
    # Output: Integer with the number of dimensions
    if isinstance(attributes, str):
        attributes = split_attributes(attributes)
    elif isinstance(attributes, list):
        pass
    else:
        raise AssertionError("Not a string nor a list, what it is?")
    dim = 0
    for att in attributes:
        if att.lower().count("dimension"):
            dim = att
            break
    if dim:
        dim = 1 + dim.count(",")
    return dim


def which_intent(attributes):
    # Description: Given a list of attributes, this function returns the intent of the variables
    # Input: List of attributes
    # Output: String with the intent of the variable, if intent is not specified the default is inout
    if isinstance(attributes, str):
        attributes = split_attributes(attributes)
    elif isinstance(attributes, list):
        pass
    intent = "inout"
    for att in attributes:
        if att.lower().count("intent"):
            intent = att
            break
    if intent.count("("):
        intent = intent[intent.find("(") + 1:-1].strip().lower()
    return intent


def is_optional(attributes):
    # Description: Given a list of attributes, this function returns a boolean describing if the variable is optional
    # Input: List of attributes
    # Output: Boolean about optionality of variable
    attributes = split_attributes(attributes)
    optional = False
    for att in attributes:
        if att.lower().count("optional"):
            optional = True
            break
    return optional


def is_parameter(attributes):
    # Description: Given a list of attributes, this function returns a boolean describing if the variable is optional
    # Input: List of attributes
    # Output: Boolean about optionality of variable
    attributes = split_attributes(attributes)
    for att in attributes:
        if att.lower().count("parameter"):
            return True
    return False


def is_allocatable(attributes):
    # Description: Given a list of attributes, this function returns a boolean describing if the variable is allocatable
    # Input: List of attributes
    # Output: Boolean about if variable is allocatable
    attributes = split_attributes(attributes)
    for att in attributes:
        if att.lower().count("allocatable"):
            return True
    return False


def parse_attributes(attributes):
    if isinstance(attributes, str):
        attributes = split_attributes(attributes)
    assert isinstance(attributes, list)
    var_type = which_type(attributes)
    dimension = which_dimension(attributes)
    intent = which_intent(attributes)
    optional = is_optional(attributes)
    allocatable = is_allocatable(attributes)
    parameter = is_parameter(attributes)
    return var_type, dimension, intent, optional, allocatable, parameter


def split_declaration_arguments(string):
    # Description: given a string containing arguments, return a list with arguments
    # Input: String with arguments
    # Output: List with arguments
    # TODO: If the string of arguments contains parenthesis, we should keep information about the dimensions.
    # TODO: If the argument has a value declaration, we should get rid of this.

    string = replace_comma_outside_parenthesis_by_hashtag(string)
    string = [l.strip().replace("#", ",") for l in string.split(",")]
    string = [el[:el.find("(")] if el.count("(") else el for el in string]
    string = [el[:el.find("=")].strip() if el.count("=") else el for el in string]
    return string


def replace_comma_outside_parenthesis_by_hashtag(string):
    op = [i for i, x in enumerate(string) if x == "("]
    cp = [i for i, x in enumerate(string) if x == ")"]
    line = [c for c in string]
    for n in range(len(string)):
        # If comma index n is outside parenthesis (number of openings equal number of closings) substitute
        if string[n] == ",":
            if len([x for x in op if x <= n]) != len([x for x in cp if x < n]):
                line[n] = "#"
    string = "".join(line)
    return string


def split_elements_in_operations(string):
    operations = ["+", "-", "*", "/"]
    return split_elements_by_symbols(string, operations)


def split_elements_in_comparison(string):
    comparison_operators = [">", "<", "=="]
    return split_elements_by_symbols(string,comparison_operators)


def split_elements_by_symbols(string, symbols):

    functions = calls_to_functions(string)
    dictionary_of_replacements = {}
    if functions:
        for index, function in enumerate(functions):
            dictionary_of_replacements["REPLACEMENT%i" % index] = function
            string = string.replace(function, "REPLACEMENT%i" % index)

    listed_text = [c for c in string]
    parenthesis_counter = 0
    for index, c in enumerate(listed_text):
        if c == "(":
            parenthesis_counter += 1
        elif c == ")":
            parenthesis_counter -= 1
        elif c in symbols and parenthesis_counter == 0:
            listed_text[index] = "#"
    string = "".join(listed_text)
    arguments = [x.strip() for x in string.split("#") if x.strip()]
    for index, argument in enumerate(arguments):
        for key, function in dictionary_of_replacements.items():
            if key in argument:
                arguments[index] = arguments[index].replace(key, function)

    return arguments


def has_operations(string):
    operations = ["+", "-", "*", "/"]
    if not string.strip():
        return False
    if string.count("'") or string.count('"'):
        return False
    # TODO: Does it break something else?
    # if string.count("-"):
    #     if string.count("-") == string.count("e-"):
    #         return False
    for op in operations:
        if op in string:
            return True
    return False


def match_arguments_and_interface(arguments, interface, current_block=None, vault=None):
    number_of_arguments = len(arguments)
    argument_types = [type_of_contents(argument, routine=current_block, vault=vault) for argument in arguments]
    keyword_arguments = [_x.split("=")[0].strip() if is_keyword_argument(_x) else False for _x in arguments]
    for procedure in interface.procedures:
        dummy_arguments = procedure.get_dummy_arguments()
        mandatory_arguments = [argument for argument in dummy_arguments if not argument.is_optional]
        if len(dummy_arguments) < number_of_arguments:
            continue
        if len(mandatory_arguments) > number_of_arguments:
            continue
        match = True
        for index, argument_type in enumerate(argument_types):
            keyword = keyword_arguments[index]
            if keyword:
                var = procedure.get_variable_by_name(keyword)
                if var:
                    dummy_type = var.type
                else:
                    match = False
            else:
                dummy_type = procedure.get_variable_by_position(index).type
            if dummy_type != argument_type:
                if (dummy_type == "real" and argument_type == "rpe_var") or (
                        dummy_type == "rpe_var" and argument_type == "real"):
                    pass
                else:
                    match = False
        if match:
            return procedure
    raise AssertionError("No matching routine found for interface : %s" % interface.name)


def function_type_from_header(header):
    pattern = "(.*)function"
    match = re.search(pattern, header, re.IGNORECASE)
    fun_type = remove_brackets_content(match.groups(0)[0].strip().lower())
    if fun_type:
        return True, fun_type
    else:
        if header.count("result"):
            pattern = "result *\((.*)\)"
            match = re.search(pattern, header, re.IGNORECASE)
            if match:
                return False, remove_brackets_content(match.groups(0)[0].strip().lower())
            else:
                raise ValueError("Not finding function result")

        else:
            pattern = "function (.*)\("
            match = re.search(pattern, header, re.IGNORECASE)
            if match:
                return False, remove_brackets_content(match.groups(0)[0].strip().lower())
            else:
                raise ValueError("Not finding function result")


def split_vars_by_allocatable(variables):
    allocatables = [var for var in variables if var.type == "rpe_var" and var.allocatable]
    non_allocatables = [var for var in variables if var.type == "rpe_var" and not var.allocatable]
    return allocatables, non_allocatables


def recover_routine_text(variable, file_objects):
    routine = variable.routine
    module = routine.module

    f = file_objects["%s.f90" % module.name]
    to_return = []
    lines = f.lines
    if routine.name == "main":
        for line in lines[:]:
            if line.lower().count("contains"):
                break
            else:
                to_return.append(line)
        to_return.append(lines[-1])
    else:
        printing = False
        for line in lines:
            if not printing:
                if line.lower().count("subroutine"):
                    called_routine = re.search("subroutine *(\w*)", line, re.IGNORECASE).group(1)
                    if called_routine.lower() == routine.name.lower():
                        to_return.append(line)
                        printing = True
            else:
                to_return.append(line)
                if line.lower().count("end subroutine"):
                    break

    return to_return


def is_allocated_inside_declaration_scope(var, file_objects):
    text = recover_routine_text(var, file_objects)
    interesting_lines = [line for line in text if
                         re.search("allocate *\(.*[^\w]%s[^\w]" % var.name, line, re.IGNORECASE)]
    if interesting_lines:
        return True
    else:
        return False


def split_by_function(variables, fun, files=None):
    pattern = "[^\w]%s[^\w]"

    vars_true = []
    vars_false = []

    for var in variables:
        appears_in = find_var_elsewhere(var, files)
        found = False
        for f in appears_in:
            appears_in_lines = [line for line in f.lines if
                                re.search(pattern % var.name, line) and not line.count("::")]
            found_array = [True if fun(line) else False for line in appears_in_lines]
            if any(found_array):
                found = True
        if found:
            vars_true.append(var)
        else:
            vars_false.append(var)
    return vars_true, vars_false


def is_namelist_line(line):
    return line.lower().strip().find("namelist") == 0


def split_by_appearance_in_namelist(variables, files=None):
    return split_by_function(variables, is_namelist_line, files=files)


def split_vars_by_main_or_routine(variables):
    in_main = [var for var in variables if var.routine.name == "main"]
    not_in_main = [var for var in variables if var.routine.name != "main"]

    return in_main, not_in_main


def replace_workalloc_in_file(path_to_input_sources, path_to_output_sources, filename):
    # This function loads a file and applies modifications to the file.
    # TODO: Find an automatic way of selecting routines to blacklist based on calls to external routines
    # Files to ignore
    from .Classes import ImplementRPE
    f = ImplementRPE(join(path_to_input_sources, filename), None)
    f.pre_process(replace_reals=False, replace_wrk_alloc=True)
    f.save_modified_file(join(path_to_output_sources, filename))


def preprocess_file(path_to_input_sources, path_to_output_sources, filename):
    # This function loads a file and applies modifications to the file.
    # TODO: Find an automatic way of selecting routines to blacklist based on calls to external routines
    # Files to ignore
    from .Classes import ImplementRPE
    f = ImplementRPE(join(path_to_input_sources, filename), None)
    f.pre_process(replace_wrk_alloc=False)
    f.save_modified_file(join(path_to_output_sources, filename))


def postprocess_file(path_to_input_sources, path_to_output_sources, filename, vault):
    # This function loads a file and applies modifications to the file.
    # TODO: Find an automatic way of selecting routines to blacklist based on calls to external routines
    # Files to ignore
    from .Classes import ImplementRPE
    f = ImplementRPE(join(path_to_input_sources, filename), vault)
    f.post_process()
    f.save_modified_file(join(path_to_output_sources, filename))


def find_var_elsewhere(variable, file_objects):
    pattern = "[^\w]%s[^\w]"

    files = ["%s.f90" % module.name for module in variable.routine.module.used_by]
    files.append("%s.f90" % variable.routine.module.name)
    files_to_search = []
    for f in file_objects:
        if f in files:
            files_to_search.append(file_objects[f])
        if len(files_to_search) == files:
            break

    return [f for f in files_to_search if re.search(pattern % variable.name, f.text)]


def find_file_and_line_of_allocation(variable, file_objects):
    if is_allocated_inside_declaration_scope(variable, file_objects):
        f = file_objects["%s.f90" % variable.routine.module.name]
        for line_index, line in enumerate(f.lines):
            if re.search("allocate *\(.*[^\w]%s[^\w]" % variable.name, line, re.IGNORECASE):
                return f, line_index
        raise ValueError("Allocation of variable %s not found" % variable.name)
    else:
        files_where_var_appears = find_var_elsewhere(variable, file_objects)
        for f in files_where_var_appears:
            for line_index, line in enumerate(f.lines):
                if re.search("allocate *\(.*[^\w]%s[^\w]" % variable.name, line, re.IGNORECASE):
                    return f, line_index
        warnings.warn("Allocation of variable %s not found" % variable.name)
        return None, None


def find_line_in_sourcefile(variable,sourcefile):
    pattern = "namelist.*[^\w]%s[^\w]" % variable.name
    namelist_name = None
    for line in sourcefile.lines:
        if re.search(pattern, line, re.IGNORECASE):
            pattern2 = "namelist/(.*)/.*%s[^\w]" % variable.name
            match = re.search(pattern2, line, re.IGNORECASE)
            namelist_name = match.group(1)
            break
    if namelist_name:
        for index, line in enumerate(sourcefile.lines):
            pattern3 = "read *\(.*[^\w]%s[^\w]" % namelist_name
            if re.search(pattern3, line, re.IGNORECASE):
                return sourcefile, index
    return None, None


def find_file_and_line_of_namelist_reading(variable, file_objects):
    routine = variable.routine
    module = routine.module
    sourcefile = file_objects["%s.f90" % module.name]

    sourcefile, index = find_line_in_sourcefile(variable, sourcefile)

    if sourcefile:
        return sourcefile, index

    other_sources = find_var_elsewhere(variable, file_objects)
    for sourcefile in other_sources:
        sourcefile, index = find_line_in_sourcefile(variable, sourcefile)
        if sourcefile:
            return sourcefile, index

    return None, None


def find_first_use(variable, file_objects):
    routine = variable.routine
    module = routine.module

    sourcefile = file_objects["%s.f90" % module.name]

    pattern = "[^\w]%s[^\w]" % variable.name
    searching = False
    loop_level = 0
    loop_start = None
    in_where_statement = False
    where_statement_start = None
    for line_index, line in enumerate(sourcefile.lines):
        line = line.strip().lower()
        if not searching:
            if line.lower().count("subroutine"):
                called_routine = re.search("subroutine *(\w*)", line, re.IGNORECASE).group(1)
                if called_routine.lower() == routine.name.lower():
                    searching = True
        else:
            if re.search(pattern, line, re.IGNORECASE):
                if line.count("::"):
                    continue
                elif line.count("subroutine"):
                    if line.count("end subroutine"):
                        break
                    continue
                elif line.find("use ") == 0:
                    continue
                elif line.find("data ") == 0:
                    continue
                else:
                    if in_where_statement:
                        return sourcefile, where_statement_start
                    else:
                        if loop_level > 0:
                            return sourcefile, loop_start
                        else:
                            return sourcefile, line_index
            elif line.count("end subroutine"):
                break
            elif re.search("^ *do *[\w]* *=", line, re.IGNORECASE):
                if loop_level == 0:
                    loop_start = line_index
                loop_level += 1
            elif re.search("^ *end +do", line, re.IGNORECASE):
                loop_level -= 1
            elif line.find("where") == 0:
                in_where_statement = True
                where_statement_start = line_index
            elif line.find("end where") == 0:
                in_where_statement = False
    return None, None


def insert_variable_precision_specification(variable, sourcefile, insertion_line):
    new_line = "    %s%%sbits = emulator_variable_precisions(%i)" % (variable.name, variable.id)
    sourcefile.lines.insert(insertion_line, new_line)
    sourcefile.rebuild_text()


def insert_variable_truncation(variable, sourcefile, insertion_line):
    new_line = "    CALL apply_truncation(%s)" % variable.name
    sourcefile.lines.insert(insertion_line, new_line)
    sourcefile.rebuild_text()


def insert_variable_precision_specification_to_allocatable(variable, file_objects, counter_object):
    sourcefile, allocation_line = find_file_and_line_of_allocation(variable, file_objects)
    if sourcefile:
        variable.id = counter_object.count
        counter_object.up()
        insert_variable_precision_specification(variable, sourcefile, allocation_line + 1)


def insert_variable_precision_specification_to_namelist_parameter(variable, file_objects, counter_object):
    sourcefile, reading_line = find_file_and_line_of_namelist_reading(variable, file_objects)
    if sourcefile:
        variable.id = counter_object.count
        counter_object.up()
        insert_variable_precision_specification(variable, sourcefile, reading_line + 1)
    else:
        warnings.warn("Variable %s not found" % variable.name)


def insert_variable_precision_to_routine_variable(variable, file_objects, counter_object):
    sourcefile, first_use_line = find_first_use(variable, file_objects)
    if sourcefile:
        variable.id = counter_object.count
        counter_object.up()
        insert_variable_precision_specification(variable, sourcefile, first_use_line)
        insert_variable_truncation(variable, sourcefile, first_use_line + 1)
    else:
        warnings.warn("Variable %s not found" % variable.name)


def add_after_call_routines(source_file, vault, counter):
    try:
        current_block = vault[source_file.filename.replace(".f90", "")].main()
    except AttributeError:
        warnings.warn("No module %s" % source_file.filename)
        return

    for _index, _line in enumerate(source_file.lines):
        # Find routine calls in line
        routine_calls = calls_to_routines(_line)
        variables_to_truncate = []
        if routine_calls is None:
            # If not, look if the code is entering or exiting a routine block
            current_block = in_which_block(_line, current_block, vault=vault)
        else:
            # If there's some call look at each one of them
            for call in routine_calls:
                is_external = False
                called_routine = find_called_routine(call)
                # If the routine is apply_truncation, continue
                if called_routine == "apply_truncation":
                    continue
                called_arguments = find_call_arguments(call)
                if not called_arguments:
                    # If there's no argument, continue
                    continue

                # Find the procedure called (object)
                procedure = vault.get_procedure(called_routine)

                # Check that we properly found the routine in the database
                from .Classes import Routine, Interface
                if isinstance(procedure, Routine):
                    pass
                elif isinstance(procedure, Interface):
                    procedure = match_arguments_and_interface(called_arguments, interface=procedure,
                                                              current_block=current_block, vault=vault)
                else:
                    warnings.warn("Routine not found in vault database %s" % called_routine)
                    is_external = True

                # If the routine is in the database
                if not is_external:
                    # Check each argument
                    for argument_index, argument in enumerate(called_arguments):
                        if argument.count("tmp_var_"):
                            # Skipping temporal variables
                            continue
                        # Look if argument is keyword argument
                        if is_keyword_argument(argument):
                            # In case it is a keyword argument, get the dummy argument using the name.
                            keyword = argument.split("=")[0].strip()
                            dummy_argument = procedure.get_variable_by_name(keyword)
                            if not dummy_argument:
                                raise AssertionError("Keyword variable not found: %s" % keyword)

                            if dummy_argument.type != "rpe_var":
                                # If the argument is not an rpe_var because it's passing the %val element of the struct
                                # it should be truncated anyway.
                                if dummy_argument.type == "real" and argument.count("%val"):
                                    variables_to_truncate.append(argument.replace("%val", "").strip())
                            elif dummy_argument.intent == "out" or dummy_argument.intent == "inout":
                                variables_to_truncate.append(argument)
                        else:
                            dummy_argument = procedure.get_variable_by_position(argument_index)
                            if not dummy_argument:
                                raise AssertionError("Variable not found in position: %i" % argument_index)
                            if dummy_argument.type != "rpe_var":
                                if dummy_argument.type == "real" and argument.count("%val"):
                                    variables_to_truncate.append(argument.replace("%val", "").strip())
                            elif dummy_argument.intent == "out" or dummy_argument.intent == "inout":
                                variables_to_truncate.append(argument)
                else:
                    # If the routine is external
                    for argument_index, argument in enumerate(called_arguments):
                        # TODO: Probably should find a better solution, the code should not identify an array as a
                        # Todo: ... function even if the name coincides.
                        argument_type = type_of_contents(argument, routine=current_block, vault=vault)
                        if argument_type == "rpe_var":
                            variables_to_truncate.append(argument)
                        elif argument_type == "real" and argument.count("%val"):
                            variables_to_truncate.append(argument.replace("%val", "").strip())
        i = _index
        for variable in variables_to_truncate:
            i = insert_in_line(variable, source_file, current_block, counter, i, vault=vault)


def insert_in_line(var, source_file, routine, counter, line_index, vault=None):
    inner_index = line_index
    var_object = retrieve_variable(var, routine=routine, vault=vault)
    # Need to solve parameter issue here
    if var_object.is_parameter:
        return inner_index
    elif var_object.intent == "in":
        return inner_index

    sbits_assignation = "    %s%%sbits = emulator_variable_precisions(%i)"
    apply_truncation = "    CALL apply_truncation(%s) "

    if var_object.id is None:
        var_object.id = counter.count
        counter.up()

    source_file.lines.insert(inner_index + 1, sbits_assignation % (var, var_object.id))
    source_file.lines.insert(inner_index + 2, apply_truncation % var)

    return inner_index + 2


def temporal_variable_declaration_line(variable_name, dimension):
    assert isinstance(dimension, int)
    if dimension == 0:
        return "type(rpe_var) :: %s" % (variable_name)
    elif dimension == 1:
        addition = ""
        return "type(rpe_var), dimension(:%s), allocatable :: %s" % (addition, variable_name)
    elif dimension < 0:
        raise AssertionError("Dimension can not be negative!")
    else:
        addition = ",:" * (dimension - 1)
        return "type(rpe_var), dimension(:%s), allocatable :: %s" % (addition, variable_name)


def temporal_variable_allocation_line(condition, temporal_variable_name, original_variable_name, dimension):

    assert isinstance(dimension, int)
    if dimension == 0:
        return ""
    elif dimension == 1:
        return condition + " allocate(%s( size(%s) ) )" % (temporal_variable_name, original_variable_name)
    elif dimension < 0:
        raise AssertionError("Dimension can not be negative!")
    else:
        addition = "size(%s,1)" % original_variable_name
        for i in range(2, dimension + 1):
            addition += ", size(%s,%i)" % (original_variable_name, i)
        return condition + " allocate(%s( %s ) )" % (temporal_variable_name, addition)


def use_temporal_variable(sourcefile, line_index, argument, argument_index, routine=None, dummy=None, counter=None):
    tmp_var = "tmp_var_%i" % counter.count
    from .Classes import Variable
    var_object = Variable(routine, tmp_var, "rpe_var", dummy.dimension)
    routine.add_variable(var_object)
    counter.up()

    temporal_call = sourcefile.lines[line_index]
    if temporal_call.lower().strip().find("if(") == 0:
        condition = close_brackets(temporal_call)
    else:
        condition = ""

    # Temporal variable declaration
    temporal_variable_declaration = temporal_variable_allocation_line(tmp_var, dummy.dimension)

    # Allocation
    allocation = temporal_variable_allocation_line(condition, tmp_var, argument,dummy.dimension)

    # Deallocation
    if dummy.dimension == 0:
        deallocation = ""
    else:
        deallocation = condition + "deallocate(%s)" % tmp_var

    # Pre assignation
    preassignation = condition + "%s = %s" % (tmp_var, argument)

    # Post assignation
    postassignation = condition + "%s = %s" % (argument, tmp_var)

    # Insert declaration line
    declaration_line = line_index
    for i in reversed(range(line_index)):
        if sourcefile.lines[i].count("::"):
            declaration_line = i
            break
    sourcefile.lines.insert(declaration_line + 1, temporal_variable_declaration)

    line_index += 1
    # Insert allocation
    sourcefile.lines.insert(line_index, allocation)
    # Insert preassignation
    sourcefile.lines.insert(line_index + 1, preassignation)

    line_index += 2

    # Replace call
    call = replace_argument(temporal_call, argument_index, tmp_var)

    sourcefile.lines[line_index] = call

    sourcefile.lines.insert(line_index + 1, postassignation)

    # Insert deallocation
    sourcefile.lines.insert(line_index + 2, deallocation)


def replace_argument(string, _field, new_argument):

    # Keep the condition if exists
    if string.lower().strip().find("if(") == 0:
        condition = close_brackets(string)
        has_condition = True
        string = string.replace(condition, "%REPLACEMENT%")
    else:
        has_condition = False

    num_arguments, intervals = find_argument_intervals(string)
    interval = intervals[_field]
    start = interval[0]
    end = interval[1]
    first_segment = string[:start]
    last_segment = string[end:]
    string = "%s %s %s" % (first_segment, new_argument, last_segment)
    if has_condition:
        string = string.replace("%REPLACEMENT%", condition)
    return string
