/*
 *
 * Copyright 2010 Samsung Electronics S.LSI Co. LTD
 *
 * 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.
 */

/*
 * @file        SEC_OSAL_Buffer.c
 * @brief
 * @author      SeungBeom Kim (sbcrux.kim@samsung.com)
 *              Jinsung Yang (jsgood.yang@samsung.com)
 * @version     1.0.2
 * @history
 *   2011.5.15 : Create
 */

#ifdef __cplusplus
extern "C" {
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "SEC_OMX_Def.h"
#include "SEC_OMX_Macros.h"
#include "SEC_OSAL_Memory.h"
#include "SEC_OSAL_Semaphore.h"
#include "SEC_OSAL_Buffer.h"
#include "SEC_OMX_Basecomponent.h"

#define SEC_LOG_OFF
#include "SEC_OSAL_Log.h"

#ifdef __cplusplus
}
#endif

#include <cutils/properties.h>
#include <ui/GraphicBuffer.h>
#include <ui/GraphicBufferMapper.h>
#include <ui/Rect.h>
#include <HardwareAPI.h>
#include <hardware/hardware.h>
#include <MetadataBufferType.h>
#include "hal_public.h"

#define HAL_PIXEL_FORMAT_C110_NV12          0x100

using namespace android;


struct AndroidNativeBuffersParams {
    OMX_U32 nSize;
    OMX_VERSIONTYPE nVersion;
    OMX_U32 nPortIndex;
};

#ifdef USE_ANDROID_EXTENSION
OMX_ERRORTYPE checkVersionANB(OMX_PTR ComponentParameterStructure)
{
    OMX_ERRORTYPE ret = OMX_ErrorNone;
    OMX_VERSIONTYPE* version = NULL;


    AndroidNativeBuffersParams *pANBP;
    pANBP = (AndroidNativeBuffersParams *)ComponentParameterStructure;

    version = (OMX_VERSIONTYPE*)((char*)pANBP + sizeof(OMX_U32));
    if (*((OMX_U32*)pANBP) <= sizeof(AndroidNativeBuffersParams)) {
        ret = OMX_ErrorBadParameter;
        goto EXIT;
    }
    if (version->s.nVersionMajor != VERSIONMAJOR_NUMBER ||
        version->s.nVersionMinor != VERSIONMINOR_NUMBER) {
        ret = OMX_ErrorVersionMismatch;
        goto EXIT;
    }

    ret = OMX_ErrorNone;

EXIT:
    return ret;
}

OMX_U32 checkPortIndexANB(OMX_PTR ComponentParameterStructure)
{
    AndroidNativeBuffersParams *pANBP;
    pANBP = (AndroidNativeBuffersParams *)ComponentParameterStructure;

    return pANBP->nPortIndex;
}

OMX_U32 getMetadataBufferType(const uint8_t *ptr)
{
    OMX_U32 type = *(OMX_U32 *) ptr;
    SEC_OSAL_Log(SEC_LOG_TRACE, "getMetadataBufferType: %ld", type);
    return type;
}

OMX_U32 getVADDRfromANB(OMX_PTR pUnreadableBuffer, OMX_U32 Width, OMX_U32 Height, void *pVirAddrs[])
{
    OMX_U32 ret = 0;
    android_native_buffer_t *buf;
    void *readableBuffer;
    GraphicBufferMapper &mapper = GraphicBufferMapper::get();
    Rect bounds(Width, Height);

    FunctionIn();

    buf = (android_native_buffer_t *)pUnreadableBuffer;
    SEC_OSAL_Log(SEC_LOG_TRACE, "pUnreadableBuffer:0x%x, buf:0x%x, buf->handle:0x%x",
                                pUnreadableBuffer, buf, buf->handle);

    ret = mapper.lock(buf->handle, GRALLOC_USAGE_SW_WRITE_OFTEN, bounds, pVirAddrs);
    if (ret != 0) {
        SEC_OSAL_Log(SEC_LOG_ERROR, "mapper.lock Error, Error code:%d", ret);
    }
    FunctionOut();

    return ret;
}

OMX_U32 putVADDRtoANB(OMX_PTR pUnreadableBuffer)
{
    android_native_buffer_t *buf;
    void *readableBuffer;
    int ret = 0;
    GraphicBufferMapper &mapper = GraphicBufferMapper::get();

    FunctionIn();

    buf = (android_native_buffer_t *)pUnreadableBuffer;

    FunctionOut();

    return mapper.unlock(buf->handle);
}

