import fred.src.utils as utils
import fred.src.web_interface.whitelist_parsing as whitelist_parsing
import fred.src.web_interface.yaml_to_wi_object as yto
import fred.src.web_interface.wi_utils as wi_utils
import copy
import re


def get_factors(organism, key_yaml, read_in_whitelists):
    """
    This function returns all experimental factors with a whitelist of their
    values
    :param key_yaml: the read in general structure
    :param organism: the organism that was selected by the user
    :return: factor_value: a dictionary containing factors and whitelists
    """

    factor_desc = list(utils.find_keys(key_yaml, "experimental_factors"))[0][
        "factor_desc"
    ]
    f_node = list(utils.find_keys(key_yaml, "factor"))[0]
    # initialize dictionary with all factors
    factor_list, whitelist_type, input_type, headers, whitelist_keys, double = (
        whitelist_parsing.parse_whitelist(
            "factor",
            f_node,
            {"organism": organism},
            whitelist_object=read_in_whitelists,
        )
    )

    factor_value = {"factor": factor_list, "desc": factor_desc}
    plain_factors = []

    for elem in factor_list:
        plain_factors += elem["whitelist"]
    # initialize empty dictionary to store values of each factor
    values = {}

    # iterate over factors
    for factor in plain_factors:

        # get attributes of factor from general structure
        node = list(utils.find_keys(key_yaml, factor))

        # factor was found in general structure
        if len(node) > 0:

            # call function 'get_factor_values' to get whitelist information
            (
                whitelist,
                whitelist_type,
                input_type,
                headers,
                w_keys,
                double,
                nested_infos,
            ) = get_factor_values(
                factor, node[0], {"organism": organism}, read_in_whitelists
            )

            # change single_autofill to multi_autofill because all factors can
            # have multiple values
            if input_type == "single_autofill":
                input_type = "multi_autofill"

            # save whitelist, input_type and whitelist_type for the values of
            # one factor
            values[factor] = {
                "whitelist": whitelist,
                "input_type": input_type,
                "whitelist_type": whitelist_type,
                "displayName": node[0]["display_name"],
                "desc": utils.print_desc(
                    (
                        node[0]["factor_desc"]
                        if "factor_desc" in node[0]
                        else node[0]["desc"]
                    ),
                    "html",
                ).replace("\n", "<br>"),
            }

            # add search_info if input is of type single- or multi-autofill
            if input_type == "multi_autofill":
                values[factor]["search_info"] = {
                    "organism": organism,
                    "key_name": factor,
                }

            if input_type == "restricted_short_text":
                # default
                values[factor]["restriction"] = {"regex": " ", "max_length": 10}

                if "special_case" in node and "restriction" in node["special_case"]:
                    if "regex" in node["special_case"]["restriction"]:
                        values[factor]["restriction"]["regex"] = node["special_case"][
                            "restriction"
                        ]["regex"]
                    if "max_length" in node["special_case"]["restriction"]:
                        values[factor]["restriction"]["max_length"] = node[
                            "special_case"
                        ]["restriction"]["max_length"]

            # add header and whitelist keys if they are defined
            if headers is not None:
                values[factor]["headers"] = headers
            if w_keys is not None:
                values[factor]["whitelist_keys"] = w_keys
            if len(double) > 0:
                values[factor]["double"] = double
            if nested_infos is not None:
                values[factor]["nested_infos"] = nested_infos

    # add the values to the dictionary
    factor_value["values"] = values

    return factor_value


