"""Contains the high level open functions and the command line endpoint to
 genomic-config.
"""
from genomic_config import (
    # __version__,
    # __name__ as pkg_name,
    base_config,
    ini_config
)
from builtins import open as base_open
import argparse
import os
# import pprint as pp


REFGEN_OPTION = 'refgen'
"""The option name for getting a reference genome from the command line (`str`)
"""
CHAIN_OPTION = 'chain'
"""The option name for getting a chain file from the command line (`str`)
"""
MAPPING_OPTION = 'mapping'
"""The option name for getting a mapping file from the command line (`str`)
"""


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def main():
    """The main entry point for the script
    """
    # Initialise and parse the command line arguments
    parser = _init_cmd_args()
    args = _parse_cmd_args(parser)

    config_file = get_config_file(config_file=args.config)
    kwargs = dict(
        use_species_synonyms=not args.no_sp_syn,
        use_assembly_synonyms=not args.no_ge_syn,
        validate_indexes=not args.no_index
    )

    with open(config_file, **kwargs) as cfg:
        if args.option == REFGEN_OPTION:
            refgen = cfg.get_reference_genome(
                args.species, args.assembly, args.name
            )
            print(refgen)
        elif args.option == CHAIN_OPTION:
            chain_file = cfg.get_chain_file(
                args.species, args.source_assembly, args.target_assembly
            )
            print(chain_file)
        elif args.option == MAPPING_OPTION:
            mapping_file = cfg.get_mapping_file(
                args.species, args.assembly, args.name
            )
            print(mapping_file)
        else:
            raise ValueError("unknown option: {0}".format(args.option))


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def _init_cmd_args():
    """Initialise the command line arguments.

    Returns
    -------
    parser : `argparse.ArgumentParser`
        The argparse parser object with arguments added.
    """
    parser = argparse.ArgumentParser(
        description="Extract entries from a genomic configuration file. This"
        " is designed to gather configuration that can be used in bash "
        "scripts. It\"\" does not implement everything yet but "
        "functionality will be added as and when I need it."
    )
    parser.add_argument('--config', '-c', type=str,
                        help="The config file, if not supplied then "
                        "GENOMIC_CONFIG is checked or the root of home")
    parser.add_argument('--no-ge-syn', '-g', action='store_true',
                        help="Do not use assembly synonyms")
    parser.add_argument('--no-sp-syn', '-s', action='store_true',
                        help="Do not use species synonyms")
    parser.add_argument('--no-index', action='store_true',
                        help="Do not attempt to validate the presence of "
                        "any index files")
    subparsers = parser.add_subparsers(dest='option')
    parser_rg = subparsers.add_parser(
        REFGEN_OPTION, help="Get a reference genome location."
    )
    parser_rg.add_argument(
        'species', help='The species'
    )
    parser_rg.add_argument(
        'assembly', help='The genome assembly, i.e. GRCh38'
    )
    parser_rg.add_argument(
        'name', help='The genome assembly name'
    )
    parser_ch = subparsers.add_parser(
        CHAIN_OPTION, help="Get a chain file location."
    )
    parser_ch.add_argument(
        'species', help='The species'
    )
    parser_ch.add_argument(
        'source_assembly', help='The source genome assembly, i.e. GRCh37'
    )
    parser_ch.add_argument(
        'target_assembly', help='The target genome assembly, i.e. GRCh38'
    )
    parser_map = subparsers.add_parser(
        MAPPING_OPTION, help="Get a mapping file location."
    )
    parser_map.add_argument(
        'species', help='The species'
    )
    parser_map.add_argument(
        'assembly', help='The genome assembly, i.e. GRCh38'
    )
    parser_map.add_argument(
        'name', help='The genome assembly name'
    )

    return parser


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def _parse_cmd_args(parser):
    """Initialise the command line arguments and return the parsed arguments

    Parameters
    ----------
    parser : `argparse.ArgumentParser`
        The argparse parser object with arguments added.

    Returns
    -------
    args : `argparse.Namespace`
        The argparse namespace object.
    """
    args = parser.parse_args()
    return args


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def open(config_file=None, **kwargs):
    """Open a config file

    This will determine how to open the config file based on the file
    extension of the config file.

    Parameters
    ----------
    config_file : `str` or `NoneType`, optional, default: `NoneType`
        A configuration file location. If this is provided, it is passed
        through to the config class. If it has not been provided then the
        default locations are checked returned values are passed through to the
        config class.

    Returns
    -------
    open_config : `ini_config.IniConfig`
        An config file handler for ``ini`` config files.

    Raises
    ------
    FileNotFoundError
        If a location for the default config file is not found
    PermissionError
        If a location for the default config file can't be read

    Notes
    -----
    Note at present only ``ini`` configuration files are supported. However, in
    future hopefully others will be supported and this will act as a uniform
    opening mechanism.

    See also
    --------
    get_config_file
    genomic_config.ini_config.IniConfig

    Examples
    --------
    open a config file with a defined path:

    >>> from genomic_config import genomic_config
    >>> cfg = genomic_config.open("my_config.ini") # doctest: +SKIP
    >>> # do stuff with the configuration
    >>> cfg.close() # doctest: +SKIP

    open the config file from a default location, either
    ``~/.genomic_config.ini`` or defined in ``${GENOMIC_CONFIG}``
    environment variable:

    >>> cfg = genomic_config.open()# doctest: +SKIP
    >>> # do stuff with the configuration
    >>> cfg.close()# doctest: +SKIP

    open the config using the context manager:

    >>> with genomic_config.open() as cfg: # doctest: +SKIP
    >>>    # do stuff with the configuration
    >>>    pass # doctest: +SKIP
    """
    config_file = get_config_file(config_file=config_file)
    cfg = ini_config.IniConfig(config_file, **kwargs)
    cfg.open()
    return cfg


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def get_config_file(config_file=None):
    """Attempt to locate and return the genomic config file location if it
    has been defined either in the arguments or the environment.

    Parameters
    ----------
    config_file : `str` or `NoneType`, optional, default: `NoneType`
        A configuration file location. If this is provided, it is simply
        checked for existence and returned. If it has not been provided
        then the default locations are checked and returned.

    Returns
    -------
    config_path : `str`
        The absolute path to the default config file, if found.

    Raises
    ------
    FileNotFoundError
        If a location for the default config file is not found
    PermissionError
        If a location for the default config file can't be read

    Notes
    -----
    The order of return is as follows, if a ``config_file`` is provided, then
    it is returned (provided it exists). If not then the ``GENOMIC_CONFIG``
    environment variable is checked to see if it is defined. If so, then it is
    returned if it exists. Finally, the root of the ``HOME`` environment
    variable is checked for a file named ``.genomic_data`` with potentially the
    following file extensions ``.cnf``, ``.ini``, ``.cfg`` (in that order). If
    that exists it is returned. If any of the defined paths do not exist then
    the relevant `FileNotFoundError` will be raised.

    Examples
    --------
    get the config file path with a defined path:

    >>> from genomic_config import genomic_config
    >>> cfg = genomic_config.get_config_file("my_config.ini") # doctest: +SKIP
    "my_config.ini"

    get the config file path from a default location, either
    ``~/.genomic_config.ini`` or defined in ``${GENOMIC_CONFIG}``
    environment variable:

    >>> cfg = genomic_config.get_config_file() # doctest: +SKIP
    ".genomic_config.ini"
    """
    if config_file is not None:
        config_file = os.path.abspath(os.path.expanduser(config_file))
        base_open(config_file).close()
        return config_file
    else:
        try:
            # Config is not provided so we attempt to look for a config file
            # environment variable
            config_file = os.environ[base_config.GENOMIC_CONFIG_ENV]
            base_open(config_file).close()
            return config_file
        except KeyError:
            for ext in base_config.GENOMIC_CONFIG_EXTENSIONS:
                config_file = os.path.join(
                    os.environ['HOME'],
                    "{0}{1}".format(
                        base_config.GENOMIC_CONFIG_DEFAULT_NAME,
                        ext
                    )
                )
                config_file = os.path.abspath(os.path.expanduser(config_file))

                try:
                    base_open(config_file).close()
                    return config_file
                except FileNotFoundError:
                    pass
    # f we get here then we done...
    raise FileNotFoundError("can't locate genomic configuration file")


# @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
if __name__ == '__main__':
    main()
