"""Actev module

Actev modules are used to parse actev commands in order to get arguments
before calling associated entry point methods to execute systems.

Warning: this file should not be modified: see src/entry_points to add your
source code.
"""

import abc
import logging
import sys
import os

from diva_evaluation_cli.bin.private_src.implementation.utils.\
    actev_cli_environment import activate_environment
from diva_evaluation_cli.bin.private_src.implementation.resources_monitoring.\
    monitor import Monitor
from diva_evaluation_cli.bin.private_src.implementation.status.status_factory \
    import StatusFactory


class ActevCommand():
    """ Abstract class that represents an actev command.

    Every actev modules must inherit from this class and
    implement the following methods

    Attributes:
        command (str): Name of the actev command
        entry_point (function): Python function that represents an entry point
        before_entry_point (function, optional): Python function that should
            be executed before entry_point method
        after_entry_point (function, optional): Python function that should be
            executed after entry_point method
    """
    __metaclass__ = abc.ABCMeta

    def __init__(self, command, entry_point, before_entry_point=None,
                 after_entry_point=None):
        """
        Args:
            command (str): Name of the actev command
            entry_point (function): Python function that represents an entry
                point
            before_entry_point (function, optional): Python function that
                should be executed before entry_point method
            after_entry_point (function, optional): Python function that
                should be executed after entry_point method
        """
        self.command = command
        self.entry_point = entry_point
        self.before_entry_point = before_entry_point
        self.after_entry_point = after_entry_point

    @abc.abstractmethod
    def cli_parser(self, arg_parser):
        """Configure the description and the arguments (positional and
        optional) to parse.

        Args:
            arg_parser(:obj:`ArgParser`): Python arg parser to describe how to
                parse the command
        """
        return

    def before_command(self, args):
        """Execute an action before executing the command

        Args:
            args (:obj:`dict`): contains the arguments passed during the actev
                command call
        """
        if self.before_entry_point:
            self.before_entry_point(**args.__dict__)

    def after_command(self, args):
        """ Execute an action after executing the command

        Args:
            args (:obj:`dict`): contains the arguments passed during the actev
                command call
        """
        if self.after_entry_point:
            self.after_entry_point(**args.__dict__)

    def command(self, args):
        """Gets arguments and passe them to an entry point. Catch the
        exception occured.

        Args:
            args (:obj:`dict`): contains the arguments passed during the actev
                command call
        """
        del args.__dict__['object']
        del args.__dict__['func']

        try:
            logging.info("Starting %s" % self.command)

            logging.debug('Loading environment')
            activate_environment()

            if os.getenv('ACTEV_SYSTEM_SETUP_DEV') == "1":
                self.entry_point(**args.__dict__)
            else:
                StatusFactory.generate_status(self, 'start', args.__dict__)

                self.before_command(args)
                mon = Monitor(self.entry_point, args, self.command)
                mon.run_monitor()
                self.after_command(args)

            logging.info("%s done" % self.command)
            StatusFactory.generate_status(self, 'done', args.__dict__)
        except KeyboardInterrupt:
            logging.info("Ctrl-C detected. Exiting.")
            sys.exit(0)
        except Exception as e:
            # Necessary for testing purposes
            if not isinstance(e, NotImplementedError):
                logging.exception("Issue during %s" % self.command)
                StatusFactory.generate_status(self, 'issue', args.__dict__)
                sys.exit(1)
            raise NotImplementedError