def get_factor_values(key, node, filled_object, read_in_whitelists, nested_infos=None):
    """
    This function is used to get the whitelists of experimental factors
    including the whitelist type, input type, headers and whitelist keys
    :param key: the name of the experimental factor
    :param node: the part of the general structure containing information of
                 the factor
    :param filled_object: a dictionary storing the used organism and used for
                          parsing of whitelists of type 'depend' (dependent
                          on organism)
    :return:
    whitelist: a list or dictionary of available values for input (type depends
               on whitelist_type)
    whitelist_type: the type of the whitelist (e.g. plain, group, ...)
    input_type: the input type for the input field in the web interface (e.g.
                short_text, select, single_autofill, ...)
    headers: the headers of the whitelist (None if no headers are defined)
    w_keys: the keys of a whitelist of type group that was rewritten to type
            plain (None if the whitelist is not plain group)
    """

    # initialize headers, whitelist keys and whitelist type with None
    headers = None
    w_keys = None
    whitelist_type = None
    double = []

    # value is a dictionary and no special case
    if isinstance(node["value"], dict) and not (
        "special_case" in node
        and ("merge" in node["special_case"] or "value_unit" in node["special_case"])
    ):

        # initialize whitelist as empty list
        whitelist = []
        nested_infos = {}

        # iterate over the keys of the value
        for k in node["value"]:

            key_info = {}

            # initialize an empty dictionary to store the properties of
            # the key
            k_val = {}

            # call this function to get the whitelist information for the keys
            (
                k_val["whitelist"],
                k_val["whitelist_type"],
                k_val["input_type"],
                header,
                whitelist_keys,
                doubled,
                nested_infos,
            ) = get_factor_values(
                k,
                node["value"][k],
                filled_object,
                read_in_whitelists,
                nested_infos=nested_infos,
            )

            # add header and whitelist keys to dictionary if they are defined
            if header is not None:
                k_val["headers"] = header
                key_info["headers"] = header
            if whitelist_keys is not None:
                k_val["whitelist_keys"] = whitelist_keys
                key_info["whitelist_keys"] = whitelist_keys
            if len(doubled) > 0:
                k_val["double"] = doubled
                key_info["double"] = doubled

            # add key 'unit' if key is of special case value_unit
            if k_val["input_type"] == "value_unit":
                k_val["unit"] = None

            # add properties from the general structure
            k_val["displayName"] = node["value"][k]["display_name"]
            k_val["required"] = node["value"][k]["mandatory"]
            k_val["position"] = k
            k_val["value"] = []
            k_val["desc"] = node["value"][k]["desc"]

            # change single_autofill to multi_autofill because all factors can
            # have multiple values
            if k_val["input_type"] == "single_autofill":
                k_val["input_type"] = "multi_autofill"

            if k_val["input_type"] == "restricted_short_text":

                # default
                k_val["restriction"] = {"regex": " ", "max_length": 10}

                if (
                    "special_case" in node["value"][k]
                    and "restriction" in node["value"][k]["special_case"]
                ):
                    if "regex" in node["value"][k]["special_case"]["restriction"]:
                        k_val["restriction"]["regex"] = node["value"][k][
                            "special_case"
                        ]["restriction"]["regex"]
                    if "max_length" in node["value"][k]["special_case"]["restriction"]:
                        k_val["restriction"]["max_length"] = node["value"][k][
                            "special_case"
                        ]["restriction"]["max_length"]

            # add search info if the input is of type single- or multi-autofill
            if k_val["input_type"] == "multi_autofill":
                k_val["whitelist"] = None
                k_val["search_info"] = {
                    "organism": filled_object["organism"],
                    "key_name": k,
                }

            # add dictionary with properties of the key to the whitelist
            whitelist.append(k_val)
            if len(key_info) > 0:
                nested_infos[k] = key_info

        # set input type to nested
        input_type = "nested"
        if len(nested_infos) == 0:
            nested_infos = None

    # value is not a dictionary or special_case: merge or value_unit
    else:

        # read and parse whitelist
        whitelist, whitelist_type, input_type, headers, w_keys, double = (
            whitelist_parsing.parse_whitelist(
                key, node, filled_object, whitelist_object=read_in_whitelists
            )
        )

    # factor takes a list as value -> can occur multiple times in one condition
    if node["list"]:

        # values are single values (no dictionaries)
        if not isinstance(node["value"], dict):

            # change single_autofill to multi_autofill because all factors can
            # have multiple values
            if input_type == "single_autofill":
                input_type = "multi_autofill"

    return whitelist, whitelist_type, input_type, headers, w_keys, double, nested_infos


