Parse the input yaml config file and download all tool inputs into the file store.

    :param str config_file: Path to the input config file
    :param int max_cores: The maximum cores to use for any single high-compute job.
    :return: tuple of dict of sample dicts, dict of universal options, dict of dicts of tool options
    :rtype: tuple(dict, dict, dict)

def _parse_config_file(job, config_file, max_cores=None):
    """
    Parse the input yaml config file and download all tool inputs into the file store.

    :param str config_file: Path to the input config file
    :param int max_cores: The maximum cores to use for any single high-compute job.
    :return: tuple of dict of sample dicts, dict of universal options, dict of dicts of tool options
    :rtype: tuple(dict, dict, dict)
    """
    job.fileStore.logToMaster('Parsing config file')
    config_file = os.path.abspath(config_file)
    if not os.path.exists(config_file):
        raise ParameterError('The config file was not found at specified location. Please verify ' +
                             'and retry.')
    # Initialize variables to hold the sample sets, the universal options, and the per-tool options
    sample_set = {}
    univ_options = {}
    tool_options = {}

    # Read in the input yaml
    with open(config_file, 'r') as conf:
        input_config = yaml.load(conf.read())

    # Read the set of all required keys
    required_entries_file = pkg_resources.resource_filename(__name__, "required_entries.yaml")
    with open(required_entries_file) as ref:
        required_keys = yaml.load(ref.read())

    # Ensure every required entry is present in the file
    all_keys = set(input_config.keys())
    _ensure_set_contains(all_keys, required_keys)

    # Now that we are sure that mutation_callers is an entry in the file we can initialise this
    mutation_caller_list = []

    # Get the default values from the defaults file
    protect_defaults_file = pkg_resources.resource_filename(__name__, "defaults.yaml")
    with open(protect_defaults_file) as pdf:
        protect_defaults = yaml.load(pdf.read())

    # Update the input yaml with defaults
    input_config = _add_default_entries(input_config, protect_defaults)

    # Flags to check for presence of encryption keys if required
    gdc_inputs = ssec_encrypted = False
    for key in input_config.keys():
        if key == 'patients':
            # Ensure each patient contains the required entries
            for sample_name in input_config[key]:
                patient_keys = input_config[key][sample_name]
                _ensure_patient_group_is_ok(patient_keys, sample_name)
                # Add options to the sample_set dictionary
                input_config['patients'][sample_name]['patient_id'] = str(sample_name)
                sample_set[sample_name] = parse_patients(
                    job, input_config['patients'][sample_name],
                    not input_config['mutation_calling']['star_fusion']['run'])
                # If any of the samples requires gdc or ssec encrypted input, make the flag True
                if sample_set[sample_name]['ssec_encrypted']:
                    ssec_encrypted = True
                if sample_set[sample_name]['gdc_inputs']:
                    if 'tumor_rna_bam' in sample_set[sample_name]['gdc_inputs']:
                        raise ParameterError('Cannot run ProTECT using GDC RNA bams. Please fix '
                                             'sample %s' % sample_name)
                    gdc_inputs = True
        else:
            # Ensure the required entries exist for this key
            _ensure_set_contains(input_config[key], required_keys[key], key)
            if key == 'Universal_Options':
                univ_options.update(input_config['Universal_Options'])
                if univ_options['reference_build'].lower() in ['hg19', 'grch37']:
                    univ_options['ref'] = 'hg19'
                elif univ_options['reference_build'].lower() in ['hg38', 'grch38']:
                    univ_options['ref'] = 'hg38'
                else:
                    raise ParameterError('reference_build can only be hg19, hg38, GRCh37 or GRCh38')
                assert univ_options['storage_location'].startswith(('Local', 'local', 'aws'))
                if univ_options['storage_location'] in ('Local', 'local'):
                    assert os.path.isabs(univ_options['output_folder']), ('Needs to be absolute if '
                                                                          'storage_location is '
                                                                          'Local.')
                    assert univ_options['output_folder'] != 'NA', ('Cannot have NA as output '
                                                                   'folder if storage location is '
                                                                   'Local.')
                    univ_options['storage_location'] = 'local'
                if univ_options['storage_location'].startswith('aws'):
                    if 'sse_key' not in univ_options:
                        raise ParameterError('Cannot write results to aws without an sse key.')
                    if 'sse_key_is_master' in univ_options:
                        if univ_options['sse_key_is_master'] not in (True, False):
                            raise ParameterError('sse_key_is_master must be True or False')
                    else:
                        univ_options['sse_key_is_master'] = False
                univ_options['max_cores'] = cpu_count() if max_cores is None else max_cores
            else:
                if key == 'reports':
                    # The reporting group doesn't have any sub-dicts
                    tool_options[key] = input_config[key]
                else:
                    if key == 'alignment':
                        append_subgroup = ['post']
                    elif key == 'mutation_calling':
                        mutation_caller_list = input_config[key].keys()
                        append_subgroup = []
                    else:
                        append_subgroup = []
                    tool_options.update(_process_group(input_config[key], required_keys[key],
                                                       key, append_subgroup))
    # netmhciipan needs to be handled separately
    tool_options['mhcii']['netmhciipan'] = tool_options['netmhciipan']
    tool_options.pop('netmhciipan')
    # Check for encryption related issues before we download files.
    if ssec_encrypted:
        assert univ_options['sse_key'] is not None, 'Cannot read ssec encrypted data without a key.'
    if gdc_inputs:
        assert univ_options['gdc_download_token'] is not None, ('Cannot read from GDC without a '
                                                                'token.')
    # Get all the tool inputs
    job.fileStore.logToMaster('Obtaining tool inputs')
    process_tool_inputs = job.addChildJobFn(get_all_tool_inputs, tool_options,
                                            mutation_caller_list=mutation_caller_list)
    job.fileStore.logToMaster('Obtained tool inputs')
    return sample_set, univ_options, process_tool_inputs.rv()