/*
 * Copyright (C) 2018 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.
 */

#ifndef ANDROID_MICROPHONE_INFO_H
#define ANDROID_MICROPHONE_INFO_H

#include <binder/Parcel.h>
#include <binder/Parcelable.h>
#include <system/audio.h>
#include <utils/String16.h>
#include <utils/Vector.h>

namespace android {
namespace media {

#define RETURN_IF_FAILED(calledOnce)                                     \
    {                                                                    \
        status_t returnStatus = calledOnce;                              \
        if (returnStatus) {                                              \
            ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \
            return returnStatus;                                         \
         }                                                               \
    }

class MicrophoneInfo : public Parcelable {
public:
    MicrophoneInfo() = default;
    MicrophoneInfo(const MicrophoneInfo& microphoneInfo) = default;
    MicrophoneInfo(audio_microphone_characteristic_t& characteristic) {
        mDeviceId = String16(&characteristic.device_id[0]);
        mPortId = characteristic.id;
        mType = characteristic.device;
        mAddress = String16(&characteristic.address[0]);
        mDeviceLocation = characteristic.location;
        mDeviceGroup = characteristic.group;
        mIndexInTheGroup = characteristic.index_in_the_group;
        mGeometricLocation.push_back(characteristic.geometric_location.x);
        mGeometricLocation.push_back(characteristic.geometric_location.y);
        mGeometricLocation.push_back(characteristic.geometric_location.z);
        mOrientation.push_back(characteristic.orientation.x);
        mOrientation.push_back(characteristic.orientation.y);
        mOrientation.push_back(characteristic.orientation.z);
        Vector<float> frequencies;
        Vector<float> responses;
        for (size_t i = 0; i < characteristic.num_frequency_responses; i++) {
            frequencies.push_back(characteristic.frequency_responses[0][i]);
            responses.push_back(characteristic.frequency_responses[1][i]);
        }
        mFrequencyResponses.push_back(frequencies);
        mFrequencyResponses.push_back(responses);
        for (size_t i = 0; i < AUDIO_CHANNEL_COUNT_MAX; i++) {
            mChannelMapping.push_back(characteristic.channel_mapping[i]);
        }
        mSensitivity = characteristic.sensitivity;
        mMaxSpl = characteristic.max_spl;
        mMinSpl = characteristic.min_spl;
        mDirectionality = characteristic.directionality;
    }

    virtual ~MicrophoneInfo() = default;

    virtual status_t writeToParcel(Parcel* parcel) const {
        RETURN_IF_FAILED(parcel->writeString16(mDeviceId));
        RETURN_IF_FAILED(parcel->writeInt32(mPortId));
        RETURN_IF_FAILED(parcel->writeUint32(mType));
        RETURN_IF_FAILED(parcel->writeString16(mAddress));
        RETURN_IF_FAILED(parcel->writeInt32(mDeviceLocation));
        RETURN_IF_FAILED(parcel->writeInt32(mDeviceGroup));
        RETURN_IF_FAILED(parcel->writeInt32(mIndexInTheGroup));
        RETURN_IF_FAILED(writeFloatVector(parcel, mGeometricLocation));
        RETURN_IF_FAILED(writeFloatVector(parcel, mOrientation));
        if (mFrequencyResponses.size() != 2) {
            return BAD_VALUE;
        }
        for (size_t i = 0; i < mFrequencyResponses.size(); i++) {
            RETURN_IF_FAILED(parcel->writeInt32(mFrequencyResponses[i].size()));
            RETURN_IF_FAILED(writeFloatVector(parcel, mFrequencyResponses[i]));
        }
        std::vector<int> channelMapping;
        for (size_t i = 0; i < mChannelMapping.size(); ++i) {
            channelMapping.push_back(mChannelMapping[i]);
        }
        RETURN_IF_FAILED(parcel->writeInt32Vector(channelMapping));
        RETURN_IF_FAILED(parcel->writeFloat(mSensitivity));
        RETURN_IF_FAILED(parcel->writeFloat(mMaxSpl));
        RETURN_IF_FAILED(parcel->writeFloat(mMinSpl));
        RETURN_IF_FAILED(parcel->writeInt32(mDirectionality));
        return OK;
    }

