import logging
import sys
import os
import json
from collections import namedtuple, OrderedDict

import numpy as np
import pandas as pd

import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

from plottingscripts.plotting.scatter import plot_scatter_plot

from aslib_scenario.aslib_scenario import ASlibScenario

from asapy.perf_analysis.perf_analysis import PerformanceAnalysis
from asapy.feature_analysis.feature_analysis import FeatureAnalysis
from asapy.out_builder.html_builder import HTMLBuilder

__author__ = "Marius Lindauer"
__copyright__ = "Copyright 2016, ML4AAD"
__license__ = "MIT"
__email__ = "lindauer@cs.uni-freiburg.de"


class ASAPy(object):

    def __init__(self,
                 output_dn: str=".",
                 plot_log_perf: bool=False):
        '''
        Constructor

        Arguments
        ---------
        output_dn:str
            output directory name
        '''
        self.logger = logging.getLogger("ASAPy")

        self.scenario = None
        self.output_dn = output_dn
        self.plot_log_perf = plot_log_perf

        if not os.path.isdir(self.output_dn):
            os.mkdir(self.output_dn)

    def read_scenario_ASlib(self, scenario_dn: str):
        '''
        Read scenario from ASlib format

        Arguments
        ---------
        scenario_dn: str
            Scenario directory name 
        '''

        self.scenario = ASlibScenario()
        self.scenario.read_scenario(dn=scenario_dn)

    def read_scenario_CSV(self, csv_data: namedtuple):
        '''
        Read scenario from ASlib format

        Arguments
        ---------
        csv_data: namedtuple
            namedtuple with the following fields: "perf_csv", "feat_csv", "obj", "cutoff", "maximize", "cv_csv" 
            "cv_csv" can be None
        '''
        self.scenario = ASlibScenario()
        self.scenario.read_from_csv(perf_fn=csv_data.perf_csv,
                                    feat_fn=csv_data.feat_csv,
                                    objective=csv_data.obj,
                                    runtime_cutoff=csv_data.cutoff,
                                    maximize=csv_data.maximize,
                                    cv_fn=csv_data.cv_csv)

    def get_default_config(self):
        '''
            get default configuration which enables all plots

            Returns
            -------
            dict
        '''
        config = {"Performance Analysis": {"Status bar plot": True,
                                           "Box plot": True,
                                           "Violin plot": True,
                                           "CDF plot": True,
                                           "Scatter plots": True,
                                           "Correlation plot": True,
                                           "Contribution of algorithms": True,
                                           "Critical Distance Diagram": True,
                                           "Footprints": True,
                                           "Instance Hardness": True,
                                           "Baselines": True
                                           },
                  "Feature Analysis": {"Status Bar Plot": True,
                                       "Violin and box plots": True,
                                       "Correlation plot": True,
                                       "Feature importance": True,
                                       "Clustering": True,
                                       "CDF plot on feature costs": True
                                       }
                  }

        return config

    def print_config(self):
        '''
            generate template for config file
        '''
        print(json.dumps(self.get_default_config(), indent=2))

    def load_config(self, fn: str):
        '''
            load config from file

            Arguments
            ---------
            fn: str
                file name with config in json format

            Returns
            -------
            config: dict
        '''
        with open(fn) as fp:
            config = json.load(fp)

        return config

    def main(self, config: dict, max_algos: int=20,
             only_fold:int=None):
        '''
            main method

            Arguments
            ---------
            config: dict
                configuration that enables or disables plots
            max_algos: int
                maximum number of algos to consider; 
                if more are available, we take the n best algorithm on average performance
            only_fold: int
                use only the given <only_fold> cv-fold data for analyze
        '''
        
        if only_fold:
            self.logger.info("Using only test data from %d cv-split" %(only_fold))
            _, self.scenario = self.scenario.get_split(only_fold)

        n_prev_algos = None
        if self.scenario is None:
            raise ValueError(
                "Please first read in Scenario data; use scenario input or csv input")

        if self.scenario.performance_type[0] == "solution_quality" and self.scenario.maximize[0]:
            # revoke inverting the performance as done in the scenario reader
            self.scenario.performance_data *= -1
            self.logger.info("Revoke * -1 on performance data")

        if len(self.scenario.algorithms) > max_algos:
            n_prev_algos = len(self.scenario.algorithms)
            self.logger.warning(
                "We reduce the algorithms to the greedy selected VBS-improving algorithms (at most %d)" % (max_algos))
            pa = PerformanceAnalysis(output_dn=self.output_dn,
                                     scenario=self.scenario)

            algos_score = pa.reduce_algos(max_algos=max_algos)
            best_algos = [a[0] for a in algos_score] 
            self.scenario.algorithms = best_algos