/*
 * Copyright (C) 2013 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 "rsAllocation.h"
#include "rsContext.h"
#include "rsGrallocConsumer.h"
#include "rs_hal.h"

namespace android {
namespace renderscript {

GrallocConsumer::GrallocConsumer (const Context *rsc, Allocation *a, uint32_t numAlloc)
{
    mCtx = rsc;
    mAlloc = new Allocation *[numAlloc];
    mAcquiredBuffer = new AcquiredBuffer[numAlloc];
    isIdxUsed = new bool[numAlloc];

    mAlloc[0] = a;
    isIdxUsed[0] = true;
    mNumAlloc = numAlloc;

    uint32_t width  = a->mHal.drvState.lod[0].dimX;
    uint32_t height = a->mHal.drvState.lod[0].dimY;
    if (height < 1) height = 1;

    int32_t format = AIMAGE_FORMAT_RGBA_8888;
    if (a->mHal.state.yuv) {
        format = AIMAGE_FORMAT_YUV_420_888;
    }

    media_status_t ret = AImageReader_new(
            width, height, format,
            mNumAlloc, &mImgReader);
    if (ret != AMEDIA_OK || mImgReader == nullptr) {
        ALOGE("Error creating image reader. ret %d", ret);
    }

    ret = AImageReader_getWindow(mImgReader, &mNativeWindow);
    if (ret != AMEDIA_OK || mNativeWindow == nullptr) {
        ALOGE("Error creating native window. ret %d", ret);
    }

    mReaderCb = {this, GrallocConsumer::onFrameAvailable};
    ret = AImageReader_setImageListener(mImgReader, &mReaderCb);

    for (uint32_t i = 1; i < numAlloc; i++) {
        isIdxUsed[i] = false;
    }
}

GrallocConsumer::~GrallocConsumer() {
    AImageReader_delete(mImgReader);
    delete[] mAlloc;
    delete[] mAcquiredBuffer;
    delete[] isIdxUsed;
}

void GrallocConsumer::onFrameAvailable(void* obj, AImageReader* reader) {
    GrallocConsumer* consumer = (GrallocConsumer *) obj;
    for (uint32_t i = 0; i < consumer->mNumAlloc; i++) {
        if (consumer->mAlloc[i] != nullptr) {
            intptr_t ip = (intptr_t)(consumer->mAlloc[i]);
            consumer->mCtx->sendMessageToClient(&ip,
                RS_MESSAGE_TO_CLIENT_NEW_BUFFER, 0, sizeof(ip), true);
        }
    }
}

ANativeWindow* GrallocConsumer::getNativeWindow() {
    return mNativeWindow;
}

media_status_t GrallocConsumer::lockNextBuffer(uint32_t idx) {
    media_status_t ret;

    if (idx >= mNumAlloc) {
        ALOGE("Invalid buffer index: %d", idx);
        return AMEDIA_ERROR_INVALID_PARAMETER;
    }

    if (mAcquiredBuffer[idx].mImg != nullptr) {
        ret = unlockBuffer(idx);
        if (ret != AMEDIA_OK) {
            return ret;
        }
    }

    ret = AImageReader_acquireNextImage(mImgReader, &(mAcquiredBuffer[idx].mImg));
    if (ret != AMEDIA_OK || mAcquiredBuffer[idx].mImg == nullptr) {
        ALOGE("%s: acquire image from reader %p failed! ret: %d, img %p",
                __FUNCTION__, mImgReader, ret, mAcquiredBuffer[idx].mImg);
        return ret;
    }

    AImage *img = mAcquiredBuffer[idx].mImg;
    int32_t format = -1;
    ret = AImage_getFormat(img, &format);
    if (ret != AMEDIA_OK || format == -1) {
        ALOGE("%s: get format for image %p failed! ret: %d, format %d",
                 __FUNCTION__, img, ret, format);
        return ret;
    }

    if (format != AIMAGE_FORMAT_YUV_420_888 && format != AIMAGE_FORMAT_RGBA_8888) {
        ALOGE("Format %d not supported", format);
        return AMEDIA_ERROR_INVALID_OBJECT;
    }

    uint8_t *data = nullptr;
    int dataLength = 0;
    ret =  AImage_getPlaneData(img, 0, &data, &dataLength);
    if (ret != AMEDIA_OK || data == nullptr || dataLength <= 0) {
        ALOGE("%s: get data for image %p failed! ret: %d, data %p, len %d",
                __FUNCTION__, img, ret, data, dataLength);
        return ret;
    }

    int64_t timestamp = -1;
    ret = AImage_getTimestamp(img, &timestamp);
    if (ret != AMEDIA_OK || timestamp == -1) {
        ALOGE("%s: get timestamp for image %p failed! ret: %d",
                __FUNCTION__, img, ret);
        return ret;
    }

    int32_t rowstride = -1;
    ret = AImage_getPlaneRowStride(img, 0, &rowstride);
    if (ret != AMEDIA_OK || rowstride == -1) {
        ALOGE("%s: get row stride for image %p failed! ret: %d, rowstride %d",
                __FUNCTION__, img, ret, rowstride);
        return ret;
    }

    AHardwareBuffer *hardwareBuffer = nullptr;
    ret =  AImage_getHardwareBuffer(img, &hardwareBuffer);
    if (ret != AMEDIA_OK || hardwareBuffer == nullptr) {
        ALOGE("%s: get hardware buffer for image %p failed! ret: %d",
                __FUNCTION__, img, ret);
        return ret;
    }

    mAcquiredBuffer[idx].mBufferPointer = data;

    mAlloc[idx]->mHal.drvState.lod[0].mallocPtr = data;
    mAlloc[idx]->mHal.drvState.lod[0].stride = rowstride;
    mAlloc[idx]->mHal.state.nativeBuffer = hardwareBuffer;
    mAlloc[idx]->mHal.state.timestamp = timestamp;

    if (format == AIMAGE_FORMAT_YUV_420_888) {
        const int yWidth = mAlloc[idx]->mHal.drvState.lod[0].dimX;
        const int yHeight = mAlloc[idx]->mHal.drvState.lod[0].dimY;

        const int cWidth = yWidth / 2;
        const int cHeight = yHeight / 2;

        uint8_t *uData = nullptr;
        int uDataLength = 0;
        ret =  AImage_getPlaneData(img, 1, &uData, &uDataLength);
        if (ret != AMEDIA_OK || uData == nullptr || uDataLength <= 0) {
            ALOGE("%s: get U data for image %p failed! ret: %d, data %p, len %d",
                    __FUNCTION__, img, ret, uData, uDataLength);
            return ret;
        }

        uint8_t *vData = nullptr;
        int vDataLength = 0;
        ret =  AImage_getPlaneData(img, 2, &vData, &vDataLength);
        if (ret != AMEDIA_OK || vData == nullptr || vDataLength <= 0) {
            ALOGE("%s: get V data for image %p failed! ret: %d, data %p, len %d",
                    __FUNCTION__, img, ret, vData, vDataLength);
            return ret;
        }

        int32_t uRowStride = -1;
        ret = AImage_getPlaneRowStride(img, 1, &uRowStride);
        if (ret != AMEDIA_OK || uRowStride == -1) {
            ALOGE("%s: get U row stride for image %p failed! ret: %d, uRowStride %d",
                    __FUNCTION__, img, ret, uRowStride);
            return ret;
        }

        int32_t vRowStride = -1;
        ret = AImage_getPlaneRowStride(img, 2, &vRowStride);
        if (ret != AMEDIA_OK || vRowStride == -1) {
            ALOGE("%s: get V row stride for image %p failed! ret: %d, vRowStride %d",
                    __FUNCTION__, img, ret, vRowStride);
            return ret;
        }

        int32_t uPixStride = -1;
        ret = AImage_getPlanePixelStride(img, 1, &uPixStride);
        if (ret != AMEDIA_OK || uPixStride == -1) {
            ALOGE("%s: get U pixel stride for image %p failed! ret: %d, uPixStride %d",
                    __FUNCTION__, img, ret, uPixStride);
            return ret;
        }

        mAlloc[idx]->mHal.drvState.lod[1].dimX = cWidth;
        mAlloc[idx]->mHal.drvState.lod[1].dimY = cHeight;
        mAlloc[idx]->mHal.drvState.lod[2].dimX = cWidth;
        mAlloc[idx]->mHal.drvState.lod[2].dimY = cHeight;

        mAlloc[idx]->mHal.drvState.lod[1].mallocPtr = uData;
        mAlloc[idx]->mHal.drvState.lod[2].mallocPtr = vData;

        mAlloc[idx]->mHal.drvState.lod[1].stride = uRowStride;
        mAlloc[idx]->mHal.drvState.lod[2].stride = vRowStride;

        mAlloc[idx]->mHal.drvState.yuv.shift = 1;
        mAlloc[idx]->mHal.drvState.yuv.step = uPixStride;
        mAlloc[idx]->mHal.drvState.lodCount = 3;
    }

    return AMEDIA_OK;
}

media_status_t GrallocConsumer::unlockBuffer(uint32_t idx) {
    media_status_t ret;

    if (idx >= mNumAlloc) {
        ALOGE("Invalid buffer index: %d", idx);
        return AMEDIA_ERROR_INVALID_PARAMETER;
    }
    if (mAcquiredBuffer[idx].mImg == nullptr) {
       return AMEDIA_OK;
    }

    AImage_delete(mAcquiredBuffer[idx].mImg);
    mAcquiredBuffer[idx].mImg = nullptr;
    return AMEDIA_OK;
}

uint32_t GrallocConsumer::getNextAvailableIdx(Allocation *a) {
    for (uint32_t i = 0; i < mNumAlloc; i++) {
        if (isIdxUsed[i] == false) {
            mAlloc[i] = a;
            isIdxUsed[i] = true;
            return i;
        }
    }
    return mNumAlloc;
}

bool GrallocConsumer::releaseIdx(uint32_t idx) {
    if (idx >= mNumAlloc) {
        ALOGE("Invalid buffer index: %d", idx);
        return false;
    }
    if (isIdxUsed[idx] == false) {
        ALOGV("Buffer index already released: %d", idx);
        return true;
    }
    media_status_t ret;
    ret = unlockBuffer(idx);
    if (ret != OK) {
        ALOGE("Unable to unlock graphic buffer");
        return false;
    }
    mAlloc[idx] = nullptr;
    isIdxUsed[idx] = false;
    return true;
}

bool GrallocConsumer::isActive() {
    for (uint32_t i = 0; i < mNumAlloc; i++) {
        if (isIdxUsed[i]) {
            return true;
        }
    }
    return false;
}

} // namespace renderscript
} // namespace android