    virtual status_t readFromParcel(const Parcel* parcel) {
        RETURN_IF_FAILED(parcel->readString16(&mDeviceId));
        RETURN_IF_FAILED(parcel->readInt32(&mPortId));
        RETURN_IF_FAILED(parcel->readUint32(&mType));
        RETURN_IF_FAILED(parcel->readString16(&mAddress));
        RETURN_IF_FAILED(parcel->readInt32(&mDeviceLocation));
        RETURN_IF_FAILED(parcel->readInt32(&mDeviceGroup));
        RETURN_IF_FAILED(parcel->readInt32(&mIndexInTheGroup));
        RETURN_IF_FAILED(readFloatVector(parcel, &mGeometricLocation, 3));
        RETURN_IF_FAILED(readFloatVector(parcel, &mOrientation, 3));
        int32_t frequenciesNum;
        RETURN_IF_FAILED(parcel->readInt32(&frequenciesNum));
        Vector<float> frequencies;
        RETURN_IF_FAILED(readFloatVector(parcel, &frequencies, frequenciesNum));
        int32_t responsesNum;
        RETURN_IF_FAILED(parcel->readInt32(&responsesNum));
        Vector<float> responses;
        RETURN_IF_FAILED(readFloatVector(parcel, &responses, responsesNum));
        if (frequencies.size() != responses.size()) {
            return BAD_VALUE;
        }
        mFrequencyResponses.push_back(frequencies);
        mFrequencyResponses.push_back(responses);
        std::vector<int> channelMapping;
        status_t result = parcel->readInt32Vector(&channelMapping);
        if (result != OK) {
            return result;
        }
        if (channelMapping.size() != AUDIO_CHANNEL_COUNT_MAX) {
            return BAD_VALUE;
        }
        for (size_t i = 0; i < channelMapping.size(); i++) {
            mChannelMapping.push_back(channelMapping[i]);
        }
        RETURN_IF_FAILED(parcel->readFloat(&mSensitivity));
        RETURN_IF_FAILED(parcel->readFloat(&mMaxSpl));
        RETURN_IF_FAILED(parcel->readFloat(&mMinSpl));
        RETURN_IF_FAILED(parcel->readInt32(&mDirectionality));
        return OK;
    }

    String16 getDeviceId() const {
        return mDeviceId;
    }

    int getPortId() const {
        return mPortId;
    }

    unsigned int getType() const {
        return mType;
    }

    String16 getAddress() const {
        return mAddress;
    }

    int getDeviceLocation() const {
        return mDeviceLocation;
    }

    int getDeviceGroup() const {
        return mDeviceGroup;
    }

    int getIndexInTheGroup() const {
        return mIndexInTheGroup;
    }

    const Vector<float>& getGeometricLocation() const {
        return mGeometricLocation;
    }

    const Vector<float>& getOrientation() const {
        return mOrientation;
    }

    const Vector<Vector<float>>& getFrequencyResponses() const {
        return mFrequencyResponses;
    }

    const Vector<int>& getChannelMapping() const {
        return mChannelMapping;
    }

    float getSensitivity() const {
        return mSensitivity;
    }

    float getMaxSpl() const {
        return mMaxSpl;
    }

    float getMinSpl() const {
        return mMinSpl;
    }

    int getDirectionality() const {
        return mDirectionality;
    }

private:
    status_t readFloatVector(
            const Parcel* parcel, Vector<float> *vectorPtr, size_t defaultLength) {
        std::unique_ptr<std::vector<float>> v;
        status_t result = parcel->readFloatVector(&v);
        if (result != OK) return result;
        vectorPtr->clear();
        if (v.get() != nullptr) {
            for (const auto& iter : *v) {
                vectorPtr->push_back(iter);
            }
        } else {
            vectorPtr->resize(defaultLength);
        }
        return OK;
    }
    status_t writeFloatVector(Parcel* parcel, const Vector<float>& vector) const {
        std::vector<float> v;
        for (size_t i = 0; i < vector.size(); i++) {
            v.push_back(vector[i]);
        }
        return parcel->writeFloatVector(v);
    }

    String16 mDeviceId;
    int32_t mPortId;
    uint32_t mType;
    String16 mAddress;
    int32_t mDeviceLocation;
    int32_t mDeviceGroup;
    int32_t mIndexInTheGroup;
    Vector<float> mGeometricLocation;
    Vector<float> mOrientation;
    Vector<Vector<float>> mFrequencyResponses;
    Vector<int> mChannelMapping;
    float mSensitivity;
    float mMaxSpl;
    float mMinSpl;
    int32_t mDirectionality;
};

} // namespace media
} // namespace android

#endif