/*
 * Copyright 2016, 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.
 */

#include <algorithm>

#include <media/omx/1.0/WOmxNode.h>
#include <media/omx/1.0/WOmxBufferSource.h>
#include <media/omx/1.0/Conversion.h>

namespace android {
namespace hardware {
namespace media {
namespace omx {
namespace V1_0 {
namespace utils {

using ::android::hardware::Void;

// LWOmxNode
status_t LWOmxNode::freeNode() {
    return toStatusT(mBase->freeNode());
}

status_t LWOmxNode::sendCommand(
        OMX_COMMANDTYPE cmd, OMX_S32 param) {
    return toStatusT(mBase->sendCommand(
            toRawCommandType(cmd), param));
}

status_t LWOmxNode::getParameter(
        OMX_INDEXTYPE index, void *params, size_t size) {
    hidl_vec<uint8_t> tParams = inHidlBytes(params, size);
    status_t fnStatus;
    status_t transStatus = toStatusT(mBase->getParameter(
            toRawIndexType(index),
            tParams,
            [&fnStatus, params, size](
                    Status status, hidl_vec<uint8_t> const& outParams) {
                fnStatus = toStatusT(status);
                std::copy(
                        outParams.data(),
                        outParams.data() + outParams.size(),
                        static_cast<uint8_t*>(params));
            }));
    return transStatus == NO_ERROR ? fnStatus : transStatus;
}

status_t LWOmxNode::setParameter(
        OMX_INDEXTYPE index, const void *params, size_t size) {
    hidl_vec<uint8_t> tParams = inHidlBytes(params, size);
    return toStatusT(mBase->setParameter(
            toRawIndexType(index), tParams));
}

status_t LWOmxNode::getConfig(
        OMX_INDEXTYPE index, void *params, size_t size) {
    hidl_vec<uint8_t> tParams = inHidlBytes(params, size);
    status_t fnStatus;
    status_t transStatus = toStatusT(mBase->getConfig(
            toRawIndexType(index),
            tParams,
            [&fnStatus, params, size](
                    Status status, hidl_vec<uint8_t> const& outParams) {
                fnStatus = toStatusT(status);
                std::copy(
                        outParams.data(),
                        outParams.data() + size,
                        static_cast<uint8_t*>(params));
            }));
    return transStatus == NO_ERROR ? fnStatus : transStatus;
}

status_t LWOmxNode::setConfig(
        OMX_INDEXTYPE index, const void *params, size_t size) {
    hidl_vec<uint8_t> tParams = inHidlBytes(params, size);
    return toStatusT(mBase->setConfig(toRawIndexType(index), tParams));
}

status_t LWOmxNode::setPortMode(
        OMX_U32 port_index, IOMX::PortMode mode) {
    return toStatusT(mBase->setPortMode(port_index, toHardwarePortMode(mode)));
}

status_t LWOmxNode::prepareForAdaptivePlayback(
        OMX_U32 portIndex, OMX_BOOL enable,
        OMX_U32 maxFrameWidth, OMX_U32 maxFrameHeight) {
    return toStatusT(mBase->prepareForAdaptivePlayback(
            portIndex, toRawBool(enable), maxFrameWidth, maxFrameHeight));
}

status_t LWOmxNode::configureVideoTunnelMode(
        OMX_U32 portIndex, OMX_BOOL tunneled,
        OMX_U32 audioHwSync, native_handle_t **sidebandHandle) {
    status_t fnStatus;
    status_t transStatus = toStatusT(mBase->configureVideoTunnelMode(
            portIndex,
            toRawBool(tunneled),
            audioHwSync,
            [&fnStatus, sidebandHandle](
                    Status status, hidl_handle const& outSidebandHandle) {
                fnStatus = toStatusT(status);
                *sidebandHandle = outSidebandHandle == nullptr ?
                        nullptr : native_handle_clone(outSidebandHandle);
            }));
    return transStatus == NO_ERROR ? fnStatus : transStatus;
}

status_t LWOmxNode::getGraphicBufferUsage(
        OMX_U32 portIndex, OMX_U32* usage) {
    status_t fnStatus;
    status_t transStatus = toStatusT(mBase->getGraphicBufferUsage(
            portIndex,
            [&fnStatus, usage](
                    Status status, uint32_t outUsage) {
                fnStatus = toStatusT(status);
                *usage = outUsage;
            }));
    return transStatus == NO_ERROR ? fnStatus : transStatus;
}

status_t LWOmxNode::setInputSurface(
        const sp<IOMXBufferSource> &bufferSource) {
    return toStatusT(mBase->setInputSurface(
            new TWOmxBufferSource(bufferSource)));
}

status_t LWOmxNode::allocateSecureBuffer(
        OMX_U32 portIndex, size_t size, buffer_id *buffer,
        void **buffer_data, sp<NativeHandle> *native_handle) {
    *buffer_data = nullptr;
    status_t fnStatus;
    status_t transStatus = toStatusT(mBase->allocateSecureBuffer(
            portIndex,
            static_cast<uint64_t>(size),
            [&fnStatus, buffer, buffer_data, native_handle](
                    Status status,
                    uint32_t outBuffer,
                    hidl_handle const& outNativeHandle) {
                fnStatus = toStatusT(status);
                *buffer = outBuffer;
                *native_handle = NativeHandle::create(
                        native_handle_clone(outNativeHandle), true);
            }));
    return transStatus == NO_ERROR ? fnStatus : transStatus;
}

status_t LWOmxNode::useBuffer(
        OMX_U32 portIndex, const OMXBuffer &omxBuffer, buffer_id *buffer) {
    CodecBuffer codecBuffer;
    if (!wrapAs(&codecBuffer, omxBuffer)) {
        return BAD_VALUE;
    }
    status_t fnStatus;
    status_t transStatus = toStatusT(mBase->useBuffer(
            portIndex,
            codecBuffer,
            [&fnStatus, buffer](Status status, uint32_t outBuffer) {
                fnStatus = toStatusT(status);
                *buffer = outBuffer;
            }));
    return transStatus == NO_ERROR ? fnStatus : transStatus;
}

status_t LWOmxNode::freeBuffer(
        OMX_U32 portIndex, buffer_id buffer) {
    return toStatusT(mBase->freeBuffer(portIndex, buffer));
}

status_t LWOmxNode::fillBuffer(
        buffer_id buffer, const OMXBuffer &omxBuffer, int fenceFd) {
    CodecBuffer codecBuffer;
    if (!wrapAs(&codecBuffer, omxBuffer)) {
        return BAD_VALUE;
    }
    native_handle_t* fenceNh = native_handle_create_from_fd(fenceFd);
    if (!fenceNh) {
        return NO_MEMORY;
    }
    status_t status = toStatusT(mBase->fillBuffer(
            buffer, codecBuffer, fenceNh));
    native_handle_close(fenceNh);
    native_handle_delete(fenceNh);
    return status;
}

status_t LWOmxNode::emptyBuffer(
        buffer_id buffer, const OMXBuffer &omxBuffer,
        OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) {
    CodecBuffer codecBuffer;
    if (!wrapAs(&codecBuffer, omxBuffer)) {
        return BAD_VALUE;
    }
    native_handle_t* fenceNh = native_handle_create_from_fd(fenceFd);
    if (!fenceNh) {
        return NO_MEMORY;
    }
    status_t status = toStatusT(mBase->emptyBuffer(
            buffer,
            codecBuffer,
            flags,
            toRawTicks(timestamp),
            fenceNh));
    native_handle_close(fenceNh);
    native_handle_delete(fenceNh);
    return status;
}
status_t LWOmxNode::getExtensionIndex(
        const char *parameter_name,
        OMX_INDEXTYPE *index) {
    status_t fnStatus;
    status_t transStatus = toStatusT(mBase->getExtensionIndex(
            hidl_string(parameter_name),
            [&fnStatus, index](Status status, uint32_t outIndex) {
                fnStatus = toStatusT(status);
                *index = toEnumIndexType(outIndex);
            }));
    return transStatus == NO_ERROR ? fnStatus : transStatus;
}

status_t LWOmxNode::dispatchMessage(const omx_message &lMsg) {
    Message tMsg;
    native_handle_t* nh;
    if (!wrapAs(&tMsg, &nh, lMsg)) {
        return NO_MEMORY;
    }
    status_t status = toStatusT(mBase->dispatchMessage(tMsg));
    native_handle_close(nh);
    native_handle_delete(nh);
    return status;
}

// TWOmxNode
TWOmxNode::TWOmxNode(sp<IOMXNode> const& base) : mBase(base) {
}

Return<Status> TWOmxNode::freeNode() {
    return toStatus(mBase->freeNode());
}

Return<Status> TWOmxNode::sendCommand(uint32_t cmd, int32_t param) {
    return toStatus(mBase->sendCommand(toEnumCommandType(cmd), param));
}

Return<void> TWOmxNode::getParameter(
        uint32_t index, hidl_vec<uint8_t> const& inParams,
        getParameter_cb _hidl_cb) {
    hidl_vec<uint8_t> params(inParams);
    Status status = toStatus(mBase->getParameter(
            toEnumIndexType(index),
            static_cast<void*>(params.data()),
            params.size()));
    _hidl_cb(status, params);
    return Void();
}

Return<Status> TWOmxNode::setParameter(
        uint32_t index, hidl_vec<uint8_t> const& inParams) {
    hidl_vec<uint8_t> params(inParams);
    return toStatus(mBase->setParameter(
            toEnumIndexType(index),
            static_cast<void const*>(params.data()),
            params.size()));
}

Return<void> TWOmxNode::getConfig(
        uint32_t index, const hidl_vec<uint8_t>& inConfig,
        getConfig_cb _hidl_cb) {
    hidl_vec<uint8_t> config(inConfig);
    Status status = toStatus(mBase->getConfig(
            toEnumIndexType(index),
            static_cast<void*>(config.data()),
            config.size()));
    _hidl_cb(status, config);
    return Void();
}

Return<Status> TWOmxNode::setConfig(
        uint32_t index, const hidl_vec<uint8_t>& inConfig) {
    hidl_vec<uint8_t> config(inConfig);
    return toStatus(mBase->setConfig(
            toEnumIndexType(index),
            static_cast<void const*>(config.data()),
            config.size()));
}

Return<Status> TWOmxNode::setPortMode(uint32_t portIndex, PortMode mode) {
    return toStatus(mBase->setPortMode(portIndex, toIOMXPortMode(mode)));
}

Return<Status> TWOmxNode::prepareForAdaptivePlayback(
        uint32_t portIndex, bool enable,
        uint32_t maxFrameWidth, uint32_t maxFrameHeight) {
    return toStatus(mBase->prepareForAdaptivePlayback(
            portIndex,
            toEnumBool(enable),
            maxFrameWidth,
            maxFrameHeight));
}

Return<void> TWOmxNode::configureVideoTunnelMode(
        uint32_t portIndex, bool tunneled, uint32_t audioHwSync,
        configureVideoTunnelMode_cb _hidl_cb) {
    native_handle_t* sidebandHandle = nullptr;
    Status status = toStatus(mBase->configureVideoTunnelMode(
            portIndex,
            toEnumBool(tunneled),
            audioHwSync,
            &sidebandHandle));
    _hidl_cb(status, hidl_handle(sidebandHandle));
    return Void();
}

Return<void> TWOmxNode::getGraphicBufferUsage(
        uint32_t portIndex, getGraphicBufferUsage_cb _hidl_cb) {
    OMX_U32 usage;
    Status status = toStatus(mBase->getGraphicBufferUsage(
            portIndex, &usage));
    _hidl_cb(status, usage);
    return Void();
}

Return<Status> TWOmxNode::setInputSurface(
        const sp<IOmxBufferSource>& bufferSource) {
    return toStatus(mBase->setInputSurface(new LWOmxBufferSource(
            bufferSource)));
}

Return<void> TWOmxNode::allocateSecureBuffer(
        uint32_t portIndex, uint64_t size,
        allocateSecureBuffer_cb _hidl_cb) {
    IOMX::buffer_id buffer;
    void* bufferData;
    sp<NativeHandle> nativeHandle;
    Status status = toStatus(mBase->allocateSecureBuffer(
            portIndex,
            static_cast<size_t>(size),
            &buffer,
            &bufferData,
            &nativeHandle));
    _hidl_cb(status, buffer, nativeHandle == nullptr ?
            nullptr : nativeHandle->handle());
    return Void();
}

Return<void> TWOmxNode::useBuffer(
        uint32_t portIndex, const CodecBuffer& codecBuffer,
        useBuffer_cb _hidl_cb) {
    IOMX::buffer_id buffer;
    OMXBuffer omxBuffer;
    if (!convertTo(&omxBuffer, codecBuffer)) {
        _hidl_cb(Status::BAD_VALUE, 0);
        return Void();
    }
    Status status = toStatus(mBase->useBuffer(
            portIndex, omxBuffer, &buffer));
    _hidl_cb(status, buffer);
    return Void();
}

Return<Status> TWOmxNode::freeBuffer(uint32_t portIndex, uint32_t buffer) {
    return toStatus(mBase->freeBuffer(portIndex, buffer));
}

Return<Status> TWOmxNode::fillBuffer(
        uint32_t buffer, const CodecBuffer& codecBuffer,
        const hidl_handle& fence) {
    OMXBuffer omxBuffer;
    if (!convertTo(&omxBuffer, codecBuffer)) {
        return Status::BAD_VALUE;
    }
    return toStatus(mBase->fillBuffer(
            buffer,
            omxBuffer,
            dup(native_handle_read_fd(fence))));
}

Return<Status> TWOmxNode::emptyBuffer(
        uint32_t buffer, const CodecBuffer& codecBuffer, uint32_t flags,
        uint64_t timestampUs, const hidl_handle& fence) {
    OMXBuffer omxBuffer;
    if (!convertTo(&omxBuffer, codecBuffer)) {
        return Status::BAD_VALUE;
    }
    return toStatus(mBase->emptyBuffer(
            buffer,
            omxBuffer,
            flags,
            toOMXTicks(timestampUs),
            dup(native_handle_read_fd(fence))));
}

Return<void> TWOmxNode::getExtensionIndex(
        const hidl_string& parameterName,
        getExtensionIndex_cb _hidl_cb) {
    OMX_INDEXTYPE index;
    Status status = toStatus(mBase->getExtensionIndex(
            parameterName.c_str(), &index));
    _hidl_cb(status, toRawIndexType(index));
    return Void();
}

Return<Status> TWOmxNode::dispatchMessage(const Message& tMsg) {
    omx_message lMsg;
    if (!convertTo(&lMsg, tMsg)) {
        return Status::BAD_VALUE;
    }
    return toStatus(mBase->dispatchMessage(lMsg));
}

}  // namespace utils
}  // namespace V1_0
}  // namespace omx
}  // namespace media
}  // namespace hardware
}  // namespace android