def get_conditions(factors, organism_name, key_yaml, read_in_whitelists):
    """
    This function creates all combinations of selected experimental factors and
    their values
    :param key_yaml: the read in general structure
    :param organism_name: the selected organism
    :param factors: multiple dictionaries containing the keys 'factor' and
    'values' with their respective values grouped in a list
    e.g. [{'factor': 'gender', 'values': ['male', 'female']},
          {'factor: 'life_stage', 'values': ['child', 'adult']}]
    :return: a dictionary containing the following keys:
             conditions: a condition object containing a list of all conditions
                         saved as dictionaries
             whitelist_object: a dictionary containing all keys that store a
                               whitelist and their whitelist
             organism: the organism that was chosen by the user
    """

    # initialize an empty dictionary to store the values as they are displayed
    # on the website -> used if there are headers or whitelist keys
    # e.g. value 'GFP' is displayed as 'GFP (other)' on the website since the
    # value is stored in a grouped whitelist that was refactored to plain
    real_val = {}

    # iterate over factors
    for i in range(len(factors)):

        # extract the properties of the factor from the general structure
        factor_infos = list(utils.find_keys(key_yaml, factors[i]["factor"]))

        # sanity check -> factor was found in general structure and
        # list of values contains only one element
        if len(factor_infos) > 0 and len(factors[i]["values"]) == 1:

            # set val to the values specified for the factor
            val = factors[i]["values"][0]

            # value is a dictionary -> e.g. disease
            if isinstance(val, dict):

                # remove keys with value None or empty lists and dictionaries
                val = {
                    k: v
                    for k, v in val.items()
                    if not (type(v) in [list, dict] and len(v) == 0) and v is not None
                }

                # add an ident key to the value
                # -> defined in general structure or None
                val["ident_key"] = (
                    factor_infos[0]["special_case"]["group"]
                    if "special_case" in factor_infos[0]
                    and "group" in factor_infos[0]["special_case"]
                    else None
                )

                val["control"] = (
                    factor_infos[0]["special_case"]["control"]
                    if "special_case" in factor_infos[0]
                    and "control" in factor_infos[0]["special_case"]
                    else None
                )

                if "nested_infos" in factors[i]:
                    for val_key in factors[i]["nested_infos"]:
                        if val_key in val:
                            for v in range(len(val[val_key])):
                                full_value = copy.deepcopy(val[val_key][v])
                                whitelist_key = None
                                if (
                                    "whitelist_keys"
                                    in factors[i]["nested_infos"][val_key]
                                ):
                                    for w_key in factors[i]["nested_infos"][val_key][
                                        "whitelist_keys"
                                    ]:
                                        if val[val_key][v].endswith(f"({w_key})"):
                                            val[val_key][v] = (
                                                val[val_key][v]
                                                .rstrip(f"({w_key})")
                                                .strip()
                                            )
                                            whitelist_key = w_key
                                            break
                                if "headers" in factors[i]["nested_infos"][val_key]:
                                    if isinstance(
                                        factors[i]["nested_infos"][val_key]["headers"],
                                        dict,
                                    ):
                                        if (
                                            whitelist_key is not None
                                            and whitelist_key
                                            in factors[i]["nested_infos"][val_key][
                                                "headers"
                                            ]
                                        ):
                                            str_value = wi_utils.parse_headers(
                                                factors[i]["nested_infos"][val_key][
                                                    "headers"
                                                ][whitelist_key],
                                                val[val_key][v],
                                                mode="str",
                                            )
                                            val[val_key][v] = (
                                                f'{"{"}' f'{str_value}{"}"}'
                                            )
                                    else:
                                        str_value = wi_utils.parse_headers(
                                            factors[i]["nested_infos"][val_key][
                                                "headers"
                                            ],
                                            val[val_key][v],
                                            mode="str",
                                        )
                                        val[val_key][v] = f'{"{"}' f'{str_value}{"}"}'
                                real_val[val[val_key][v]] = full_value

                # generate combinations of the values of the dictionary for the
                # conditions and overwrite the values with them
                factors[i]["values"] = utils.get_combis(
                    val,
                    factors[i]["factor"],
                    {"organism": organism_name.split(" ")[0]},
                    key_yaml,
                    read_in_whitelists=read_in_whitelists,
                )

        elif factor_infos[0]["list"]:

            new_values = []
            for j in range(len(factors[i]["values"])):
                single_val = copy.deepcopy(factors[i]["values"][j])
                single_val_key = None
                if "whitelist_keys" in factors[i]:
                    for w_key in factors[i]["whitelist_keys"]:
                        if single_val.endswith(f"({w_key})"):
                            single_val = single_val.rstrip(f"({w_key})").strip()
                            single_val_key = w_key
                            break
                if "headers" in factors[i]:
                    if isinstance(factors[i]["headers"], dict):
                        if single_val_key is not None:
                            if single_val_key in factors[i]["headers"]:
                                single_val = wi_utils.parse_headers(
                                    factors[i]["headers"][single_val_key],
                                    single_val,
                                    mode="dict",
                                )
                    else:
                        single_val = wi_utils.parse_headers(
                            factors[i]["headers"], single_val, mode="dict"
                        )
                if isinstance(single_val, dict):
                    val = "|".join([f'{key}:"{single_val[key]}"' for key in single_val])
                    val = f'{factors[i]["factor"]}:{"{"}{val}{"}"}'
                    real_val[val] = factors[i]["values"][j]
                new_values.append(single_val)
            factors[i]["values"] = utils.get_combis(
                new_values,
                factors[i]["factor"],
                {"organism": organism_name.split(" ")[0]},
                key_yaml,
                read_in_whitelists=read_in_whitelists,
            )

        # iterate over all values
        for j in range(len(factors[i]["values"])):

            # factor contains whitelist keys
            if "whitelist_keys" in factors[i]:

                # save the original value as full_value
                full_value = copy.deepcopy(factors[i]["values"][j])

                # save the headers if they are specified for the factor else
                # None
                headers = factors[i]["headers"] if "headers" in factors[i] else None

                # rewrite the values by removing the whitelist keys and split
                # them according to the headers if headers were defined
                factors[i]["values"][j] = wi_utils.parse_whitelist_keys(
                    factors[i]["whitelist_keys"],
                    factors[i]["values"][j],
                    headers,
                    mode="str",
                )

                # headers were defined and the whitelist key of the value is
                # defined within the headers
                if (
                    headers is not None
                    and full_value.split("(")[-1].replace(")", "") in headers
                ):
                    # rewrite the value to '<factor>:{<values>}'
                    factors[i]["values"][j] = (
                        f'{factors[i]["factor"]}:{"{"}'
                        f'{factors[i]["values"][j]}{"}"}'
                    )

                # save the original value in real_val with the new value as key
                if factors[i]["values"][j] not in real_val:
                    real_val[factors[i]["values"][j]] = full_value

            # factor contains headers but not whitelist keys
            elif "headers" in factors[i]:

                # save the original value
                full_value = copy.deepcopy(factors[i]["values"][j])

                # split the value according to the header and save them as a
                # string
                str_value = wi_utils.parse_headers(
                    factors[i]["headers"], factors[i]["values"][j], mode="str"
                )

                # # rewrite the value to '<factor>:{<values>}'
                factors[i]["values"][j] = (
                    f'{factors[i]["factor"]}:{"{"}' f'{str_value}{"}"}'
                )

                # save the original value in real_val with the new value as key
                if factors[i]["values"][j] not in real_val:
                    real_val[factors[i]["values"][j]] = full_value

    # generate the conditions
    conditions = utils.get_condition_combinations(factors)

    # extract the properties of 'sample' from the general structure
    sample = list(utils.find_keys(key_yaml, "samples"))

    # initialize a condition- and a whitelist object
    condition_object = []
    whitelist_object = {}

    # sanity check -> sample was found in the general structure
    if len(sample) > 0:

        # create an emtpy wi_object and a whitelist object from the sample
        # structure
        sample, whitelist_object = yto.parse_empty(
            sample[0],
            "experimental_setting:conditions:biological_replicates:samples",
            key_yaml,
            {"organism": organism_name},
            read_in_whitelists,
            get_whitelist_object=True,
        )

        # overwrite sample with its input fields
        sample_desc = sample["desc"]
        sample = sample["input_fields"]

        # iterate over conditions
        for cond in conditions:

            # generate a sample name from the condition
            sample_name = utils.get_short_name(
                cond,
                {"organism": organism_name},
                key_yaml,
                read_in_whitelists=read_in_whitelists,
            )

            # split the condition into key-value pairs
            split_condition = utils.split_cond(cond)
            search_condition = []

            # TODO: remove list (can there be a list?) or own function for items
            for elem in split_condition:
                if isinstance(elem[1], dict):
                    val = "|".join([f'{k}:"{elem[1][k]}"' for k in elem[1]])
                    val = f'{elem[0]}:{"{"}{val}{"}"}'
                    if val in real_val:
                        search_condition.append(f'{elem[0]}:"{real_val[val]}"'.lower())
                    else:
                        for key in elem[1]:
                            if isinstance(elem[1][key], dict):
                                val = "|".join(
                                    [f'{k}:"{elem[1][key][k]}"' for k in elem[1][key]]
                                )
                                val = f'{key}:{"{"}{val}{"}"}'
                                if val in real_val:
                                    search_condition.append(
                                        f'{elem[0]}:"{real_val[val]}"'.lower()
                                    )
                                else:
                                    # TODO: real/val or own function to get values from deeper dictionaries
                                    for sub_key in elem[1][key]:
                                        search_condition.append(
                                            f'{elem[0]}:"{elem[1][key][sub_key]}"'.lower()
                                        )
                            else:
                                search_condition.append(
                                    f'{elem[0]}:"{elem[1][key]}"'.lower()
                                )
                elif isinstance(elem[1], list):
                    for item in elem[1]:
                        search_condition.append(f'{elem[0]}:"{item}"'.lower())
                else:
                    if elem[1] in real_val:
                        search_condition.append(
                            f'{elem[0]}:"{real_val[elem[1]]}"'.lower()
                        )
                    else:
                        search_condition.append(f'{elem[0]}:"{elem[1]}"'.lower())
            # call functions to fill the samples with the values from the
            # condition
            filled_sample = get_samples(
                split_condition,
                copy.deepcopy(sample),
                real_val,
                key_yaml,
                sample_name,
                organism_name,
                read_in_whitelists,
            )

            # rewrite condition into title and string to readd deleted
            # conditions
            title, readd = get_condition_title(split_condition)

            # save the condition as a dictionary with the filled sample as
            # input fields
            d = {
                "correct_value": cond,
                "title": title,
                "search": search_condition,
                "readd": readd,
                "position": "experimental_setting:condition",
                "list": True,
                "mandatory": True,
                "list_value": [],
                "input_disabled": False,
                "desc": sample_desc,
                "input_fields": copy.deepcopy(filled_sample),
            }

            # add the dictionary for the condition to the condition object
            condition_object.append(d)

    return {
        "conditions": condition_object,
        "whitelist_object": whitelist_object,
        "organism": organism_name,
    }