OMX_ERRORTYPE enableAndroidNativeBuffer(OMX_HANDLETYPE hComponent, OMX_PTR ComponentParameterStructure)
{
    OMX_ERRORTYPE          ret = OMX_ErrorNone;
    OMX_COMPONENTTYPE     *pOMXComponent = NULL;
    SEC_OMX_BASECOMPONENT *pSECComponent = NULL;
    SEC_OMX_BASEPORT      *pSECPort = NULL;

    EnableAndroidNativeBuffersParams *peanbp;

    FunctionIn();

    if (hComponent == NULL) {
        ret = OMX_ErrorBadParameter;
        goto EXIT;
    }
    pOMXComponent = (OMX_COMPONENTTYPE *)hComponent;
    if (pOMXComponent->pComponentPrivate == NULL) {
        ret = OMX_ErrorBadParameter;
        goto EXIT;
    }
    pSECComponent = (SEC_OMX_BASECOMPONENT *)pOMXComponent->pComponentPrivate;
    peanbp = (EnableAndroidNativeBuffersParams *)ComponentParameterStructure;
    pSECPort = &pSECComponent->pSECPort[peanbp->nPortIndex];

    if (peanbp->enable == OMX_FALSE) {
        SEC_OSAL_Log(SEC_LOG_TRACE, "disable AndroidNativeBuffer");
        pSECPort->bUseAndroidNativeBuffer = OMX_FALSE;
    } else {
        SEC_OSAL_Log(SEC_LOG_TRACE, "enable AndroidNativeBuffer");
        pSECPort->bUseAndroidNativeBuffer = OMX_TRUE;
        pSECPort->portDefinition.format.video.eColorFormat = (OMX_COLOR_FORMATTYPE)OMX_SEC_COLOR_FormatANBYUV420SemiPlanar;
    }

    ret = OMX_ErrorNone;

EXIT:
    FunctionOut();

    return ret;
}

OMX_ERRORTYPE getAndroidNativeBuffer(OMX_HANDLETYPE hComponent, OMX_PTR ComponentParameterStructure)
{
    OMX_ERRORTYPE          ret = OMX_ErrorNone;
    OMX_COMPONENTTYPE     *pOMXComponent = NULL;
    SEC_OMX_BASECOMPONENT *pSECComponent = NULL;
    SEC_OMX_BASEPORT      *pSECPort = NULL;

    GetAndroidNativeBufferUsageParams *pganbp;

    FunctionIn();

    pganbp = (GetAndroidNativeBufferUsageParams *)ComponentParameterStructure;

    pganbp->nUsage = GRALLOC_USAGE_SW_WRITE_OFTEN;

    ret = OMX_ErrorNone;

EXIT:
    FunctionOut();

    return ret;
}

OMX_ERRORTYPE UseBufferANB(
    OMX_IN OMX_HANDLETYPE            hComponent,
    OMX_INOUT OMX_BUFFERHEADERTYPE **ppBufferHdr,
    OMX_IN OMX_U32                   nPortIndex,
    OMX_IN OMX_PTR                   pAppPrivate,
    OMX_IN OMX_U32                   nSizeBytes,
    OMX_IN OMX_U8                   *pBuffer)
{
    OMX_ERRORTYPE          ret = OMX_ErrorNone;
    OMX_COMPONENTTYPE     *pOMXComponent = NULL;
    SEC_OMX_BASECOMPONENT *pSECComponent = NULL;
    SEC_OMX_BASEPORT      *pSECPort = NULL;
    OMX_BUFFERHEADERTYPE  *temp_bufferHeader = NULL;
    int                    i = 0;

    FunctionIn();

    if (hComponent == NULL) {
        ret = OMX_ErrorBadParameter;
        goto EXIT;
    }
    pOMXComponent = (OMX_COMPONENTTYPE *)hComponent;
    if (pOMXComponent->pComponentPrivate == NULL) {
        ret = OMX_ErrorBadParameter;
        goto EXIT;
    }
    pSECComponent = (SEC_OMX_BASECOMPONENT *)pOMXComponent->pComponentPrivate;

    pSECPort = &pSECComponent->pSECPort[nPortIndex];
    if (nPortIndex >= pSECComponent->portParam.nPorts) {
        ret = OMX_ErrorBadPortIndex;
        goto EXIT;
    }
    if (pSECPort->portState != OMX_StateIdle) {
        ret = OMX_ErrorIncorrectStateOperation;
        goto EXIT;
    }

    if (CHECK_PORT_TUNNELED(pSECPort) && CHECK_PORT_BUFFER_SUPPLIER(pSECPort)) {
        ret = OMX_ErrorBadPortIndex;
        goto EXIT;
    }

    temp_bufferHeader = (OMX_BUFFERHEADERTYPE *)SEC_OSAL_Malloc(sizeof(OMX_BUFFERHEADERTYPE));
    if (temp_bufferHeader == NULL) {
        ret = OMX_ErrorInsufficientResources;
        goto EXIT;
    }
    SEC_OSAL_Memset(temp_bufferHeader, 0, sizeof(OMX_BUFFERHEADERTYPE));

    for (i = 0; i < pSECPort->portDefinition.nBufferCountActual; i++) {
        if (pSECPort->bufferStateAllocate[i] == BUFFER_STATE_FREE) {
            pSECPort->bufferHeader[i] = temp_bufferHeader;
            pSECPort->bufferStateAllocate[i] = (BUFFER_STATE_ASSIGNED | HEADER_STATE_ALLOCATED);
            INIT_SET_SIZE_VERSION(temp_bufferHeader, OMX_BUFFERHEADERTYPE);
            temp_bufferHeader->pBuffer        = pBuffer;
            temp_bufferHeader->nAllocLen      = nSizeBytes;
            temp_bufferHeader->pAppPrivate    = pAppPrivate;
            if (nPortIndex == INPUT_PORT_INDEX)
                temp_bufferHeader->nInputPortIndex = INPUT_PORT_INDEX;
            else
                temp_bufferHeader->nOutputPortIndex = OUTPUT_PORT_INDEX;

            pSECPort->assignedBufferNum++;
            if (pSECPort->assignedBufferNum == pSECPort->portDefinition.nBufferCountActual) {
                pSECPort->portDefinition.bPopulated = OMX_TRUE;
                /* SEC_OSAL_MutexLock(pSECComponent->compMutex); */
                SEC_OSAL_SemaphorePost(pSECPort->loadedResource);
                /* SEC_OSAL_MutexUnlock(pSECComponent->compMutex); */
            }
            *ppBufferHdr = temp_bufferHeader;
            ret = OMX_ErrorNone;
            goto EXIT;
        }
    }

    SEC_OSAL_Free(temp_bufferHeader);
    ret = OMX_ErrorInsufficientResources;

EXIT:
    FunctionOut();

    return ret;
}

