#!python

# External imports
import argparse
import os
import sys


# Internal imports

MODES = ['classifiers', 'regressors']


def dir(dir_name):
    dir_name = dir_name.strip('"')
    if not os.path.isdir(dir_name):
        raise ValueError(f"Directory {dir_name} does not exist.")
    return os.path.abspath(dir_name)


def file(file_name):
    file_name = file_name.strip('"')
    if not os.path.isfile(file_name):
        raise ValueError(f"File {file_name} does not exist.")
    return os.path.abspath(file_name)


class ExhauFS(object):
    def __init__(self):
        # Create new parser of script arguments
        parser = argparse.ArgumentParser(
            usage='exhaufs [-h] <command> [<args>]',
            description="""
            Exhaustive features selector
            
            Available exhaufs commands are:
              build       Build feature selector pipeline
              estimate    Estimate running time of a pipeline
              summary     Get summary of a model
              km_plot     Plot Kaplan-Maier curve for the specified classifier
            """,
            formatter_class=argparse.RawDescriptionHelpFormatter)

        # Read the first positional argument defining a command
        parser.add_argument('command', metavar='command',
                            type=str, choices=['build', 'estimate', 'summary', 'km_plot'],
                            help='Subcommand to run')
        args = parser.parse_args(sys.argv[1:2])

        # Read arguments for a given command
        getattr(self, args.command)()

    def common_args(self, parser):
        parser.add_argument('-c', '--config', metavar='path',
                            type=file, default="./config.json",
                            help='Configuration file; Default: %(default)s.')

        # Example of bool option
        # parser.add_argument('--opt', action='store_true', help='Option description.')

    def build(self):
        # Create new parser for build arguments
        parser = argparse.ArgumentParser(
            prog='exhaufs build',
            description="""
            Build feature selectors

            Available building modes are:
              classifiers    Build tuples of features using predictive classification model
              regressors     Build tuples of features using predictive regression model
            """,
            formatter_class=argparse.RawDescriptionHelpFormatter)

        # Add common options
        self.common_args(parser)

        # Add build options
        parser.add_argument('mode', metavar='mode',
                            type=str, choices=MODES,
                            help='Building mode')

        # Parser build options
        args = parser.parse_args(sys.argv[2:])

        # Run builder
        if args.mode == 'classifiers':
            from src import build_classifiers
            build_classifiers.main(args.config)

        elif args.mode == 'regressors':
            from src import build_regressors
            build_regressors.main(args.config)

    def estimate(self):
        # Create new parser for estimate arguments
        parser = argparse.ArgumentParser(
            prog='exhaufs estimate',
            description="""
            Estimate pipeline running time

            Available estimating modes are:
              classifiers    Search the maximal number of selected features for which 
                       estimated classification pipeline running time is less than max_estimated_time.
                       Pipeline running time is calculated using bounded number of
                       feature subsets.
              regressors     Search the maximal number of selected features for which 
                       estimated regression pipeline running time is less than max_estimated_time.
                       Pipeline running time is calculated using bounded number of
                       feature subsets.
            """,
            formatter_class=argparse.RawDescriptionHelpFormatter)

        # Add common options
        self.common_args(parser)

        # Add estimate options
        parser.add_argument('mode', metavar='mode',
                            type=str, choices=MODES,
                            help='Estimating mode')
        parser.add_argument('--max_k', metavar='<num>',
                            type=int, default=100,
                            help='Maximal length of features subset; Default: %(default)s.')
        parser.add_argument('--max_estimated_time', metavar='<time>',
                            type=float, default=24,
                            help='Maximal estimated time of single pipeline running in hours; ' \
                                 'Default: %(default)s.')
        parser.add_argument('--n_feature_subsets', metavar='<num>',
                            type=int, default=100,
                            help='Number of processed feature subsets; Default: %(default)s.')

        # Parser estimate options
        args = parser.parse_args(sys.argv[2:])

        # Run estimator
        from src import running_time_estimator
        running_time_estimator.main(
            args.config,
            args.max_k,
            args.max_estimated_time,
            args.n_feature_subsets,
            args.mode == 'regressors',
        )

    def km_plot(self):
        # Create new parser for summary arguments
        parser = argparse.ArgumentParser(
            prog='exhaufs km_plot',
            description="""
            Plot Kaplan-Maier curve for the specified classifier
            """,
            formatter_class=argparse.RawDescriptionHelpFormatter)

        # Add common options
        self.common_args(parser)

        # Parser summary options
        args = parser.parse_args(sys.argv[2:])

        from src import make_km_plot_for_classifier
        make_km_plot_for_classifier.main(args.config)

    def summary(self):
        # Create new parser for summary arguments
        parser = argparse.ArgumentParser(
            prog='exhaufs summary',
            description="""
            Get summary

            Available summary modes are:
              classifiers    Fit and evaluate classifier, generate report of its accuracy 
                             metrics for datasets, plot its features importances (only for 
                             SVM or RF classifier), and save trained model to file.
              regressors     Fit and evaluate regressor, generate report of its accuracy 
                             metrics for datasets, plot Kaplan-Meier curves, and save trained model to file. 
            """,
            formatter_class=argparse.RawDescriptionHelpFormatter)

        # Add common options
        self.common_args(parser)

        # Add summary options
        parser.add_argument('mode', metavar='mode',
                            type=str, choices=MODES,
                            help='Summary mode')

        # Parser summary options
        args = parser.parse_args(sys.argv[2:])

        # Get summary
        if args.mode == 'classifiers':
            from src import make_classifier_summary
            make_classifier_summary.main(args.config)
        elif args.mode == 'regressors':
            from src import make_regressor_summary
            make_regressor_summary.main(args.config)


if __name__ == '__main__':
    ExhauFS()