def get_condition_title(split_condition):
    """
    This function rewrites a condition into a table to be displayed as a title
    on the website. It also creates a string that is shown when removed
    conditions have to be re-added
    :param split_condition: a list containing the factors and values the
                            condition consists of
    :return: html: the title as a table in html
             readd: the string shown when re-adding deleted conditions
    """

    # initialize the html string with the table header and style
    html = '<table class="table_style_condition_title"><tbody>'

    # initialize readd as an empty string
    readd = ""

    # iterate over the factors and values
    for i in range(len(split_condition)):

        # start the row of the table with the style depending on the index of
        # the element that is written into the row (the style creates a
        # horizontal line below the row and includes padding)
        if len(split_condition) > 1 and i < len(split_condition) - 1:
            html += '<tr class="tr_style_condition_title">'
        else:
            html += "<tr>"

        # add the factor to readd
        readd += f"{split_condition[i][0]}:\n"

        # add the factor to the table as a data cell with the style of a value
        # (padding)
        html += (
            f'<td class="td_style_condition_title_value">'
            f"{split_condition[i][0]}:</td>"
        )

        vals, readd = get_title_value(split_condition[i][1], readd)

        # add the string with the values to the table as a data cell
        html += f'<td class="td_style_condition_title_value">{vals}' f"</td></tr>"

        # if the condition consists of multiple factors than write a '---'
        # between them in readd
        if len(split_condition) > 1 and i < len(split_condition) - 1:
            readd += "---\n"

    # close the html table
    html += f"</tbody></table>"

    return html, readd


