# 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.

"""This module provides the audio widgets related to ARC used in audio tests."""

import copy
import tempfile

from autotest_lib.client.cros.audio import audio_test_data
from autotest_lib.client.cros.chameleon import audio_widget

class CrosInputWidgetARCHandler(audio_widget.CrosInputWidgetHandler):
    """

    This class abstracts a Cros device audio input widget ARC handler.

    """
    # AMR-NB uses variable bit rates so we set sample_format to None.
    # Other format info are actually useless for sox because sox can read them
    # from file header.
    _SOURCE_FORMAT = dict(file_type='amr-nb',
                          sample_format=None,
                          channel=1,
                          rate=8000)

    def start_recording(self):
        """Starts recording audio through ARC."""
        self._audio_facade.start_arc_recording()


    def stop_recording(self):
        """Stops recording audio through ARC.

        @returns:
            A tuple (remote_path, format).
                remote_path: The path to the recorded file on Cros device.
                format: A dict containing:
                    file_type: 'raw'.
                    sample_format: 'S16_LE' for 16-bit signed integer in
                                   little-endian.
                    channel: channel number.
                    rate: sampling rate.

        """
        return (self._audio_facade.stop_arc_recording(),
                self._DEFAULT_DATA_FORMAT)


    def get_recorded_binary(self, remote_path, record_format):
        """Gets remote recorded file binary from Cros device..

        Gets and reads recorded file from Cros device.
        The argument 'record_format' is what API user want on output.
        The real file format of file at 'remote_path' can be another source
        format. This method handles the format conversion from source format
        into record_format, and returns the converted binary.

        Handle the format conversion from source format into record_format.

        @param remote_path: The path to the recorded file on Cros device.
        @param record_format: The data format of returned binary.
                     A dict containing
                     file_type: 'raw' or 'wav'.
                     sample_format: 'S32_LE' for 32-bit signed integer in
                                    little-endian. Refer to aplay manpage for
                                    other formats.
                     channel: channel number.
                     rate: sampling rate.

        @returns: The recorded binary.

        @raises: CrosInputWidgetHandlerError if record_format is not correct.

        """
        if record_format != self._DEFAULT_DATA_FORMAT:
            raise audio_widget.CrosInputWidgetHandlerError(
                    'Record format %r is not valid' % record_format)

        ext = '.' + self._SOURCE_FORMAT['file_type']
        with tempfile.NamedTemporaryFile(prefix='recorded_', suffix=ext) as f:
            self._audio_facade.get_recorded_file(remote_path, f.name)

            # Handles conversion from source format into record_format.
            test_data = audio_test_data.AudioTestData(
                    self._SOURCE_FORMAT, f.name)
            converted_test_data = test_data.convert(record_format, 1.0)
            try:
                return converted_test_data.get_binary()
            finally:
                converted_test_data.delete()


class CrosOutputWidgetARCHandlerError(Exception):
    """Error in CrosOutputWidgetARCHandler."""
    pass


class CrosOutputWidgetARCHandler(audio_widget.CrosOutputWidgetHandler):
    """This class abstracts a Cros device audio output widget ARC handler."""
    _SUPPORTED_FILE_TYPES = ['wav', 'mp3']
    _DEFAULT_FILE_TYPE = 'wav'

    def set_playback_data(self, test_data):
        """Sets data to play.

        @param test_data: An AudioTestData object.

        @returns: Path to the file in container on Cros host.

        """
        # Handle the format conversion because ARC does not recognize raw file.
        if test_data.data_format['file_type'] not in self._SUPPORTED_FILE_TYPES:
            new_data_format = copy.deepcopy(test_data.data_format)
            new_data_format['file_type'] = self._DEFAULT_FILE_TYPE
            test_data = test_data.convert(new_data_format, 1.0)
        return self._audio_facade.set_arc_playback_file(test_data.path)


    def start_playback(self, path, blocking=False):
        """Starts playing audio.

        @param path: Path to the file to play in container on Cros host.
        @param blocking: Blocks this call until playback finishes.

        @raises: NotImplementedError if blocking is True.

        """
        if blocking:
            raise NotImplementedError(
                    'Blocking playback on ARC is not supported.')
        self._audio_facade.start_arc_playback(path)


    def stop_playback(self):
        """Stops playing audio."""
        self._audio_facade.stop_arc_playback()