import copy

import src.web_interface.whitelist_parsing as whitelist_parsing
import src.web_interface.wi_utils as wi_utils
import datetime
import src.utils as utils

# TODO: example of format in wi_object (Keys)


def get_empty_wi_object(key_yaml, read_in_whitelists):
    """
    This function parses the keys.yaml and returns an empty object for the web
    interface.
    :return: wi_object: object containing all information from the keys.yaml
             in a format readable by the web interface
    """

    # initialize emtpy dict to save wi_object to
    wi_object = {}

    # iterate over all keys in general structure
    for key in key_yaml:

        # parse information of a key into wi_object format
        wi_object[key], whitelist_object = parse_empty(key_yaml[key], key,
                                                       key_yaml, {}, read_in_whitelists)

    # add a key 'all_factors' with an empty list as value to the object
    # the experimental factors will be saved here after input on the website
    wi_object['all_factors'] = []
    wi_object['publication_records'] = {}

    return wi_object


def parse_empty(node, pos, key_yaml, filled_object, read_in_whitelists,
                get_whitelist_object=False):
    """
    This function parses a part of the key.yaml into an object readable by the
    web-interface
    :param filled_object: a dictionary containing pre-filled information
    :param node: a part of the key.yaml that should be parsed
    :param pos: the position of the node (chained keys)
    :param key_yaml: the whole key.yaml
    :param get_whitelist_object: bool, True if whitelists should be written
                                 into a separate object, False if not, default
                                 False
    :return: part_object: an object for the web interface parsed from node
             whitelist_object: a dictionary containing all whitelists, it is
                               only filled if 'get_whitelist_object' is set to
                               True
    """

    # disable input for the keys condition_name and sample_name because they
    # are generated
    input_disabled = True if 'special_case' in node and 'generated' in \
                             node['special_case'] and \
                             node['special_case']['generated'] in \
                             ['end', 'now'] and pos.split(':')[-1] not in ['id', 'setting', 'techniques'] \
        else False

    # initialize a whitelist object to store all whitelists
    whitelist_object = {}

    # test if the value of the current node is a dictionary
    if isinstance(node['value'], dict):
        # initialize the input type with None
        input_type = None

        # special case: merge or value_unit
        if 'special_case' in node and ('merge' in node['special_case'] or
                                       'value_unit' in node['special_case']):

            # read and parse whitelist
            whitelist, whitelist_type, input_type, headers, whitelist_keys, double = \
                whitelist_parsing.parse_whitelist(pos.split(':')[-1], node,
                                                  filled_object, whitelist_object=read_in_whitelists)

            # set input type of field organism to 'organism' -> special case
            # (used to display button to confirm organism on the website)
            if pos.split(':')[-1] == 'organism':
                input_type = 'organism_name'

            # test if the whitelist should be stored in a separate object
            if get_whitelist_object:

                # special case: value_unit
                if input_type == 'value_unit':

                    # store the whitelist in the object under the key 'unit'
                    whitelist_object['unit'] = whitelist
                    whitelist = 'unit'

                # a whitelist is defined for the key
                elif whitelist is not None:

                    # save the whitelist to the object under the keyname
                    whitelist_object[pos.split(':')[-1]] = whitelist
                    whitelist = pos.split(':')[-1]

            # creation and filling of dictionary containing all necessary
            # information for one input field
            part_object = {'position': pos, 'mandatory': node['mandatory'],
                           'list': node['list'],
                           'displayName': node['display_name'],
                           'desc': utils.print_desc(node['desc'], 'html').replace('\n', '<br>'),
                           'value': None,
                           'whitelist': whitelist,
                           'whitelist_type': whitelist_type,
                           'input_type': input_type,
                           'input_disabled': input_disabled,
                           'delete_disabled': False}

            if (part_object['whitelist'] == ['True', 'False'] or part_object['whitelist'] == 'pooled') and part_object[
                'value'] is not \
                    None:
                if part_object['value']:
                    part_object['value'] = 'True'
                else:
                    part_object['value'] = 'False'
            # special case : value unit -> add key value_unit to dict
            if input_type == 'value_unit':
                part_object['value_unit'] = node['value']['unit']['value']

            # save headers and whitelist keys in the object if they are defined
            if headers is not None:
                part_object['headers'] = headers
            if whitelist_keys is not None:
                part_object['whitelist_keys'] = whitelist_keys

            # add a search info for autofill
            if input_type in ['single_autofill', 'multi_autofill']:
                part_object['search_info'] = {
                    'organism': filled_object['organism']
                    if 'organism' in filled_object else None,
                    'key_name': part_object['position'].split(':')[-1]}

        # no special case -> the value takes a dictionary and should be
        # displayed via an expandable
        else:

            # initialize variable input_fields with empty list
            # it is used to save the keys of the 'value' dictionary as input
            # fields
            input_fields = []

            # iterate over all keys in value
            for key in node['value']:

                if not ('special_case' in node['value'][key] and 'invisible' in
                        node['value'][key]['special_case'] and
                        node['value'][key]['special_case']['invisible']):

                    # call this function to create a dictionary object for all keys
                    # storing their necessary information
                    field_infos, w_object = parse_empty(
                        node['value'][key], pos + ':' + key, key_yaml,
                        filled_object, read_in_whitelists,
                        get_whitelist_object=get_whitelist_object)

                    # add the parsed input fields to the list
                    input_fields.append(field_infos)

                    # merge the whitelist object with the object from the input
                    # field
                    whitelist_object = {**whitelist_object, **w_object}

            # creation and filling of dictionary containing all necessary
            # information for one expandable with its input fields
            part_object = {'position': pos, 'mandatory': node['mandatory'],
                           'list': node['list'], 'title': node['display_name'],
                           'desc': utils.print_desc(node['desc'], 'html').replace('\n', '<br>'), 'input_fields': input_fields,
                           'original_input': copy.deepcopy(input_fields),
                           'input_disabled': input_disabled,
                           'delete_disabled': False}

            if 'special_case' in node:
                if 'group' in node['special_case']:
                    part_object['ident_key'] = f'{pos}:{node["special_case"]["group"]}'
                if 'control' in node['special_case']:
                    part_object['control'] = node['special_case']['control']

            if 'short_desc' in node:
                part_object['short_desc'] = node['short_desc']
        # test if the key takes multiple values and add the property
        # 'list_value' as a place to save those values to via the website
        if node['list'] or input_type == 'single_autofill':
            part_object['list_value'] = []

    # the key does not contain a dictionary as value
    else:
        # read and parse whitelist
        whitelist, whitelist_type, input_type, headers, whitelist_keys, double = \
            whitelist_parsing.parse_whitelist(pos.split(':')[-1], node,
                                              filled_object, whitelist_object=read_in_whitelists)

        # test if whitelists are defined and should be stored in a separate
        # object
        if get_whitelist_object and whitelist is not None:

            # set the whitelist to the name of the key if a whitelist was
            # defined
            whitelist_object[pos.split(':')[-1]] = whitelist
            whitelist = pos.split(':')[-1]

        # creation and filling of dictionary containing all necessary
        # information for one input field
        part_object = {'position': pos, 'mandatory': node['mandatory'],
                       'list': node['list'],
                       'displayName': node['display_name'],
                       'desc': utils.print_desc(node['desc'], 'html').replace('\n', '<br>'), 'value': node['value'],
                       'whitelist': whitelist,
                       'whitelist_type': whitelist_type,
                       'input_type': input_type,
                       'input_disabled': input_disabled,
                       'delete_disabled': False}

        if (part_object['whitelist'] == ['True', 'False'] or part_object['whitelist'] == 'pooled') and part_object['value'] is not \
                None:
            if part_object['value']:
                part_object['value'] = 'True'
            else:
                part_object['value'] = 'False'

        # add headers and whitelist keys to the empty object if they are
        # defined
        if headers is not None:
            part_object['headers'] = headers
        if whitelist_keys is not None:
            part_object['whitelist_keys'] = whitelist_keys

        # test if the key takes multiple values or uses autofill and add the
        # property 'list_value' as a place to save those values to via the
        # website
        if node['list'] or input_type in ['single_autofill', 'restricted_short_text']:
            part_object['list_value'] = []

        if input_type == 'restricted_short_text':

            # default
            part_object['restriction'] = {'regex': ' ',
                                          'max_length': 10}

            if 'special_case' in node and 'restriction' in node['special_case']:
                if 'regex' in node['special_case']['restriction']:
                    part_object['restriction']['regex'] = \
                        node['special_case']['restriction']['regex']
                if 'max_length' in node['special_case']['restriction']:
                    part_object['restriction']['max_length'] = \
                        node['special_case']['restriction']['max_length']

        # add the key 'search_info' to all fields using autofill to help get
        # the correct whitelist via the website
        if input_type == 'single_autofill' or input_type == 'multi_autofill':
            part_object['search_info'] = {
                'organism': filled_object['organism']
                if 'organism' in filled_object else None,
                'key_name': part_object['position'].split(':')[-1]}

        if input_type == 'date':
            part_object['value'] = wi_utils.str_to_date(datetime.datetime.now().strftime('%d.%m.%Y'))

    return part_object, whitelist_object
