"""Module with class that handle performance test results and compute statistic."""
import csv


class PerfTests:
    """Class that handle performance test results."""

    INPUT_FILES = {
        "component analysis": {
            "sequenced_calls_known_component":
                "component_analysis_sequenced_calls_known_component.csv",
            "sequenced_calls_unknown_component":
                "component_analysis_sequenced_calls_unknown_component.csv",
            "parallel_calls_known_component":
                "component_analysis_parallel_calls_known_component.csv",
            "parallel_calls_unknown_component":
                "component_analysis_parallel_calls_unknown_component.csv",
        },
        "stack analysis": {
            "sequenced_calls": "stack_analysis_sequenced_calls.csv",
            "parallel_calls": "stack_analysis_parallel_calls.csv"
        }
    }

    @staticmethod
    def read_csv(filename, skip_first_line=False):
        """Read the given CSV file, parse it, and return as list of records."""
        output = []
        # it is expected that the input CSV might not exists at the beginning
        try:
            with open(filename, 'r') as fin:
                csv_content = csv.reader(fin, delimiter=',')
                if skip_first_line:
                    next(csv_content, None)
                for row in csv_content:
                    output.append(row)
        except Exception as e:
            print("Unable to load CSV file", e)
        return output

    def __init__(self):
        """Construct an instance of the class."""
        self._results = {}
        self._statistic = {}

    def read_analysis_results(self, input_files):
        """Read the performence test results from the selected bundle of CSV files.

        Typically the bundle would be 'stack analysis' or 'component analysis').
        """
        results = {}
        for name, filename in input_files.items():
            results[name] = PerfTests.read_csv(filename)
        return results

    def read_results(self):
        """Read results generated by all performance tests."""
        for analyse_type, input_files in PerfTests.INPUT_FILES.items():
            self._results[analyse_type] = self.read_analysis_results(input_files)

    @staticmethod
    def compute_stat_for_result_set(results):
        """Compute the statistic for selected result set (that is - for 2D matrix of floats)."""
        # let make statistic for all columns, even for columns where it does not
        # make much sense to do so (like the 1st column that might contain run #)
        COLUMN_MIN_VALUES = 2
        COLUMN_MAX_VALUES = 3
        COLUMN_AVG_VALUES = 4

        n = len(results)
        # don't fail when no results are computed (yet)
        if n == 0:
            return
        columns = len(results[0])

        # initial values to be updated later
        sum_val = [0] * columns
        avg_val = [None] * columns
        max_val = [float("-inf")] * columns
        min_val = [float("inf")] * columns

        # compute min, max, and sum
        for result in results:
            for column in range(columns):
                r = float(result[column])
                sum_val[column] += r
                if r < min_val[column]:
                    min_val[column] = r
                if max_val[column] < r:
                    max_val[column] = r

        # really don't wanna to divide by zero
        if n > 0:
            for column in range(columns):
                avg_val[column] = sum_val[column] / n

        if columns == 1:
            return {
                "sum": sum_val[0],
                "avg": avg_val[0],
                "min": min_val[0],
                "max": max_val[0],
            }
        else:
            return {
                "sum": sum_val[COLUMN_MAX_VALUES],
                "avg": avg_val[COLUMN_AVG_VALUES],
                "min": min_val[COLUMN_MIN_VALUES],
                "max": max_val[COLUMN_MAX_VALUES],
            }

    def compute_statistic_for_analysis(self, results):
        """Compute the basic statistic (min, max, sum, avg) for the selected set of results."""
        stat = {}
        for key, results in results.items():
            stat[key] = PerfTests.compute_stat_for_result_set(results)
        return stat

    def compute_statistic(self):
        """Compute statistic for all results generated by performance tests."""
        for analyse in PerfTests.INPUT_FILES.keys():
            self._statistic[analyse] = self.compute_statistic_for_analysis(self._results[analyse])

    @property
    def results(self):
        """Getter for the 'results' attribute."""
        return self._results

    @property
    def statistic(self):
        """Getter for the 'statistic' attribute."""
        return self._statistic


if __name__ == "__main__":
    # execute only if run as a script
    perf_tests = PerfTests()
    perf_tests.read_results()
    perf_tests.compute_statistic()
    print("Results:")
    print(perf_tests.results)
    print("-----------------------------------------")
    print("Statistic:")
    print(perf_tests.statistic)