OMX_ERRORTYPE useAndroidNativeBuffer(OMX_HANDLETYPE hComponent, OMX_PTR ComponentParameterStructure)
{
    OMX_ERRORTYPE          ret = OMX_ErrorNone;
    OMX_COMPONENTTYPE     *pOMXComponent = NULL;
    SEC_OMX_BASECOMPONENT *pSECComponent = NULL;
    SEC_OMX_BASEPORT      *pSECPort = NULL;
    OMX_U32                frameSize = 0;
    OMX_U32                bufWidth, bufHeight;
    UseAndroidNativeBufferParams *puanbp;

    FunctionIn();

    puanbp = (UseAndroidNativeBufferParams *)ComponentParameterStructure;

    OMX_PTR buffer = (void *)puanbp->nativeBuffer.get();
    android_native_buffer_t *buf = (android_native_buffer_t *)buffer;
    bufWidth = ((buf->width + 15) / 16) * 16;
    bufHeight = ((buf->height + 15) / 16) * 16;
    frameSize = (bufWidth * bufHeight * 3) / 2;
    SEC_OSAL_Log(SEC_LOG_TRACE, "buffer:0x%x, buf:0x%x, buf->handle:0x%x", buffer, buf, buf->handle);

    ret = UseBufferANB(hComponent, puanbp->bufferHeader, puanbp->nPortIndex,
                       puanbp->pAppPrivate, frameSize, (OMX_U8 *)buffer);

EXIT:
    FunctionOut();

    return ret;
}

OMX_ERRORTYPE enableStoreMetaDataInBuffers(OMX_HANDLETYPE hComponent, OMX_PTR ComponentParameterStructure)
{
    OMX_ERRORTYPE          ret = OMX_ErrorNone;
    OMX_COMPONENTTYPE     *pOMXComponent = NULL;
    SEC_OMX_BASECOMPONENT *pSECComponent = NULL;
    SEC_OMX_BASEPORT      *pSECPort = NULL;

    StoreMetaDataInBuffersParams *pStoreMetaData;

    FunctionIn();

    if (hComponent == NULL) {
        ret = OMX_ErrorBadParameter;
        goto EXIT;
    }
    pOMXComponent = (OMX_COMPONENTTYPE *)hComponent;
    if (pOMXComponent->pComponentPrivate == NULL) {
        ret = OMX_ErrorBadParameter;
        goto EXIT;
    }
    pSECComponent = (SEC_OMX_BASECOMPONENT *)pOMXComponent->pComponentPrivate;
    pStoreMetaData = (StoreMetaDataInBuffersParams*)ComponentParameterStructure;
    pSECPort = &pSECComponent->pSECPort[pStoreMetaData->nPortIndex];

    if (pStoreMetaData->bStoreMetaData == OMX_FALSE) {
        SEC_OSAL_Log(SEC_LOG_TRACE, "disable StoreMetaDataInBuffers");
        pSECPort->bStoreMetaDataInBuffer = OMX_FALSE;
    } else {
        SEC_OSAL_Log(SEC_LOG_TRACE, "enable StoreMetaDataInBuffers");
        pSECPort->bStoreMetaDataInBuffer = OMX_TRUE;
    }

EXIT:
    FunctionOut();

    return ret;
}

