# Copyright 2016 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 os

from autotest_lib.client.bin import test
from autotest_lib.client.bin import utils
from autotest_lib.client.common_lib import error
from autotest_lib.client.cros.input_playback import input_playback


class a11y_test_base(test.test):
    """Base class for a11y tests."""
    version = 1

    # ChromeVox extension id
    _CHROMEVOX_ID = 'mndnfokpggljbaajbnioimlmbfngpief'
    _CVOX_STATE_TIMEOUT = 40
    _CVOX_INDICATOR_TIMEOUT = 40


    def warmup(self):
        """Test setup."""
        # Emulate a keyboard for later ChromeVox toggle (if needed).
        # See input_playback. The keyboard is used to play back shortcuts.
        self._player = input_playback.InputPlayback()
        self._player.emulate(input_type='keyboard')
        self._player.find_connected_inputs()


    def _child_test_cleanup(self):
        """Can be overwritten by child classes and run duing parent cleanup."""
        return


    def cleanup(self):
        self._player.close()
        self._child_test_cleanup()


    def _toggle_chromevox(self):
        """Use keyboard shortcut and emulated keyboard to toggle ChromeVox."""
        self._player.blocking_playback_of_default_file(
                input_type='keyboard', filename='keyboard_ctrl+alt+z')


    def _search_shift_move(self, direction):
        """Playback the keyboard movement shortcut for given direction.

        @param direction: right, left, up, or down.

        """
        assert direction in ['right', 'left', 'up', 'down']
        self._player.blocking_playback_of_default_file(
                input_type='keyboard',
                filename='keyboard_search+shift+%s' % direction)


    def _tab_move(self, direction='forwards'):
        """Playback a tab or shift+tab for the given direction.

        @param direction: forwards or backwards.

        """
        assert direction in ['forwards', 'backwards']
        is_forwards = direction is 'forwards'
        filename = 'keyboard_tab' if is_forwards else 'keyboard_shift+tab'
        self._player.blocking_playback_of_default_file(
                input_type='keyboard', filename=filename)


    def _set_feature(self, feature, value):
        """Set given feature to given value using a11y API call.

        Presupposes self._extension (with accessibilityFeatures enabled).

        @param feature: string of accessibility feature to change.
        @param value: boolean of expected value.

        @raises: error.TestError if feature cannot be set.

        """
        value_str = 'true' if value else 'false'
        cmd = ('window.__result = null;\n'
               'chrome.accessibilityFeatures.%s.set({value: %s});\n'
               'chrome.accessibilityFeatures.%s.get({}, function(d) {'
               'window.__result = d[\'value\']; });' % (
                       feature, value_str, feature))
        self._extension.ExecuteJavaScript(cmd)

        poll_cmd = 'window.__result == %s;' % value_str
        utils.poll_for_condition(
                lambda: self._extension.EvaluateJavaScript(poll_cmd),
                exception = error.TestError(
                        'Timeout while trying to set %s to %s' %
                        (feature, value_str)))


    def _get_chromevox_state(self):
        """Return whether ChromeVox is enabled or not.

        Presupposes self._extension (with management enabled).

        @returns: value of management.get.enabled.

        @raises: error.TestError if state cannot be determined.

        """
        cmd = ('window.__enabled = null;\n'
               'chrome.management.get(\'%s\', function(r) {'
               'if (r) {window.__enabled = r[\'enabled\'];} '
               'else {window.__enabled = false;}});' % self._CHROMEVOX_ID)
        self._extension.ExecuteJavaScript(cmd)

        poll_cmd = 'window.__enabled != null;'
        utils.poll_for_condition(
                lambda: self._extension.EvaluateJavaScript(poll_cmd),
                exception=error.TestError(
                        'ChromeVox: management.get.enabled was not set!'))
        return self._extension.EvaluateJavaScript('window.__enabled')


    def _confirm_chromevox_state(self, value):
        """Fail test unless ChromeVox state is given value.

        Presupposes self._extension (with management enabled).

        @param value: True or False, whether ChromeVox should be enabled.

        @raises: error.TestFail if actual state doesn't match expected.

        """
        utils.poll_for_condition(
                lambda: self._get_chromevox_state() == value,
                exception=error.TestFail('ChromeVox: enabled state '
                                         'was not %s.' % value),
                timeout=self._CVOX_STATE_TIMEOUT)


    def _get_chromevox_indicator(self, tab):
        """Return whether the orange ChromeVox highlight is present or not.

        Looks for the orange highlight on the given tab.

        @returns: whether 'cvox_indicator_container' is found on the page

        """
        cmd = ('document.getElementsByClassName('
               '  "cvox_indicator_container").length > 0;')
        return tab.EvaluateJavaScript(cmd)


    def _confirm_chromevox_indicator(self, value):
        """Fail test unless indicator state is given value.

        Presupposes self._tab (the tab on which to check).

        @param value: True or False, whether ChromeVox indicator should show.

        @raises: error.TestFail if actual state doesn't match expected.

        """
        utils.poll_for_condition(
                lambda: self._get_chromevox_indicator(self._tab) == value,
                exception=error.TestFail('ChromeVox: "Indicator present" '
                                         'was not %s.' % value),
                timeout=self._CVOX_INDICATOR_TIMEOUT)


    def _get_extension_path(self):
        """Return the path to the default accessibility extension.

        @returns: string of path to default extension.

        """
        this_dir = os.path.dirname(__file__)
        return os.path.join(this_dir, 'a11y_ext')