# Copyright 2017 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import importlib
import logging
import os
import re

import yaml

from autotest_lib.client.common_lib import error

class DeviceCapability(object):
    """
    Generate capabilities status on DUT from yaml files in a given path.
    Answer from the capabilities whether some capability is satisfied on DUT.
    """

    def __init__(self, settings_path='/usr/local/etc/autotest-capability'):
        """
        @param settings_path: string, the base directory for autotest
                              capability. There should be yaml files.
        """
        self.capabilities = self.__get_autotest_capability(settings_path)
        logging.info("Capabilities:\n%r", self.capabilities)


    def __get_autotest_capability(self, settings_path):
        """
        Generate and summarize capabilities from yaml files in
        settings_path with detectors.

        @param settings_path: string, the base directory for autotest
                              capability. There should be yaml files.
        @returns dict:
            The capabilities on DUT.
            Its key is string denoting a capability. Its value is 'yes', 'no' or
            'disable.'
        """

        def run_detector(name):
            """
            Run a detector in the detector directory. (i.e.
            autotest/files/client/cros/video/detectors)
            Return the result of the detector.

            @param name: string, the name of running detector.
            @returns string, a result of detect() in the detector script.
            """
            if name not in detect_results:
                detector = importlib.import_module(
                    "autotest_lib.client.cros.video.detectors.%s"
                    % name)
                detect_results[name] = detector.detect()
                logging.info("Detector result (%s): %s",
                             name, detect_results[name])
            return detect_results[name]

        managed_cap_fpath = os.path.join(settings_path,
                                         'managed-capabilities.yaml')
        if not os.path.exists(managed_cap_fpath):
            raise error.TestFail("%s is not installed" % managed_cap_fpath)
        managed_caps = yaml.load(file(managed_cap_fpath))

        cap_files = [f for f in os.listdir(settings_path)
                     if re.match(r'^[0-9]+-.*\.yaml$', f)]
        cap_files.sort(key=lambda f: int(f.split('-', 1)[0]))

        detect_results = {}
        autotest_caps = dict.fromkeys(managed_caps, 'no')
        for fname in cap_files:
            logging.debug('Processing caps: %s', fname)
            fname = os.path.join(settings_path, fname)
            for rule in yaml.load(file(fname)):
                # The type of rule is string or dict
                # If the type is a string, it is a capability (e.g. webcam).
                # If a specific condition (e.g. kepler, cpu type) is required,
                # rule would be dict, for example,
                # {'detector': 'intel_cpu',
                #  'match': ['intel_celeron_1007U'],
                #  'capabilities': ['no hw_h264_enc_1080_30'] }.
                logging.debug("%r", rule)
                caps = []
                if isinstance(rule, dict):
                    if run_detector(rule['detector']) in rule['match']:
                        caps = rule['capabilities']
                else:
                    caps = [rule]

                for capability in caps:
                    m = re.match(r'(?:(disable|no)\s+)?([\w\-]+)$', capability)
                    prefix, capability = m.groups()
                    if capability in managed_caps:
                        autotest_caps[capability] = prefix or 'yes'
                    else:
                        raise error.TestFail(
                            "Unexpected capability: %s" % capability)

        return autotest_caps


    def get_managed_caps(self):
        return self.capabilities.keys()


    def get_capability_results(self):
        return self.capabilities


    def get_capability(self, cap):
        """
        Decide if a device satisfies a required capability for an autotest.

        @param cap: string, denoting one capability. It must be one in
                    settings_path + 'managed-capabilities.yaml.'
        @returns 'yes', 'no', or 'disable.'
        """
        try:
            return self.capabilities[cap]
        except KeyError:
            raise error.TestFail("Unexpected capability: %s" % cap)


    def ensure_capability(self, cap):
        """
        Raise TestNAError if a device doesn't satisfy cap.
        """
        if self.get_capability(cap) != 'yes':
            raise error.TestNAError("Missing Capability: %s" % cap)


    def have_capability(self, cap):
        """
        Return whether cap is available.
        """
        return self.get_capability(cap) == 'yes'