# Copyright 2014 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.
"""Audio port ids shared in Chameleon audio test framework"""


class ChameleonIds(object):
    """Ids for audio ports on Chameleon

    An Id is composed by host name 'Chameleon' and interface name e.g. 'HDMI'.
    Note that the interface name must match what get_connector_type method
    returns on a ChameleonPort so ChameleonPortFinder can find the port.

    """
    HDMI = 'Chameleon HDMI'
    LINEIN = 'Chameleon LineIn'
    LINEOUT = 'Chameleon LineOut'
    MIC = 'Chameleon Mic'
    USBIN = 'Chameleon USBIn'
    USBOUT = 'Chameleon USBOut'

    SINK_PORTS = [HDMI, LINEIN, MIC, USBIN]
    SOURCE_PORTS = [LINEOUT, USBOUT]


class CrosIds(object):
    """Ids for audio ports on Cros device.

    Note that an bidirectional interface like 3.5mm jack is separated to
    two interfaces, that is, 'Headphone' and 'External Mic'.

    """
    HDMI = 'Cros HDMI'
    HEADPHONE = 'Cros Headphone'
    EXTERNAL_MIC = 'Cros External Mic'
    SPEAKER = 'Cros Speaker'
    INTERNAL_MIC = 'Cros Internal Mic'
    BLUETOOTH_HEADPHONE = 'Cros Bluetooth Headphone'
    BLUETOOTH_MIC = 'Cros Bluetooth Mic'
    USBIN = 'Cros USBIn'
    USBOUT = 'Cros USBOut'

    SINK_PORTS = [EXTERNAL_MIC, INTERNAL_MIC, BLUETOOTH_MIC, USBIN]
    SOURCE_PORTS = [HDMI, HEADPHONE, SPEAKER, BLUETOOTH_HEADPHONE, USBOUT]


class PeripheralIds(object):
    """Ids for peripherals.

    These peripherals will be accessible by Cros device/Chameleon through
    audio board.

    """
    SPEAKER = 'Peripheral Speaker'
    MIC = 'Peripheral Mic'

    # Peripheral devices should have two roles but we only care one.
    # For example, to test internal microphone on Cros device:
    #
    #                                         (air)
    #                    Peripheral Speaker -------> Internal Microphone
    #                         ------                  ------
    # Chameleon LineOut ----> |    |                  |    |
    #                         ------                  ------
    #                        Audio board             Cros device
    #
    # In the above example, peripheral speaker is a sink as it takes signal
    # from audio board. It should be a source as peripheral speaker transfer
    # signal to internal microphone of Cros device,
    # However, we do not abstract air as a link because it does not contain
    # properties like level, channel_map, occupied to manipulate.
    # So, we set peripheral speaker to be a sink to reflect the part related
    # to audio bus.
    #
    # For example, to test internal speaker on Cros device:
    #
    #                                         (air)
    #                    Peripheral Micropone <----- Internal Speaker
    #                         ------                  ------
    # Chameleon LineIn <----  |    |                  |    |
    #                         ------                  ------
    #                        Audio board             Cros device
    #
    # In the above example, peripheral microphone is a source as it feeds signal
    # to audio board. It should be a sink as peripheral microphone receives
    # signal from internal speaker of Cros device.
    # However, we do not abstract air as a link because it does not contain
    # properties like level, channel_map, occupied to manipulate.
    # So, we set peripheral microphone to be a source to reflect the part related
    # to audio bus.

    BLUETOOTH_DATA_RX = ('Peripheral Bluetooth Signal Output and Data Receiver')
    BLUETOOTH_DATA_TX = ('Peripheral Bluetooth Signal Input and Data '
                         'Transmitter')

    # Bluetooth module has both source and sink roles.
    # When Cros device plays audio to bluetooth module data receiver through
    # bluetooth connection, bluetooth module is a sink of audio signal.
    # When we route audio signal from bluetooth signal output to Chameleon
    # Line-In, bluetooth module is a signal source in terms of audio bus.
    #
    #                     Bluetooth module
    #
    #                    signal     data      (bluetooth)
    #                    output    receiver <------------ Bluetooth Headphone
    #                         ------                        ------
    # Chameleon LineIn <----  |    |                        |    |
    #                         ------                        ------
    #                        Audio board                   Cros device
    #
    #
    # When Cros device records audio from bluetooth module data transmitter
    # through bluetooth connection, bluetooth module is a source of audio
    # signal. When we route audio signal from Chameleon Line-Out to bluetooth
    # signal input, bluetooth module is a signal sink in terms of audio bus.
    #
    #                     Bluetooth module
    #
    #                    signal     data      (bluetooth)
    #                    input    transmitter -----------> Bluetooth Microphone
    #                         ------                        ------
    # Chameleon LineOut ----> |    |                        |    |
    #                         ------                        ------
    #                        Audio board                   Cros device
    #
    # From above diagram it is clear that "signal output" is connected to
    # "data receiver", while "signal input" is connected to "data transmitter".
    # To simplify the number of nodes, we group "signal output" and
    # "data receiver" into one Id, and group "signal input" and
    # "data transmitter" into one Id. Also, we let these two Ids be both source
    # and sink.
    SOURCE_PORTS = [MIC, BLUETOOTH_DATA_RX, BLUETOOTH_DATA_TX]
    SINK_PORTS = [SPEAKER, BLUETOOTH_DATA_RX, BLUETOOTH_DATA_TX]


SINK_PORTS = []
for cls in [ChameleonIds, CrosIds, PeripheralIds]:
    SINK_PORTS += cls.SINK_PORTS

SOURCE_PORTS = []
for cls in [ChameleonIds, CrosIds, PeripheralIds]:
    SOURCE_PORTS += cls.SOURCE_PORTS


def get_host(port_id):
    """Parses given port_id to get host name.

    @param port_id: A string, that is, id in ChameleonIds, CrosIds, or
                    PeripheralIds.

    @returns: Host name. A string in ['Chameleon', 'Cros', 'Peripheral'].

    @raises: ValueError if port_id is invalid.

    """
    host = port_id.split()[0]
    if host not in ['Chameleon', 'Cros', 'Peripheral']:
        raise ValueError('Not a valid port id: %r' % port_id)
    return host


def get_interface(port_id):
    """Parses given port_id to get interface name.

    @param port_id: A string, that is, id in ChameleonIds, CrosIds, or
                    PeripheralIds.

    @returns: Interface name. A string, e.g. 'HDMI', 'LineIn'.

    @raises: ValueError if port_id is invalid.

    """
    try:
        return port_id.split(' ', 1)[1]
    except IndexError:
        raise ValueError('Not a valid port id: %r' % port_id)


def get_role(port_id):
    """Gets the role of given port_id.

    @param port_id: A string, that is, id in ChameleonIds, CrosIds, or
                    PeripheralIds.

    @returns: 'source' or 'sink'.

    @raises: ValueError if port_id is invalid.

    """
    if port_id in SOURCE_PORTS:
        return 'source'
    if port_id in SINK_PORTS:
        return 'sink'
    if port_id in SOURCE_PORTS and port_id in SINK_PORTS:
        return 'sink | source'
    raise ValueError('Not a valid port id: %r' % port_id)