def get_title_value(cond_value, readd):
    # value is a dictionary
    if isinstance(cond_value, dict):

        # initialize an empty string to save the values to
        vals = ""

        # iterate over the keys
        for key in cond_value:

            value, readd = get_title_value(cond_value[key], readd)

            # add the value to the string with a <br> if it is not in the
            # last row
            if key != list(cond_value.keys())[-1]:
                vals += f"{value}<br>"
            else:
                vals += f"{value}"

    # value is not a dictionary
    else:

        # replace more than 3 zeros with 3 dots to shorten the value
        vals = f'"{re.sub("0000(0)*", "...", cond_value)}"'

        # add the value to readd
        readd += f"{vals}\n"

    return vals, readd


def get_samples(
    split_condition,
    sample,
    real_val,
    key_yaml,
    sample_name,
    organism_name,
    read_in_whitelists,
    is_factor=True,
):
    """
    This function created a pre-filled object with the structure of the samples
    to be displayed in the web interface
    :param is_factor: a boolean defining if the input field is an experimental
                      factor
    :param organism_name: the name of the selected organism
    :param sample_name: the identifier of the sample build from the condition
    :param key_yaml: the read in general structure
    :param real_val: a dictionary containing the values containing headers and
                     whitelist keys as they should be displayed on the website
    :param split_condition: the condition the sample is created for
    :param sample: the empty structure of the sample
    :return: sample: the pre-filled sample
    """
    # save all factors in a list
    factors = [cond[0] for cond in split_condition]

    # iterate over samples
    for i in range(len(sample)):

        # input field: sample_name
        if sample[i]["position"].endswith("samples:sample_name"):

            # TODO: improve display
            # add whitespaces to sample name to enable line breaks on the
            # website
            sample[i]["value"] = (
                sample_name.replace(":", ": ")
                .replace("|", "| ")
                .replace("#", "# ")
                .replace("-", " - ")
                .replace("+", " + ")
            )

            # save the unchanged sample name as 'correct_value'
            sample[i]["correct_value"] = sample_name.split("_")[0]

        # elif sample[i]['position'].endswith('samples:sequencer_name'):
        #    sample[i]['value'] = sequencer_name
        #    sample[i]['input_disabled'] = True

        # input field is a factor
        elif sample[i]["position"].split(":")[-1] in factors:

            # iterate over factors in condition
            for c in split_condition:

                # input field of current factor
                if sample[i]["position"].split(":")[-1] == c[0]:
                    # extract properties of the factor from the general
                    # structure
                    info = list(utils.find_keys(key_yaml, c[0]))

                    # sanity check -> factor was found in general structure
                    if len(info) > 0:

                        filled_value = ""
                        # special case value_unit
                        if (
                            "special_case" in info[0]
                            and "value_unit" in info[0]["special_case"]
                        ):

                            if isinstance(c[1], dict):
                                value_unit = c[1]
                            else:
                                # split the value into value and unit
                                value_unit = utils.split_value_unit(c[1])

                            # save the value and unit in the sample
                            sample[i]["value"] = value_unit["value"]
                            sample[i]["value_unit"] = value_unit["unit"]
                            if is_factor:
                                sample[i]["input_disabled"] = True

                        # value is a dictionary
                        elif isinstance(c[1], dict):

                            # rewrite the value into a string
                            val = "|".join([f'{key}:"{c[1][key]}"' for key in c[1]])
                            val = f'{c[0]}:{"{"}{val}{"}"}'
                            # save the value from the dictionary real_val if
                            # real_val contains the current value as key

                            if val in real_val:
                                filled_value = real_val[val]

                            # value is not in real_val
                            else:

                                # TODO: rework with real_val

                                if "input_fields" in sample[i]:
                                    # call this function on the keys of the
                                    # value in order to fill them
                                    filled_value = get_samples(
                                        [
                                            (x, c[1][x])
                                            for x in c[1]  # if not (
                                            # c[1] == 'technical_replicates'
                                            # and x == 'sample_name')
                                        ],
                                        copy.deepcopy(sample[i]["input_fields"]),
                                        info,
                                        key_yaml,
                                        sample_name,
                                        organism_name,
                                        read_in_whitelists,
                                        is_factor=is_factor,
                                    )
                                # TODO: als real_val? automate
                                elif c[0] == "enrichment_type":
                                    connected_value = []
                                    for key in c[1]:
                                        connected_value.append(c[1][key])
                                    connected_value = " ".join(connected_value)
                                    filled_value = f"{connected_value} (proteins)"
                                    sample[i]["headers"] = {
                                        "proteins": "gene_name ensembl_id"
                                    }
                                elif "headers" in sample[i]:
                                    headers = [x for x in c[1]]
                                    w_key = None
                                    if isinstance(sample[i]["headers"], dict):

                                        for k in sample[i]["headers"]:
                                            if sorted(
                                                sample[i]["headers"][k].split(" ")
                                            ) == sorted(headers):
                                                w_key = k
                                                break
                                    else:
                                        if sorted(headers) != sorted(
                                            sample[i]["headers"].split(" ")
                                        ):
                                            headers = None

                                    if headers is not None:
                                        filled_value = ""
                                        for header in headers:
                                            filled_value = (
                                                filled_value + " " + c[1][header]
                                            )
                                        filled_value = filled_value.lstrip(" ").rstrip(
                                            " "
                                        )

                                    if (
                                        filled_value is not None
                                        and w_key is not None
                                        and sample[i]["whitelist_type"] == "plain_group"
                                    ):
                                        filled_value = f"{filled_value} " f"({w_key})"
                            # save the filled value in 'list_value' if the
                            # input field takes a list
                            if sample[i]["list"]:
                                sample[i]["list_value"].append(filled_value)

                            # save the filled value in 'input_fields' if the
                            # input field takes a dictionary
                            elif "input_fields" in sample[i]:
                                sample[i]["input_fields"] = filled_value

                            # save the filled value in 'value'
                            else:
                                sample[i]["value"] = filled_value

                        # value is not a dictionary
                        else:
                            # save the value from the dictionary real_val if it
                            # contains the current value
                            if (
                                c[0] == "enrichment_type"
                                and "headers" in sample[i]
                                and "proteins" in sample[i]["headers"]
                            ):
                                if not c[1].endswith("(proteins)"):
                                    filled_value = f"{c[1]} (proteins)"
                                else:
                                    filled_value = c[1]

                            elif c[1] in real_val:
                                filled_value = real_val[c[1]]

                            # save the current value
                            else:

                                if (
                                    sample[i]["whitelist_type"] == "plain_group"
                                    and "whitelist_keys" in sample[i]
                                ):
                                    w = utils.get_whitelist(
                                        sample[i]["position"].split(":")[-1],
                                        {"organism": organism_name},
                                        whitelist_object=read_in_whitelists,
                                    )

                                    for key in sample[i]["whitelist_keys"]:
                                        filled_value = c[1]
                                        if f"{c[1]} ({key})" in w["whitelist"]:
                                            filled_value = f"{c[1]} ({key})"
                                            break
                                else:
                                    if (
                                        "input_type" in info[0]
                                        and info[0]["input_type"] == "number"
                                    ):
                                        filled_value = int(c[1])
                                    else:
                                        filled_value = c[1]
                            # save the filled value in 'list_value' if the
                            # input field takes a list
                            if sample[i]["list"]:
                                sample[i]["list_value"].append(filled_value)

                            # save the filled value in 'value'
                            else:
                                sample[i]["value"] = filled_value

                        # input field is of type single_autofill
                        if "input_type" in sample[i] and sample[i]["input_type"] in [
                            "single_autofill",
                            "restricted_short_text",
                        ]:
                            # initialize the key 'list_value' and move the
                            # value under the key 'value' to the key
                            # 'list_value'
                            sample[i]["list_value"] = (
                                []
                                if sample[i]["value"] is None
                                else [sample[i]["value"]]
                            )
                            sample[i]["value"] = None

                        if is_factor:

                            if "list" in info[0] and info[0]["list"]:
                                sample[i]["delete_disabled"] = True
                                sample[i]["fixed_length"] = len(sample[i]["list_value"])
                            else:
                                # disable the input for the filled input field
                                sample[i]["input_disabled"] = True

                        if "input_type" in info[0] and info[0]["input_type"] == "bool":
                            if sample[i]["value"]:
                                sample[i]["value"] = "True"
                            else:
                                sample[i]["value"] = "False"

    return sample
