#    Copyright 2013-2018 ARM Limited
#
# 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.
#

# pylint: disable=E1101
import os
import re
import tempfile
import json
from collections import defaultdict

from wa import ApkUiautoWorkload, Parameter
from wa.framework.exception import ConfigError, WorkloadError
from wa.utils.misc import capitalize
from wa.utils.types import version_tuple


class Geekbench(ApkUiautoWorkload):

    name = 'geekbench'
    description = """
    Geekbench provides a comprehensive set of benchmarks engineered to quickly
    and accurately measure processor and memory performance.

    http://www.primatelabs.com/geekbench/
    From the website:
    Designed to make benchmarks easy to run and easy to understand, Geekbench
    takes the guesswork out of producing robust and reliable benchmark results.
    Geekbench scores are calibrated against a baseline score of 1,000 (which is
    the score of a single-processor Power Mac G5 @ 1.6GHz). Higher scores are
    better, with double the score indicating double the performance.

    The benchmarks fall into one of four categories:
        - integer performance.
        - floating point performance.
        - memory performance.
        - stream performance.

    Geekbench benchmarks: http://www.primatelabs.com/geekbench/doc/benchmarks.html
    Geekbench scoring methedology:
    http://support.primatelabs.com/kb/geekbench/interpreting-geekbench-scores
    """
    summary_metrics = ['score', 'multicore_score']

    supported_versions = ['4.4.2', '4.4.0', '4.3.4', '4.3.2', '4.3.1', '4.2.0', '4.0.1', '3.4.1', '3.0.0', '2']
    package_names = ['com.primatelabs.geekbench', 'com.primatelabs.geekbench3', 'ca.primatelabs.geekbench2']

    begin_regex = re.compile(r'^\s*D/WebViewClassic.loadDataWithBaseURL\(\s*\d+\s*\)'
                             r'\s*:\s*(?P<content>\<.*)\s*$')
    replace_regex = re.compile(r'<[^>]*>')

    parameters = [
        Parameter('version', allowed_values=supported_versions,
                  description='Specifies which version of the workload should be run.',
                  override=True),
        Parameter('loops', kind=int, default=1, aliases=['times'],
                  description=('Specfies the number of times the benchmark will be run in a "tight '
                               'loop", i.e. without performaing setup/teardown inbetween.')),
        Parameter('timeout', kind=int, default=3600,
                  description=('Timeout for a single iteration of the benchmark. This value is '
                               'multiplied by ``times`` to calculate the overall run timeout. ')),
        Parameter('disable_update_result', kind=bool, default=False,
                  description=('If ``True`` the results file will not be pulled from the targets '
                               '``/data/data/com.primatelabs.geekbench`` folder.  This allows the '
                               'workload to be run on unrooted targets and the results extracted '
                               'manually later.')),
    ]

    is_corporate = False

    phones_home = True

    requires_network = True

    def initialize(self, context):
        super(Geekbench, self).initialize(context)
        self.gui.uiauto_params['version'] = self.version
        self.gui.uiauto_params['loops'] = self.loops
        self.gui.uiauto_params['is_corporate'] = self.is_corporate
        self.gui.timeout = self.timeout
        if not self.disable_update_result and not self.target.is_rooted:
            raise WorkloadError(
                'Geekbench workload requires root to collect results. '
                'You can set disable_update_result=True in the workload params '
                'to run without collecting results.')

    def setup(self, context):
        super(Geekbench, self).setup(context)
        self.run_timeout = self.timeout * self.loops

    def update_output(self, context):
        super(Geekbench, self).update_output(context)
        if not self.disable_update_result:
            major_version = version_tuple(self.version)[0]
            update_method = getattr(self, 'update_result_{}'.format(major_version))
            update_method(context)

    def validate(self):
        if (self.loops > 1) and (self.version == '2'):
            raise ConfigError('loops parameter is not supported for version 2 of Geekbench.')

    def update_result_2(self, context):
        score_calculator = GBScoreCalculator()
        score_calculator.parse(self.logcat_log)
        score_calculator.update_results(context)

    def update_result_3(self, context):
        outfile_glob = self.target.path.join(self.target.package_data_directory, self.apk.package, 'files', '*gb3')
        on_target_output_files = [f.strip() for f in self.target.execute('ls {}'.format(outfile_glob),
                                                                         as_root=True).split('\n') if f]
        for i, on_target_output_file in enumerate(on_target_output_files):
            host_temp_file = tempfile.mktemp()
            self.target.pull(on_target_output_file, host_temp_file, as_root=True)
            host_output_file = os.path.join(context.output_directory, os.path.basename(on_target_output_file))
            with open(host_temp_file) as fh:
                data = json.load(fh)
            os.remove(host_temp_file)
            with open(host_output_file, 'w') as wfh:
                json.dump(data, wfh, indent=4)
            context.add_artifact('geekout', host_output_file, kind='data',
                                 description='Geekbench 3 output from target.')
            context.add_metric(namemify('score', i), data['score'])
            context.add_metric(namemify('multicore_score', i), data['multicore_score'])
            for section in data['sections']:
                context.add_metric(namemify(section['name'] + '_score', i), section['score'])
                context.add_metric(namemify(section['name'] + '_multicore_score', i),
                                   section['multicore_score'])

    def update_result_4(self, context):
        outfile_glob = self.target.path.join(self.target.package_data_directory, self.apk.package, 'files', '*gb*')
        on_target_output_files = [f.strip() for f in self.target.execute('ls {}'.format(outfile_glob),
                                                                         as_root=True).split('\n') if f]
        for i, on_target_output_file in enumerate(on_target_output_files):
            host_temp_file = tempfile.mktemp()
            self.target.pull(on_target_output_file, host_temp_file, as_root=True)
            host_output_file = os.path.join(context.output_directory, os.path.basename(on_target_output_file))
            with open(host_temp_file) as fh:
                data = json.load(fh)
            os.remove(host_temp_file)
            with open(host_output_file, 'w') as wfh:
                json.dump(data, wfh, indent=4)
            context.add_artifact('geekout', host_output_file, kind='data',
                                 description='Geekbench 4 output from target.')
            context.add_metric(namemify('score', i), data['score'])
            context.add_metric(namemify('multicore_score', i), data['multicore_score'])
            for section in data['sections']:
                context.add_metric(namemify(section['name'] + '_score', i), section['score'])
                for workloads in section['workloads']:
                    workload_name = workloads['name'].replace(" ", "-")
                    context.add_metric(namemify(section['name'] + '_' + workload_name + '_score', i),
                                       workloads['score'])

    update_result_5 = update_result_4


class GBWorkload(object):
    """
    Geekbench workload (not to be confused with WA's workloads). This is a single test run by
    geek bench, such as preforming compression or generating Madelbrot.
    """

    # Index maps onto the hundreds digit of the ID.
    categories = [None, 'integer', 'float', 'memory', 'stream']