/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * 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.
 */
package com.android.server.usb.descriptors;

import android.util.Log;

import com.android.server.usb.descriptors.report.ReportCanvas;
import com.android.server.usb.descriptors.report.UsbStrings;

/**
 * @hide
 * An audio class-specific Interface.
 * see audio10.pdf section 4.3.2
 */
public abstract class UsbACInterface extends UsbDescriptor {
    private static final String TAG = "UsbACInterface";

    // Audio Control Subtypes
    public static final byte ACI_UNDEFINED = 0;
    public static final byte ACI_HEADER = 1;
    public static final byte ACI_INPUT_TERMINAL = 2;
    public static final byte ACI_OUTPUT_TERMINAL = 3;
    public static final byte ACI_MIXER_UNIT = 4;
    public static final byte ACI_SELECTOR_UNIT = 5;
    public static final byte ACI_FEATURE_UNIT = 6;
    public static final byte ACI_PROCESSING_UNIT = 7;
    public static final byte ACI_EXTENSION_UNIT = 8;
    // Not handled yet
    public static final byte ACI_CLOCK_SOURCE = 0x0A;
    public static final byte ACI_CLOCK_SELECTOR = 0x0B;
    public static final byte ACI_CLOCK_MULTIPLIER = 0x0C;
    public static final byte ACI_SAMPLE_RATE_CONVERTER =  0x0D;

    // Audio Streaming Subtypes
    public static final byte ASI_UNDEFINED = 0;
    public static final byte ASI_GENERAL = 1;
    public static final byte ASI_FORMAT_TYPE = 2;
    public static final byte ASI_FORMAT_SPECIFIC = 3;

    // MIDI Streaming Subtypes
    public static final byte MSI_UNDEFINED = 0;
    public static final byte MSI_HEADER = 1;
    public static final byte MSI_IN_JACK = 2;
    public static final byte MSI_OUT_JACK = 3;
    public static final byte MSI_ELEMENT = 4;

    // Sample format IDs (encodings)
    // FORMAT_I
    public static final int FORMAT_I_UNDEFINED     = 0x0000;
    public static final int FORMAT_I_PCM           = 0x0001;
    public static final int FORMAT_I_PCM8          = 0x0002;
    public static final int FORMAT_I_IEEE_FLOAT    = 0x0003;
    public static final int FORMAT_I_ALAW          = 0x0004;
    public static final int FORMAT_I_MULAW         = 0x0005;
    // FORMAT_II
    public static final int FORMAT_II_UNDEFINED    = 0x1000;
    public static final int FORMAT_II_MPEG         = 0x1001;
    public static final int FORMAT_II_AC3          = 0x1002;
    // FORMAT_III
    public static final int FORMAT_III_UNDEFINED              = 0x2000;
    public static final int FORMAT_III_IEC1937AC3             = 0x2001;
    public static final int FORMAT_III_IEC1937_MPEG1_Layer1   = 0x2002;
    public static final int FORMAT_III_IEC1937_MPEG1_Layer2   = 0x2003;
    public static final int FORMAT_III_IEC1937_MPEG2_EXT      = 0x2004;
    public static final int FORMAT_III_IEC1937_MPEG2_Layer1LS = 0x2005;

    protected final byte mSubtype;  // 2:1 HEADER descriptor subtype
    protected final int mSubclass;  // from the mSubclass member of the
                                    // "enclosing" Interface Descriptor

    public UsbACInterface(int length, byte type, byte subtype, int subclass) {
        super(length, type);
        mSubtype = subtype;
        mSubclass = subclass;
    }

    public byte getSubtype() {
        return mSubtype;
    }

    public int getSubclass() {
        return mSubclass;
    }

