"""Provides centralised access to example data sets that can be used in tests
and also in example code and/or jupyter notebooks.

The `genomic_config.examples` module is very simple, this is not really
designed for editing via end users but they should call three public
functions, `genomic_config.example_data.examples.get_data()`,
`genomic_config.example_data.examples.help()` and
`genomic_config.example_data.examples.list_datasets()`.

Notes
-----
Data can be "added" either through functions that generate the data on the fly
or via functions that load the data from a static file located in the
``example_data`` directory. The data files being added  should be as small as
possible (i.e. kilobyte/megabyte range). The dataset functions should be
decorated with the ``@dataset`` decorator, so the example module knows about
them. If the function is loading a dataset from a file in the package, it
should look for the path in ``_ROOT_DATASETS_DIR``.

Examples
--------

Registering a function as a dataset providing function:

>>> @dataset
>>> def dummy_data(*args, **kwargs):
>>>     \"\"\"A dummy dataset function that returns a small list.
>>>
>>>     Returns
>>>     -------
>>>     data : `list`
>>>         A list of length 3 with ``['A', 'B', 'C']``
>>>
>>>     Notes
>>>     -----
>>>     This function is called ``dummy_data`` and has been decorated with a
>>>     ``@dataset`` decorator which makes it available with the
>>>     `example_data.get_data(<NAME>)` function and also
>>>     `example_data.help(<NAME>)` functions.
>>>     \"\"\"
>>>     return ['A', 'B', 'C']

The dataset can then be used as follows:

>>> from genomic_config import examples
>>> examples.get_data('dummy_data')
>>> ['A', 'B', 'C']

A dataset function that loads a dataset from file, these functions should load
 from the ``_ROOT_DATASETS_DIR``:

>>> @dataset
>>> def dummy_load_data(*args, **kwargs):
>>>     \"\"\"A dummy dataset function that loads a string from a file.
>>>
>>>     Returns
>>>     -------
>>>     str_data : `str`
>>>         A string of data loaded from an example data file.
>>>
>>>     Notes
>>>     -----
>>>     This function is called ``dummy_data`` and has been decorated with a
>>>     ``@dataset`` decorator which makes it available with the
>>>     `example_data.get_data(<NAME>)` function and also
>>>     `example_data.help(<NAME>)` functions. The path to this dataset is
>>>     built from ``_ROOT_DATASETS_DIR``.
>>>     \"\"\"
>>>     load_path = os.path.join(_ROOT_DATASETS_DIR, "string_data.txt")
>>>     with open(load_path) as data_file:
>>>         return data_file.read().strip()

The dataset can then be used as follows:

>>> from genomic_config import examples
>>> examples.get_data('dummy_load_data')
>>> 'an example data string'
"""
import os
import re
import configparser
import tempfile
import pprint as pp


# The name of the example datasets directory
_EXAMPLE_DATASETS = "example_datasets"
"""The example dataset directory name (`str`)
"""

_ROOT_DATASETS_DIR = os.path.join(os.path.dirname(__file__), _EXAMPLE_DATASETS)
"""The root path to the dataset files that are available (`str`)
"""

