#!/usr/bin/env python3

###############################################################################
#                                                                             #
#  Metrics - rEproducible sofTware peRformance analysIs in perfeCt Simplicity #
#  Copyright (c) 2019-2022 - Univ Artois & CNRS, Exakis Nelite                #
#                                                                             #
#  This program is free software: you can redistribute it and/or modify it    #
#  under the terms of the GNU Lesser General Public License as published by   #
#  the Free Software Foundation, either version 3 of the License, or (at your #
#  option) any later version.                                                 #
#                                                                             #
#  This program is distributed in the hope that it will be useful, but        #
#  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY #
#  or FITNESS FOR A PARTICULAR PURPOSE.                                       #
#  See the GNU Lesser General Public License for more details.                #
#                                                                             #
#  You should have received a copy of the GNU Lesser General Public License   #
#  along with this program.                                                   #
#  If not, see <https://www.gnu.org/licenses/>.                               #
#                                                                             #
###############################################################################


"""
This script automates the analysis of a campaign of experiments with Metrics.
"""

from argparse import ArgumentParser
from os import system
from typing import Any, Dict, Tuple

from pyfiglet import Figlet

import metrics
from metrics.scalpel import CampaignParserListener
from metrics.scalpel.config import ScalpelConfigurationLoader
from metrics.scalpel.config.configsaver import ScalpelConfigurationWrapperSaverDecorator

from metrics.studio import ReportBuilder

#############
# FUNCTIONS #
#############
from metrics.studio.scalpelcli import CLIScalpelConfigurationWrapper


def parse_arguments() -> Tuple[ArgumentParser, Dict[str, Any]]:
    """
    Parses the command line arguments.

    :return: The parser for the arguments given to Metrics, and the arguments themselves.
    """
    parser = ArgumentParser(prog=metrics.__name__, description=metrics.__summary__, add_help=False)

    subparser = parser.add_subparsers(help="The commands recognized by this script.",
                                      dest="command")

    parser_command_init = subparser.add_parser("init", help="init command")

    # Registering the option used to switch to the interactive mode.
    parser_command_init.add_argument('-i', '--interactive',
                                     help='switches to the interactive mode',
                                     action='store_true')

    # Registering the option used to specify the root directory for the report.
    parser_command_init.add_argument('-d', '--root-directory',
                                     help='specifies the root directory for the report',
                                     default='.')

    # Registering options about the metadata of the analysis.
    parser_command_init.add_argument('--title', help='the title of the analysis')
    parser_command_init.add_argument('--date', help='the date of the analysis')
    parser_command_init.add_argument('--description', help='the description of the analysis')
    parser_command_init.add_argument('--input-description', help='the description of the input set')
    parser_command_init.add_argument('--xp-ware-description',
                                     help='the description of the experiment-wares')

    # Registering options about the environment used for the experiments.
    parser_command_init.add_argument('--os', help='the description of the operating system')
    parser_command_init.add_argument('--cpu', help='the description of the CPU')
    parser_command_init.add_argument('--ram', help='the available RAM')
    parser_command_init.add_argument('--timeout', help='the time limit')
    parser_command_init.add_argument('--memout', help='the memory limit')

    # Registering options for generating documents.
    parser_command_init.add_argument('--requirements', help='flag for adding the requirements file',
                                     action='store_true')
    parser_command_init.add_argument('--readme', help='flag for adding the README file',
                                     action='store_true')
    parser_command_init.add_argument('--scalpel-config',
                                     help='flag for adding the Scalpel configuration file',
                                     action='store_true')
    parser_command_init.add_argument('--config-file',
                                     help='the name for the Scalpel configuration file',
                                     default='scalpel_config')
    parser_command_init.add_argument('--load', help='flag for adding the loading notebook',
                                     action='store_true')
    parser_command_init.add_argument('--load-notebook', help='the name for the loading notebook',
                                     default='load_experiments')
    parser_command_init.add_argument('--runtime', help='flag for adding the runtime notebook',
                                     action='store_true')
    parser_command_init.add_argument('--runtime-notebook', help='the name for the runtime notebook',
                                     default='runtime_analysis')
    parser_command_init.add_argument('--optimization',
                                     help='flag for adding the optimization notebook',
                                     action='store_true')
    parser_command_init.add_argument('--optimization-notebook',
                                     help='the name for the optimization notebook',
                                     default='optim_analysis')

    # Registering the option for installing Metrics' dependencies.
    parser_command_init.add_argument('--install', help="flag for installing Metrics' dependencies",
                                     action='store_true')

    # Registering the option for initializing a git repository.
    parser_command_init.add_argument('--git', help='flag for initializing a git repository',
                                     action='store_true')

    parser_command_notebook = subparser.add_parser("notebook", help="notebook command")
    parser_command_scalpel = subparser.add_parser("scalpel", help="scalpel command")
    parser_command_help = subparser.add_parser("help", help="help command")
    parser_command_version = subparser.add_parser("version", help="version command")

    # Registering the option used to display the help of the program.
    parser.add_argument('-h', '--help',
                        help='displays the help of Metrics',
                        action='store_true')

    # Registering the option used to display the version of the program.
    parser.add_argument('-v', '--version',
                        help='shows the version of Metrics being executed',
                        action='store_true')

    return parser, vars(parser.parse_args())