    private static UsbDescriptor allocAudioControlDescriptor(UsbDescriptorParser parser,
            ByteStream stream, int length, byte type, byte subtype, int subClass) {
        switch (subtype) {
            case ACI_HEADER:
            {
                int acInterfaceSpec = stream.unpackUsbShort();
                parser.setACInterfaceSpec(acInterfaceSpec);
                if (acInterfaceSpec == UsbDeviceDescriptor.USBSPEC_2_0) {
                    return new Usb20ACHeader(length, type, subtype, subClass, acInterfaceSpec);
                } else {
                    return new Usb10ACHeader(length, type, subtype, subClass, acInterfaceSpec);
                }
            }

            case ACI_INPUT_TERMINAL:
            {
                int acInterfaceSpec = parser.getACInterfaceSpec();
                if (acInterfaceSpec == UsbDeviceDescriptor.USBSPEC_2_0) {
                    return new Usb20ACInputTerminal(length, type, subtype, subClass);
                } else {
                    return new Usb10ACInputTerminal(length, type, subtype, subClass);
                }
            }

            case ACI_OUTPUT_TERMINAL:
            {
                int acInterfaceSpec = parser.getACInterfaceSpec();
                if (acInterfaceSpec == UsbDeviceDescriptor.USBSPEC_2_0) {
                    return new Usb20ACOutputTerminal(length, type, subtype, subClass);
                } else {
                    return new Usb10ACOutputTerminal(length, type, subtype, subClass);
                }
            }

            case ACI_SELECTOR_UNIT:
                return new UsbACSelectorUnit(length, type, subtype, subClass);

            case ACI_FEATURE_UNIT:
                return new UsbACFeatureUnit(length, type, subtype, subClass);

            case ACI_MIXER_UNIT:
            {
                int acInterfaceSpec = parser.getACInterfaceSpec();
                if (acInterfaceSpec == UsbDeviceDescriptor.USBSPEC_2_0) {
                    return new Usb20ACMixerUnit(length, type, subtype, subClass);
                } else {
                    return new Usb10ACMixerUnit(length, type, subtype, subClass);
                }
            }

            case ACI_PROCESSING_UNIT:
            case ACI_EXTENSION_UNIT:
            case ACI_UNDEFINED:
                // break; Fall through until we implement this descriptor
            default:
                Log.w(TAG, "Unknown Audio Class Interface subtype:0x"
                        + Integer.toHexString(subtype));
                return new UsbACInterfaceUnparsed(length, type, subtype, subClass);
        }
    }

    private static UsbDescriptor allocAudioStreamingDescriptor(UsbDescriptorParser parser,
            ByteStream stream, int length, byte type, byte subtype, int subClass) {
        //int spec = parser.getUsbSpec();
        int acInterfaceSpec = parser.getACInterfaceSpec();
        switch (subtype) {
            case ASI_GENERAL:
                if (acInterfaceSpec == UsbDeviceDescriptor.USBSPEC_2_0) {
                    return new Usb20ASGeneral(length, type, subtype, subClass);
                } else {
                    return new Usb10ASGeneral(length, type, subtype, subClass);
                }

            case ASI_FORMAT_TYPE:
                return UsbASFormat.allocDescriptor(parser, stream, length, type, subtype, subClass);

            case ASI_FORMAT_SPECIFIC:
            case ASI_UNDEFINED:
                // break; Fall through until we implement this descriptor
            default:
                Log.w(TAG, "Unknown Audio Streaming Interface subtype:0x"
                        + Integer.toHexString(subtype));
                return null;
        }
    }

    private static UsbDescriptor allocMidiStreamingDescriptor(int length, byte type,
            byte subtype, int subClass) {
        switch (subtype) {
            case MSI_HEADER:
                return new UsbMSMidiHeader(length, type, subtype, subClass);

            case MSI_IN_JACK:
                return new UsbMSMidiInputJack(length, type, subtype, subClass);

            case MSI_OUT_JACK:
                return new UsbMSMidiOutputJack(length, type, subtype, subClass);

            case MSI_ELEMENT:
                // break;
                // Fall through until we implement that descriptor

            case MSI_UNDEFINED:
            default:
                Log.w(TAG, "Unknown MIDI Streaming Interface subtype:0x"
                        + Integer.toHexString(subtype));
                return null;
        }
    }

    /**
     * Allocates an audio class interface subtype based on subtype and subclass.
     */
    public static UsbDescriptor allocDescriptor(UsbDescriptorParser parser, ByteStream stream,
            int length, byte type) {
        byte subtype = stream.getByte();
        UsbInterfaceDescriptor interfaceDesc = parser.getCurInterface();
        int subClass = interfaceDesc.getUsbSubclass();
        switch (subClass) {
            case AUDIO_AUDIOCONTROL:
                return allocAudioControlDescriptor(
                        parser, stream, length, type, subtype, subClass);

            case AUDIO_AUDIOSTREAMING:
                return allocAudioStreamingDescriptor(
                        parser, stream, length, type, subtype, subClass);

            case AUDIO_MIDISTREAMING:
                return allocMidiStreamingDescriptor(length, type, subtype, subClass);

            default:
                Log.w(TAG, "Unknown Audio Class Interface Subclass: 0x"
                        + Integer.toHexString(subClass));
                return null;
        }
    }

    @Override
    public void report(ReportCanvas canvas) {
        super.report(canvas);

        int subClass = getSubclass();
        String subClassName = UsbStrings.getACInterfaceSubclassName(subClass);

        byte subtype = getSubtype();
        String subTypeName = UsbStrings.getACControlInterfaceName(subtype);

        canvas.openList();
        canvas.writeListItem("Subclass: " + ReportCanvas.getHexString(subClass)
                + " " + subClassName);
        canvas.writeListItem("Subtype: " + ReportCanvas.getHexString(subtype) + " " + subTypeName);
        canvas.closeList();
    }
}