_DATASETS = dict()
"""This will hold the registered dataset functions (`dict`)
"""


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def dataset(func):
    """Register a dataset generating function. This function should be used as
    a decorator.

    Parameters
    ----------
    func : `function`
        The function to register as a dataset. It is registered as under the
        function name.

    Returns
    -------
    func : `function`
        The function that has been registered.

    Raises
    ------
    KeyError
        If a function of the same name has already been registered.

    Notes
    -----
    The dataset function should accept ``*args`` and ``**kwargs`` and should be
    decorated with the ``@dataset`` decorator.

    Examples
    --------
    Create a dataset function that returns a dictionary.

    >>> @dataset
    >>> def get_dict(*args, **kwargs):
    >>>     \"\"\"A dictionary to test or use as an example.
    >>>
    >>>     Returns
    >>>     -------
    >>>     test_dict : `dict`
    >>>         A small dictionary of string keys and numeric values
    >>>     \"\"\"
    >>>     return {'A': 1, 'B': 2, 'C': 3}
    >>>

    The dataset can then be used as follows:

    >>> from genomic_config import examples
    >>> examples.get_data('get_dict')
    >>> {'A': 1, 'B': 2, 'C': 3}

    """
    try:
        _DATASETS[func.__name__]
        raise KeyError("function already registered")
    except KeyError:
        pass

    _DATASETS[func.__name__] = func
    return func


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def get_data(name, *args, **kwargs):
    """Central point to get the datasets.

    Parameters
    ----------
    name : `str`
        A name for the dataset that should correspond to a unique key in the
        DATASETS module level dictionary.
    *args
        Arguments to the data generating functions
    **kwargs
        Keyword arguments to the data generating functions

    Returns
    -------
    dataset : `Any`
        The requested datasets
    """
    try:
        return _DATASETS[name](*args, **kwargs)
    except KeyError as e:
        raise KeyError("dataset not available: {0}".format(name)) from e


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def list_datasets():
    """List all the registered datasets.

    Returns
    -------
    datasets : `list` of `tuple`
        The registered datasets. Element [0] for each tuple is the dataset name
        and element [1] is a short description captured from the docstring.
    """
    datasets = []
    for d in _DATASETS.keys():
        desc = re.sub(
            r'(Parameters|Returns).*$', '', _DATASETS[d].__doc__.replace(
                '\n', ' '
            )
        ).strip()
        datasets.append((d, desc))
    return datasets


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def help(name):
    """Central point to get help for the datasets.

    Parameters
    ----------
    name : `str`
        A name for the dataset that should correspond to a unique key in the
        DATASETS module level dictionary.

    Returns
    -------
    help : `str`
        The docstring for the function.
    """
    docs = ["Dataset: {0}\n{1}\n\n".format(name, "-" * (len(name) + 9))]
    try:
        docs.extend(
            ["{0}\n".format(re.sub(r"^\s{4}", "", i))
             for i in _DATASETS[name].__doc__.split("\n")]
        )
        return "".join(docs)
    except KeyError as e:
        raise KeyError("dataset not available: {0}".format(name)) from e


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@dataset
def dummy_data(*args, **kwargs):
    """A dummy dataset function that returns a small list.

    Returns
    -------
    data : `list`
        A list of length 3 with ``['A', 'B', 'C']``

    Notes
    -----
    This function is called ``dummy_data`` and has been decorated with a
    ``@dataset`` decorator which makes it available with the
    `example_data.get_data(<NAME>)` function and also
    `example_data.help(<NAME>)` functions.
    """
    return ['A', 'B', 'C']


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@dataset
def dummy_load_data(*args, **kwargs):
    """A dummy dataset function that loads a string from a file.

    Returns
    -------
    str_data : `str`
        A string of data loaded from an example data file.

    Notes
    -----
    This function is called ``dummy_data`` and has been decorated with a
    ``@dataset`` decorator which makes it available with the
    `example_data.get_data(<NAME>)` function and also
    `example_data.help(<NAME>)` functions. The path to this dataset is built
    from ``_ROOT_DATASETS_DIR``.
    """
    load_path = os.path.join(_ROOT_DATASETS_DIR, "string_data.txt")
    with open(load_path) as data_file:
        return data_file.read().strip()


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@dataset
def config_file_path(*args, **kwargs):
    """Returns a path to a fully usable genomic configuration file with the
    basename ``genomic_data.ini``.

    Parameters
    ----------
    *args
        Ignored
    **kwargs
        Ignored

    Returns
    -------
    config_path : `str`
        The full path to a usable genomic configuration file.
    """
    return os.path.join(_ROOT_DATASETS_DIR, "genomic_data.ini")


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@dataset
def species_syn_only(*args, **kwargs):
    """Returns a path to a config file that only has the species synonym
    section.

    Parameters
    ----------
    *args
        Ignored
    **kwargs
        Ignored

    Returns
    -------
    config_path : `str`
        The full path to a usable genomic configuration file.
    """
    return os.path.join(_ROOT_DATASETS_DIR, "spec_syn.ini")


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@dataset
def test_genome_assemblies(*args, **kwargs):
    """A small selection of four "un-normalised" genome assemblies.

    Parameters
    ----------
    *args
        Ignored
    **kwargs
        Ignored

    Returns
    -------
    test_assembly_names : `list` of `str`
        Some test genome assembly names:'grch37','GRCh38','ncbi36','b35'.
    """
    return ['grch37', 'GRCh38', 'ncbi36', 'b35']


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@dataset
def config_file_copy(outdir, create_chain_files=True, create_indexes=True,
                     create_ref_genome_files=True, create_mapping_files=True,
                     create_cohort_files=True, basename="genomic_data.ini"):
    """Returns a path to a fully usable genomic configuration file with some
    local test files that are present on the system.

    Parameters
    ----------
    outdir : `str`
        The directory to output the test config file to and sub directories
        containing the local files represented in the configuration file.
    create_chain_files : `bool`, optional, default: `True`
        Create a dummy chain file as well as a reference to the file in the
        configuration file. Useful for testing missing file errors.
    create_indexes : `bool`, optional, default: `True`
        When creating dummy cohort files and reference genome files, create
        dummy index files as well.
    create_ref_genome_files : `bool`, optional, default: `True`
        Create dummy reference genome files
    create_mapping_files : `bool`, optional, default: `True`
        Create dummy mapping files.
    create_cohort_files : `bool`, optional, default: `True`
        Create dummy cohort files. Note that this does not impact the section
        and section values in the config file, it only impacts the creation of
        dummy files, so can be used to mimic missing files.
    basename : `str`, optional, default: `genomic_data.ini`
        The basename for the output config file. This is placed within outdir.

    Returns
    -------
    config_path : `str`
        The full path to a usable (for testing and examples) genomic
        configuration file.

    Notes
    -----
    The chain config section header will have the structure:
    ``chain_files.<species>.<source_assembly>``. The empty dummy chain files
    will have the file name structure:
    ``<species>_<source_assembly>_<target_assembly>.chain`` . The dummy chain
    files will be in a temporary directory created within ``outdir`` with
    the prefix ``chain_files``. The assembly names are gathered from the
    ``test_genome_assemblies`` dataset.
    See: ``help('test_genome_assemblies')``.
    """
    # The config parser is setup the same way as in the IniConfig class
    parser = configparser.ConfigParser(
        allow_no_value=True, delimiters=('=',)
    )
    # This ensures that the keys are case-sensitive
    parser.optionxform = str

    # Read in the master copy of the config we have and then add it it
    parser.read(config_file_path())

    # The location we will write the full file with chain files and local vcf
    # and bgen files
    target_path = os.path.join(outdir, basename)
    species = 'homo_sapiens'

    # The directory to write the chain files.
    chain_dir = tempfile.mkdtemp(prefix="chain_files_", dir=outdir)

    # Add some chain sections based on these assemblies. the source and target
    # assemblies will never be the same
    assemblies = test_genome_assemblies()

    for i in assemblies:
        targets = assemblies.copy()
        targets.pop(targets.index(i))
        _add_chain_file_section(
            parser, chain_dir, species, i, targets, create=create_chain_files
        )

    # Add some dummy reference genome sequences
    _add_ref_genome_sections(
        parser, outdir, species, assemblies, create=create_ref_genome_files,
        create_index=create_indexes
    )

    # Add some dummy mapping file sections
    _add_mapping_file_sections(
        parser, outdir, species, assemblies, ['all', 'common'],
        create=create_mapping_files, create_index=create_indexes
    )

    # The directory to write the cohort files.
    cohort_dir = tempfile.mkdtemp(prefix="cohort_files_", dir=outdir)

    # Add a cohort file with no implicit or explicit hinting, this means that
    # it is "genome-wide"
    _add_genome_wide_cohort(
        parser, cohort_dir, species, assemblies[0], 'NO_HINT',
        file_format='vcf', create=create_cohort_files
    )

    # Add some implicitly hinted cohorts
    _add_hint_cohort(
        parser, cohort_dir, species, assemblies[0], 'IMPLICIT',
        file_format='bgen', create=create_cohort_files, explicit=False
    )
    _add_hint_cohort(
        parser, cohort_dir, species, assemblies[0], 'IMPLICIT',
        file_format='vcf', create=create_cohort_files, explicit=False
    )
    # Add some explicitly hinted cohorts
    _add_hint_cohort(
        parser, cohort_dir, species, assemblies[0], 'EXPLICIT',
        file_format='bgen', create=create_cohort_files, explicit=True
    )
    _add_hint_cohort(
        parser, cohort_dir, species, assemblies[0], 'EXPLICIT',
        file_format='vcf', create=create_cohort_files, explicit=True
    )

    # Now write the new config file
    with open(target_path, 'w') as outini:
        parser.write(outini)
    return target_path


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def _add_hint_cohort(parser, outdir, species, assembly, name,
                     file_format='vcf', create=True, explicit=True):
    """Add a cohort file section to a config parser object with either implicit
    or explicit hinting.

    Parameters
    ----------
    parser : `configparser.ConfigParser`
        The parser object to add the cohort file section to
    outdir : `str`
        A directory to write the dummy (empty) cohort files.
    species : `str`
        A species name to associate with the cohort.
    assembly : `str`
        A assembly for the dummy cohort files.
    name : `str`
        A name for the cohort
    file_format : `str` (either `vcf` or `bgen`), optional, default: `vcf`
        The file format for the cohort. Appropriate (dummy) index files will
        also be created in addition to the cohort files (assuming create is
        `True`).
    create : `bool`, optional, default: `True`
        Create a dummy cohort files/index files in addition a reference to the
        file in the configuration file. Useful for testing missing file errors.
    explicit ; `bool`, optional, default: `True`
        Generate the config file sections for explicitly hinted files (`True`).
        If `False` then generate the config file sections for implicitly hinted
        files.

    Raises
    ------
    ValueError
        If the ``file_format`` is not `vcf` or `bgen`.
    """
    if file_format not in ['vcf', 'bgen']:
        raise ValueError("unknown file format: {0}".format(file_format))

    # Set the cohort section heading
    section = "cohort.{0}.{1}.{2}.{3}".format(
        species, assembly, file_format, name
    )
    parser.add_section(section)

    # The base file name (with chromosome placeholders) for the chromosome
    # hinted files
    basefile = os.path.join(
        outdir, "{0}_{1}_{2}_chr{4}_whole_chr.cohort.{3}".format(
            name, species, assembly, file_format, '{CHR}'
        )
    )

    # First some chromosome only implicit hinting
    for chr_name in ['1', '2', '3', '4', 'X', 'Y', 'MT']:
        outfile = basefile.format(CHR=chr_name)

        # Option to create the actual file (or not)
        if create is True:
            open(outfile, 'w').close()
            _add_cohort_index(outfile, file_format)

        # For explicit hinting we will write the hint as a key to the config
        # file and the file as a value
        if explicit is True:
            parser.set(section, chr_name, outfile)

    # For implicit hinting there is no hinting key and the key is actually the
    # file
    if explicit is False:
        parser.set(section, basefile, None)

    # The base file name (with chromosome, start, end placeholders) for the
    # fully hinted files
    basefile = os.path.join(
        outdir, "{0}_{1}_{2}_chr{4}_{5}_{6}_chunk.cohort.{3}".format(
            name, species, assembly, file_format, '{CHR}', '{START}', '{END}'
        )
    )

    # Generate the fully hinted files.
    for chr_name, start_pos, end_pos in [
            ('1', 12345, 22345), ('1', 300000, 400000),
            ('Y', 50000, 100000), ('MT', 1, 50000)
    ]:
        outfile = basefile.format(CHR=chr_name, START=start_pos, END=end_pos)

        # Option to create the actual file (or not)
        if create is True:
            open(outfile, 'w').close()
            _add_cohort_index(outfile, file_format)

        if explicit is True:
            parser.set(
                section, "{0}:{1}-{2}".format(chr_name, start_pos, end_pos),
                outfile
            )

    if explicit is False:
        parser.set(section, basefile, None)


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def _add_genome_wide_cohort(parser, outdir, species, assembly, name,
                            file_format='vcf', create=True):
    """Add a chain file section to a config parser object.

    Parameters
    ----------
    parser : `configparser.ConfigParser`
        The parser object to add the cohort file section to
    outdir : `str`
        A directory to write the dummy (empty) cohort files.
    species : `str`
        A species name to associate with the cohort.
    assembly : `str`
        A assembly for the dummy cohort files.
    name : `str`
        A name for the cohort
    file_format : `str` (either `vcf` or `bgen`)
        The file format for the cohort. Appropriate (dummy) index files will
        also be created in addition to the cohort files (assuming create is
        `True`).
    create : `bool`, optional, default: `True`
        Create a dummy cohort files/index files in addition a reference to the
        file in the configuration file. Useful for testing missing file errors.

    Raises
    ------
    ValueError
        If the ``file_format`` is not `vcf` or `bgen`.
    """
    if file_format not in ['vcf', 'bgen']:
        raise ValueError("unknown file format: {0}".format(file_format))

    section = "cohort.{0}.{1}.{2}.{3}".format(
        species, assembly, file_format, name
    )
    parser.add_section(section)

    outfile = os.path.join(
        outdir, "{0}_{1}_{2}.cohort.{3}".format(
            name, species, assembly, file_format
        )
    )
    # Option to create the actual file (or not)
    if create is True:
        open(outfile, 'w').close()
        _add_cohort_index(outfile, file_format)
    parser.set(section, outfile, None)


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def _add_cohort_index(parent_file, file_format):
    """Utility function to generate a dummy index file with the same basename
    as the parent file.

    Parameters
    ----------
    parent_file : `str`
        The path to the parent file.
    file_format : `str` (either `vcf` or `bgen`)
        The format of the parent file. If `vcf` then a `.tbi` index will be
        generated if `bgen` then a ``.bgi`` index will be generated.

    Raises
    ------
    ValueError
        If the ``file_format`` is not `vcf` or `bgen`.
    """
    if file_format == 'vcf':
        index_file = "{0}.tbi".format(parent_file)
    elif file_format == 'bgen':
        index_file = "{0}.bgi".format(parent_file)
    else:
        raise ValueError("unknown file format: {0}".format(file_format))

    open(index_file, 'w').close()
    return index_file


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def _add_chain_file_section(parser, outdir, species, source_assembly,
                            target_assemblies, create=True):
    """Add a chain file section to a config parser object.

    Parameters
    ----------
    parser : `configparser.ConfigParser`
        The parser object to add the chain file section to
    outdir : `str`
        A directory to write the dummy (empty) chain files to
    species : `str`
        A species name to associate with the chain files
    source_assembly : `str`
        A dummy source assembly for the chain files
    target_assemblies : `list` of `str`
        A selection of target assembly names that the source_assembly will
        hypothetically liftover to.
    create : `bool`, optional, default: `True`
        Create a dummy chain file as well as a reference to the file in the
        configuration file. Useful for testing missing file errors.

    Notes
    -----
    The chain config section header will have the structure:
    ``chain_files.<species>.<source_assembly>``. The empty dummy chain files
    will have the file name structure:
    ``<species>_<source_assembly>_<target_assembly>.chain
    """
    section = "chain_files.{0}.{1}".format(species, source_assembly)
    parser.add_section(section)

    for i in target_assemblies:
        outfile = os.path.join(
            outdir, "{0}_{1}_{2}.chain".format(species, source_assembly, i)
        )
        # Option to create the actual file (or not)
        if create is True:
            open(outfile, 'w').close()

        parser.set(section, i, outfile)


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def _add_ref_genome_sections(parser, outdir, species, assemblies, create=True,
                             create_index=True):
    """Add a reference genome file section to a config parser object.

    Parameters
    ----------
    parser : `configparser.ConfigParser`
        The parser object to add the chain file section to
    outdir : `str`
        A directory to write the dummy (empty) chain files to
    species : `str`
        A species name to associate with the chain files
    assemblies : `list` of `str`
        A selection of assembly names that are covered by the hypothetical
        reference genome assemblies.
    create : `bool`, optional, default: `True`
        Create a dummy reference genome assembly file file as well as a
        reference to the file in the configuration file. Useful for testing
        missing file errors.
    create_index : `bool`, optional, default: `True`
        Create a dummy dummy reference genome assembly file index file.

    Notes
    -----
    The reference_genome config section header will have the structure:
    ``reference_genome.<species>.<assembly>``. The empty dummy reference
    genome files will have the file name structure:
    ``<species>_<assembly>_ref_genome.faa.gz``
    """
    for i in assemblies:
        section = "reference_genome.{0}.{1}".format(species, i)
        parser.add_section(section)

        outfile = os.path.join(
            outdir, "{0}_{1}_ref_genome.faa.gz".format(
                species, i
            )
        )

        # Option to create the actual file (or not)
        if create is True:
            open(outfile, 'w').close()

        # Option to create the actual file (or not)
        if create_index is True:
            open("{0}.fai".format(outfile), 'w').close()
            open("{0}.gzi".format(outfile), 'w').close()

        parser.set(section, "local", outfile)


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def _add_mapping_file_sections(parser, outdir, species, assemblies, names,
                               create=True, create_index=True):
    """Add a reference genome file section to a config parser object.

    Parameters
    ----------
    parser : `configparser.ConfigParser`
        The parser object to add the chain file section to
    outdir : `str`
        A directory to write the dummy (empty) chain files to
    species : `str`
        A species name to associate with the chain files
    assemblies : `list` of `str`
        A selection of assembly names that are covered by the hypothetical
        reference genome assemblies.
    names : `list` of `str`
        Dummy names that will be associated with the dummy mapping files
        in the config file
    create : `bool`, optional, default: `True`
        Create a dummy reference genome assembly file file as well as a
        reference to the file in the configuration file. Useful for testing
        missing file errors.
    create_index : `bool`, optional, default: `True`
        Create a dummy dummy reference genome assembly file index file.
    create_index : `bool`, optional, default: `True`
        Create a dummy dummy reference genome assembly file index file.

    Notes
    -----
    The reference_genome config section header will have the structure:
    ``reference_genome.<species>.<assembly>``. The empty dummy reference
    genome files will have the file name structure:
    ``<species>_<assembly>_ref_genome.faa.gz``
    """
    for i in assemblies:
        section = "mapping_file.{0}.{1}".format(species, i)
        parser.add_section(section)
        for j in names:
            outfile = os.path.join(
                outdir, "{0}_{1}_{2}_mapping_file.vcf.gz".format(
                    species, i, j
                )
            )

            # Option to create the actual file (or not)
            if create is True:
                open(outfile, 'w').close()

            # Option to create the actual file (or not)
            if create_index is True:
                open("{0}.tbi".format(outfile), 'w').close()

            parser.set(section, j, outfile)
