#!/usr/bin/env python3

import logging
import logging.config
import argparse
from pathlib import Path

import imaging_transcriptomics
from imaging_transcriptomics import reporting


def get_args():
    """Return the args parsed from the command line.

    Reads the command line and parses the possible inputs for a match. The available inputs are:
    * input: path of the input file to analyze.
    * output (optional): path where to save the results.
    * ncomp: number of components to use for PLS regression.
    * variance: percentage of variance to extract by the PLS components.

    :return: Data structure with all args parsed from the command line.
    """
    DESCRIPTION = (
        """Perform imaging transcriptomics analysis on a neuroimaging scan. """
    )
    EPILOG = """Check your results in the specified folder or in the file path of the input scan, if you have not 
    specified an output path. If you used this software in your research please cite: 
    * Imaging transcriptomics: **Convergent cellular, transcriptomic, and molecular neuroimaging signatures in the 
    healthy adult human brain.**
    *Daniel Martins, Alessio Giacomel, Steven CR Williams, Federico E Turkheimer, Ottavia Dipasquale, Mattia Veronese, 
    PET templates working group*; bioRxiv 2021.06.18.448872; doi: `https://doi.org/10.1101/2021.06.18.448872 
    <https://doi.org/10.1101/2021.06.18.448872>`_ """

    parser = argparse.ArgumentParser(description=DESCRIPTION, epilog=EPILOG)

    parser.add_argument(
        "-i",
        "--input",
        type=str,
        help=(
            "Input imaging file in NIfTI format (.nii, .nii.gz).\n"
            "The input file is expected to have the same matrix size as the atlas used (182x218x182),"
            "if the input image has different matrix size this can be resliced to match the"
            "resolution of the MNI152 1mm template matrix size (e.g. the one provided with FSL)."
        ),
        required=True,
    )
    parser.add_argument(
        "-o",
        "--out",
        type=str,
        help="Path where to save the output, if not specified the path of the path of the input scan "
        "is used.",
        required=False,
    )
    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument(
        "-n",
        "--ncomp",
        type=int,
        help="Number of PLS components to use. The number of components has to be between 1 and 15.",
    )
    group.add_argument(
        "-v",
        "--variance",
        help="""Variance explained by the components. The variance input should be between 10 and 
                       100, and the program will select the number of components that explain a variance closest to 
                       the desired (with the explained variance used as a minimum). """,
    )
    group.add_argument("--corr", action="store_true", help="run with the correlation "
                                                            "method")
    verbosity = parser.add_mutually_exclusive_group(required=False)
    verbosity.add_argument(
        "--suppress",
        action="store_true",
        help="Suppress the log on console. Only shows WARNINGS if present.",
    )
    verbosity.add_argument(
        "--verbose",
        action="store_true",
        help="Show all logging messages from the script.",
    )
    return parser.parse_args()


def set_log_level(inputs):
    """Set the logging level based on the input form the user.

    :param inputs: Inputs read from the CLI (from argparse.parse_args())

    :return logger: Logger to use for the script execution.
    """
    if inputs.verbose:
        logger_name = "verbose"
    elif inputs.suppress:
        logger_name = "suppress"
    else:
        logger_name = "info"
    return logger_name


def main():

    inputs = get_args()
    logger_name = set_log_level(inputs)
    logger = logging.getLogger(logger_name)
    input_path = Path(inputs.input)
    data_to_analyse = imaging_transcriptomics.inputs.extract_average(
        imaging_transcriptomics.inputs.read_scan(input_path)
    )
    # Don't check inputs as checks are in the initialization of the analysis!
    n_comp = 1 if inputs.corr else inputs.ncomp
    initial_dict = {"variance": inputs.variance, "n_components": n_comp}

    # Get IO paths to save files
    if not inputs.out:
        save_dir = input_path.absolute().parent
    else:
        save_dir = Path(inputs.out)
    scan_name = input_path.name.split(".")[0]

    save_dir = reporting.make_folder(save_dir, f"Imt_{scan_name}")
    file_handler = logging.FileHandler(save_dir / "logs.log", "a")
    logger.addHandler(file_handler)
    logger.info("Setting up analysis ...")
    analysis = imaging_transcriptomics.ImagingTranscriptomics(
        data_to_analyse, **initial_dict
    )
    logger.info("Running analysis.")
    _method = "corr" if inputs.corr else "pls"
    analysis.run(method=_method)

    # Save the results
    if not inputs.corr:
        reporting.make_plots(save_dir, analysis.n_components, analysis.var_components)
        reporting.create_pdf(input_path, save_dir)
    reporting.create_csv(analysis.gene_results, analysis.n_components, save_dir)


if __name__ == "__main__":
    main()