def print_header() -> None:
    """
    Displays the header of the program, which shows the name of Metrics with big letters.
    """
    figlet = Figlet(font='slant')
    print(figlet.renderText('Metrics'))


def display_help(parser: ArgumentParser) -> None:
    """
    Displays the help of this script.
    """
    print_header()
    parser.print_help()


def version() -> None:
    """
    Displays the current version of Metrics.
    """
    print_header()
    print('Metrics version', metrics.__version__)
    print('Copyright (c)', metrics.__copyright__)
    print('This program is free software: you can redistribute it and/or modify')
    print('it under the terms of the GNU Lesser General Public License.')


def init(arguments: Dict[str, Any]) -> None:
    """
    Initializes a new report directory.
    """
    if arguments['interactive']:
        # The interactive mode is on: we need to ask the users for details.
        print_header()

    else:
        # The interactive mode is off: we get all details from the command line.
        report = ReportBuilder(arguments['root_directory'])
        report.update_vars(arguments)

        # Creating the report files.
        report.create_directories()
        if arguments['install']:
            report.install()
        if arguments['git']:
            report.git_init()
        if arguments['readme']:
            report.add_readme()
        if arguments['requirements']:
            report.add_requirements()
        if arguments['scalpel_config']:
            report.add_scalpel_config(arguments['config_file'])
        if arguments['load']:
            report.add_load_experiments(arguments['load_notebook'])
        if arguments['runtime']:
            report.add_runtime_analysis(arguments['runtime_notebook'])
        if arguments['optimization']:
            report.add_optim_analysis(arguments['optimization_notebook'])


def notebook(root_directory: str = '.') -> None:
    """
    Runs Jupyter Notebook inside the root directory of the report.

    :param root_directory: The root directory of the report.
    """
    system(f'jupyter notebook --notebook-dir="{root_directory}"')


def scalpel(scalpel_file):
    print_header()
    wrapper = ScalpelConfigurationWrapperSaverDecorator(CLIScalpelConfigurationWrapper(),
                                                        scalpel_file)
    scalpel_config = ScalpelConfigurationLoader(wrapper, CampaignParserListener())
    scalpel_config.load()
    wrapper.save()


################
# MAIN PROGRAM #
################


if __name__ == '__main__':
    # Parsing the command line arguments.
    argument_parser, args = parse_arguments()

    # If the help is asked, we display it and exit.
    if args['help']:
        display_help(argument_parser)
        exit()

    # If the version is asked, we display it and exit.
    if args['version']:
        version()
        exit()

    # Executing the specified Metrics command.
    command = args['command']
    if command == 'help':
        display_help(argument_parser)
    elif command == 'init':
        init(args)
    elif command == 'notebook':
        notebook(args['root_directory'])
    elif command == 'scalpel':
        scalpel(args['config_file'])
    elif command == 'version':
        version()