OMX_BOOL isMetadataBufferTypeGrallocSource(OMX_BYTE pInputDataBuffer)
{
    OMX_U32 type = getMetadataBufferType(pInputDataBuffer);

    if (type == kMetadataBufferTypeGrallocSource)
        return OMX_TRUE;
    else
        return OMX_FALSE;
}

OMX_ERRORTYPE preprocessMetaDataInBuffers(OMX_HANDLETYPE hComponent, OMX_BYTE pInputDataBuffer, BUFFER_ADDRESS_INFO *pInputInfo)
{
    OMX_ERRORTYPE          ret = OMX_ErrorNone;
    OMX_COMPONENTTYPE     *pOMXComponent = NULL;
    SEC_OMX_BASECOMPONENT *pSECComponent = NULL;
    SEC_OMX_BASEPORT      *pSECPort = NULL;
    OMX_U32                type = 0;

    FunctionIn();

    if (hComponent == NULL) {
        ret = OMX_ErrorBadParameter;
        goto EXIT;
    }
    pOMXComponent = (OMX_COMPONENTTYPE *)hComponent;
    if (pOMXComponent->pComponentPrivate == NULL) {
        ret = OMX_ErrorBadParameter;
        goto EXIT;
    }
    pSECComponent = (SEC_OMX_BASECOMPONENT *)pOMXComponent->pComponentPrivate;
    pSECPort = &pSECComponent->pSECPort[INPUT_PORT_INDEX];

    type = getMetadataBufferType(pInputDataBuffer);
    if (type == kMetadataBufferTypeCameraSource) {
        SEC_OSAL_Memcpy(&pInputInfo->YPhyAddr, pInputDataBuffer + 4, sizeof(void *));
        SEC_OSAL_Memcpy(&pInputInfo->CPhyAddr, pInputDataBuffer + 4 + sizeof(void *), sizeof(void *));
    } else if (type == kMetadataBufferTypeGrallocSource){
        IMG_gralloc_module_public_t *module = (IMG_gralloc_module_public_t *)pSECPort->pIMGGrallocModule;
        OMX_PTR pUnreadableBuffer = NULL;
        OMX_PTR pReadableBuffer = NULL;
        OMX_PTR pVirAddrs[3];
        int err = 0;

        pVirAddrs[0] = pSECComponent->processData[INPUT_PORT_INDEX].specificBufferHeader.YVirAddr;
        pVirAddrs[1] = pSECComponent->processData[INPUT_PORT_INDEX].specificBufferHeader.CVirAddr;
        pVirAddrs[2] = NULL;

        if (module == NULL) {
            err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, (const hw_module_t **)&module);
            if(err) {
                SEC_OSAL_Log(SEC_LOG_ERROR, "hw_get_module failed (err=%d)\n", err);
                ret = OMX_ErrorUndefined;
                goto EXIT;
            }
            pSECPort->pIMGGrallocModule = (OMX_PTR)module;
        }

        /**************************************/
        /*     IMG CSC RGB to NV12            */
        /**************************************/
        buffer_handle_t buf = *((buffer_handle_t *) (pInputDataBuffer + 4));
        SEC_OSAL_Log(SEC_LOG_TRACE, "buffer handle %p)\n", buf);
        err = module->Blit(module, buf, pVirAddrs, HAL_PIXEL_FORMAT_C110_NV12);
        if(err) {
            SEC_OSAL_Log(SEC_LOG_ERROR, "module->Blit() failed (err=%d)\n", err);
            ret = OMX_ErrorUndefined;
            goto EXIT;
        }

        pInputInfo->YPhyAddr = pSECComponent->processData[INPUT_PORT_INDEX].specificBufferHeader.YPhyAddr;
        pInputInfo->CPhyAddr = pSECComponent->processData[INPUT_PORT_INDEX].specificBufferHeader.CPhyAddr;
    } else {
        ret = OMX_ErrorNotImplemented;
        goto EXIT;
    }

EXIT:
    FunctionOut();

    return ret;
}

int isTvOutEnabled() {
    char value[PROPERTY_VALUE_MAX];
    property_get("init.svc.tvouthack", value, "");
    return (strcmp(value, "running") == 0);
}

#endif
