# SPDX-License-Identifier: Apache-2.0
#
# Copyright (C) 2015, ARM Limited and contributors.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#


import argparse
import fnmatch as fnm
import json
import math
import numpy as np
import os
import re
import sys
import logging

from collections import defaultdict
from colors import TestColors
from results import Results


# By default compare all the possible combinations
DEFAULT_COMPARE = [(r'base_', r'test_')]

class Report(object):


    def __init__(self, results_dir, compare=None, formats=['relative']):
        self.results_json = results_dir + '/results.json'
        self.results = {}

        self.compare = []

        # Setup logging
        self._log = logging.getLogger('Report')

        # Parse results (if required)
        if not os.path.isfile(self.results_json):
            Results(results_dir)

        # Load results from file (if already parsed)
        self._log.info('Load results from [%s]...',
                       self.results_json)
        with open(self.results_json) as infile:
           self.results = json.load(infile)

        # Setup configuration comparisons
        if compare is None:
            compare = DEFAULT_COMPARE
            self._log.warning('Comparing all the possible combination')
        for (base_rexp, test_rexp) in compare:
            self._log.info('Configured regexps for comparisions '
                           '(bases , tests): (%s, %s)',
                           base_rexp, test_rexp)
            base_rexp = re.compile(base_rexp, re.DOTALL)
            test_rexp = re.compile(test_rexp, re.DOTALL)
            self.compare.append((base_rexp, test_rexp))

        # Report all supported workload classes
        self.__rtapp_report(formats)
        self.__default_report(formats)

    ############################### REPORT RTAPP ###############################

    def __rtapp_report(self, formats):

        if 'rtapp' not in self.results.keys():
            self._log.debug('No RTApp workloads to report')
            return

        self._log.debug('Reporting RTApp workloads')

        # Setup lables depending on requested report
        if 'absolute' in formats:
            nrg_lable = 'Energy Indexes (Absolute)'
            prf_lable = 'Performance Indexes (Absolute)'
            self._log.info('')
            self._log.info('Absolute comparisions:')
            print ''
        else:
            nrg_lable = 'Energy Indexes (Relative)'
            prf_lable = 'Performance Indexes (Relative)'
            self._log.info('')
            self._log.info('Relative comparisions:')
            print ''

        # Dump headers
        print '{:13s}   {:20s} |'\
                ' {:33s} | {:54s} |'\
                .format('Test Id', 'Comparision',
                        nrg_lable, prf_lable)
        print '{:13s}   {:20s} |'\
                ' {:>10s} {:>10s} {:>10s}  |'\
                ' {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} |'\
                .format('', '',
                        'LITTLE', 'big', 'Total',
                        'PerfIndex', 'NegSlacks', 'EDP1', 'EDP2', 'EDP3')

        # For each test
        _results = self.results['rtapp']
        for tid in sorted(_results.keys()):
            new_test = True
            # For each configuration...
            for base_idx in sorted(_results[tid].keys()):
                # Which matches at least on base regexp
                for (base_rexp, test_rexp) in self.compare:
                    if not base_rexp.match(base_idx):
                        continue
                    # Look for a configuration which matches the test regexp
                    for test_idx in sorted(_results[tid].keys()):
                        if test_idx == base_idx:
                            continue
                        if new_test:
                            print '{:-<37s}+{:-<35s}+{:-<56s}+'\
                                    .format('','', '')
                            self.__rtapp_reference(tid, base_idx)
                            new_test = False
                        if test_rexp.match(test_idx) == None:
                            continue
                        self.__rtapp_compare(tid, base_idx, test_idx, formats)

        print ''

    def __rtapp_reference(self, tid, base_idx):
        _results = self.results['rtapp']

        self._log.debug('Test %s: compare against [%s] base',
                        tid, base_idx)
        res_line = '{0:12s}: {1:22s} | '.format(tid, base_idx)

        # Dump all energy metrics
        for cpus in ['LITTLE', 'big', 'Total']:
            res_base = _results[tid][base_idx]['energy'][cpus]['avg']
            # Dump absolute values
            res_line += ' {0:10.3f}'.format(res_base)
        res_line += ' |'

        # If available, dump also performance results
        if 'performance' not in _results[tid][base_idx].keys():
            print res_line
            return

        for pidx in ['perf_avg', 'slack_pct', 'edp1', 'edp2', 'edp3']:
            res_base = _results[tid][base_idx]['performance'][pidx]['avg']

            self._log.debug('idx: %s, base: %s', pidx, res_base)

            if pidx in ['perf_avg']:
                res_line += ' {0:s}'.format(TestColors.rate(res_base))
                continue
            if pidx in ['slack_pct']:
                res_line += ' {0:s}'.format(
                        TestColors.rate(res_base, positive_is_good = False))
                continue
            if 'edp' in pidx:
                res_line += ' {0:10.2e}'.format(res_base)
                continue
        res_line += ' |'
        print res_line

    def __rtapp_compare(self, tid, base_idx, test_idx, formats):
        _results = self.results['rtapp']

        self._log.debug('Test %s: compare %s with %s',
                        tid, base_idx, test_idx)
        res_line = '{0:12s}:   {1:20s} | '.format(tid, test_idx)

        # Dump all energy metrics
        for cpus in ['LITTLE', 'big', 'Total']:
            res_base = _results[tid][base_idx]['energy'][cpus]['avg']
            res_test = _results[tid][test_idx]['energy'][cpus]['avg']
            speedup_cnt =  res_test - res_base
            if 'absolute' in formats:
                res_line += ' {0:10.2f}'.format(speedup_cnt)
            else:
                speedup_pct = 0
                if res_base != 0:
                    speedup_pct =  100.0 * speedup_cnt / res_base
                res_line += ' {0:s}'\
                        .format(TestColors.rate(
                            speedup_pct,
                            positive_is_good = False))
        res_line += ' |'

        # If available, dump also performance results
        if 'performance' not in _results[tid][base_idx].keys():
            print res_line
            return

        for pidx in ['perf_avg', 'slack_pct', 'edp1', 'edp2', 'edp3']:
            res_base = _results[tid][base_idx]['performance'][pidx]['avg']
            res_test = _results[tid][test_idx]['performance'][pidx]['avg']

            self._log.debug('idx: %s, base: %s, test: %s',
                            pidx, res_base, res_test)

            if pidx in ['perf_avg']:
                res_line += ' {0:s}'.format(TestColors.rate(res_test))
                continue

            if pidx in ['slack_pct']:
                res_line += ' {0:s}'.format(
                        TestColors.rate(res_test, positive_is_good = False))
                continue

            # Compute difference base-vs-test
            if 'edp' in pidx:
                speedup_cnt = res_base - res_test
                if 'absolute':
                    res_line += ' {0:10.2e}'.format(speedup_cnt)
                else:
                    res_line += ' {0:s}'.format(TestColors.rate(speedup_pct))

        res_line += ' |'
        print res_line

    ############################### REPORT DEFAULT #############################

    def __default_report(self, formats):

        # Build list of workload types which can be rendered using the default parser
        wtypes = []
        for supported_wtype in DEFAULT_WTYPES:
            if supported_wtype in self.results.keys():
                wtypes.append(supported_wtype)

        if len(wtypes) == 0:
            self._log.debug('No Default workloads to report')
            return

        self._log.debug('Reporting Default workloads')

        # Setup lables depending on requested report
        if 'absolute' in formats:
            nrg_lable = 'Energy Indexes (Absolute)'
            prf_lable = 'Performance Indexes (Absolute)'
            self._log.info('')
            self._log.info('Absolute comparisions:')
            print ''
        else:
            nrg_lable = 'Energy Indexes (Relative)'
            prf_lable = 'Performance Indexes (Relative)'
            self._log.info('')
            self._log.info('Relative comparisions:')
            print ''

        # Dump headers
        print '{:9s}   {:20s} |'\
                ' {:33s} | {:54s} |'\
                .format('Test Id', 'Comparision',
                        nrg_lable, prf_lable)
        print '{:9s}   {:20s} |'\
                ' {:>10s} {:>10s} {:>10s}  |'\
                ' {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} |'\
                .format('', '',
                        'LITTLE', 'big', 'Total',
                        'Perf', 'CTime', 'EDP1', 'EDP2', 'EDP3')

        # For each default test
        for wtype in wtypes:
            _results = self.results[wtype]
            for tid in sorted(_results.keys()):
                new_test = True
                # For each configuration...
                for base_idx in sorted(_results[tid].keys()):
                    # Which matches at least on base regexp
                    for (base_rexp, test_rexp) in self.compare:
                        if not base_rexp.match(base_idx):
                            continue
                        # Look for a configuration which matches the test regexp
                        for test_idx in sorted(_results[tid].keys()):
                            if test_idx == base_idx:
                                continue
                            if new_test:
                                print '{:-<37s}+{:-<35s}+{:-<56s}+'\
                                        .format('','', '')
                                new_test = False
                            if not test_rexp.match(test_idx):
                                continue
                            self.__default_compare(wtype, tid, base_idx, test_idx, formats)

        print ''

    def __default_compare(self, wtype, tid, base_idx, test_idx, formats):
        _results = self.results[wtype]

        self._log.debug('Test %s: compare %s with %s',
                        tid, base_idx, test_idx)
        res_comp = '{0:s} vs {1:s}'.format(test_idx, base_idx)
        res_line = '{0:8s}: {1:22s} | '.format(tid, res_comp)

        # Dump all energy metrics
        for cpus in ['LITTLE', 'big', 'Total']:

            # If either base of test have a 0 MAX energy, this measn that
            # energy has not been collected
            base_max = _results[tid][base_idx]['energy'][cpus]['max']
            test_max = _results[tid][test_idx]['energy'][cpus]['max']
            if base_max == 0 or test_max == 0:
                res_line += ' {0:10s}'.format('NA')
                continue

            # Otherwise, report energy values
            res_base = _results[tid][base_idx]['energy'][cpus]['avg']
            res_test = _results[tid][test_idx]['energy'][cpus]['avg']

            speedup_cnt =  res_test - res_base
            if 'absolute' in formats:
                res_line += ' {0:10.2f}'.format(speedup_cnt)
            else:
                speedup_pct =  100.0 * speedup_cnt / res_base
                res_line += ' {0:s}'\
                        .format(TestColors.rate(
                            speedup_pct,
                            positive_is_good = False))
        res_line += ' |'

        # If available, dump also performance results
        if 'performance' not in _results[tid][base_idx].keys():
            print res_line
            return

        for pidx in ['perf_avg', 'ctime_avg', 'edp1', 'edp2', 'edp3']:
            res_base = _results[tid][base_idx]['performance'][pidx]['avg']
            res_test = _results[tid][test_idx]['performance'][pidx]['avg']

            self._log.debug('idx: %s, base: %s, test: %s',
                            pidx, res_base, res_test)

            # Compute difference base-vs-test
            speedup_cnt = 0
            if res_base != 0:
                if pidx in ['perf_avg']:
                    speedup_cnt =  res_test - res_base
                else:
                    speedup_cnt =  res_base - res_test

            # Compute speedup if required
            speedup_pct = 0
            if 'absolute' in formats:
                if 'edp' in pidx:
                    res_line += ' {0:10.2e}'.format(speedup_cnt)
                else:
                    res_line += ' {0:10.2f}'.format(speedup_cnt)
            else:
                if res_base != 0:
                    if pidx in ['perf_avg']:
                        # speedup_pct =  100.0 * speedup_cnt / res_base
                        speedup_pct =  speedup_cnt
                    else:
                        speedup_pct =  100.0 * speedup_cnt / res_base
                res_line += ' {0:s}'.format(TestColors.rate(speedup_pct))
        res_line += ' |'
        print res_line

# List of workload types which can be parsed using the default test parser
DEFAULT_WTYPES = ['perf_bench_messaging', 'perf_bench_pipe']

#vim :set tabstop=4 shiftwidth=4 expandtab