/*
 * Copyright (C) 2011 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.
 */
/**
*************************************************************************
 * @file    M4DECODER_Null.c
 * @brief   Implementation of the Null decoder public interface
 * @note    This file implements a "null" video decoder, i.e. a decoder
 *          that does nothing
*************************************************************************
*/
#include "NXPSW_CompilerSwitches.h"

#include "M4OSA_Types.h"
#include "M4OSA_Debug.h"
#include "M4TOOL_VersionInfo.h"
#include "M4DA_Types.h"
#include "M4DECODER_Common.h"
#include "M4DECODER_Null.h"

/**
 ************************************************************************
 * NULL Video Decoder version information
 ************************************************************************
*/
/* CHANGE_VERSION_HERE */
#define M4DECODER_NULL_MAJOR    1
#define M4DECODER_NULL_MINOR    0
#define M4DECODER_NULL_REVISION 0

/**
 ************************************************************************
 * structure    M4_VideoHandler_Context
 * @brief       Defines the internal context of a video decoder instance
 * @note        The context is allocated and freed by the video decoder
 ************************************************************************
*/
typedef struct {
    void*                    m_pLibrary;            // Core library identifier
    M4OSA_Int32              m_DecoderId;           // Core decoder identifier
    M4OSA_Int32              m_RendererId;          // Core renderer identifier
    M4_VideoStreamHandler*   m_pVideoStreamhandler; // Video stream description
    M4_AccessUnit*           m_pNextAccessUnitToDecode; // Access unit used to
                                                        // read and decode one frame
    void*                    m_pUserData;           // Pointer to any user data
    M4READER_DataInterface*  m_pReader;             // Reader data interface
    M4OSA_Bool               m_bDoRendering;        // Decides if render required
    M4OSA_Int32              m_structSize;          // Size of the structure

    M4DECODER_OutputFilter* m_pVideoFilter;         // Color conversion filter
    M4VIFI_ImagePlane       *pDecYuvData;           // Pointer to Yuv data plane
    M4VIFI_ImagePlane       *pDecYuvWithEffect;     // Pointer to Yuv plane with color effect
    M4OSA_Bool               bYuvWithEffectSet;     // Original Yuv data OR Yuv with color effect

} M4_VideoHandler_Context;

/***********************************************************************/
/************** M4DECODER_VideoInterface implementation ****************/
/***********************************************************************/

/**
 ************************************************************************
 * @brief   Creates an instance of the decoder
 * @note    Allocates the context
 *
 * @param   pContext:       (OUT)   Context of the decoder
 * @param   pStreamHandler: (IN)    Pointer to a video stream description
 * @param   pSrcInterface:  (IN)    Pointer to the M4READER_DataInterface
 *                                  structure that must be used by the
 *                                  decoder to read data from the stream
 * @param   pAccessUnit     (IN)    Pointer to an access unit
 *                                  (allocated by the caller) where decoded data
 *                                  are stored
 *
 * @return  M4NO_ERROR              There is no error
 * @return  M4ERR_STATE             State automaton is not applied
 * @return  M4ERR_ALLOC             A memory allocation has failed
 * @return  M4ERR_PARAMETER         At least one input parameter is not proper
 ************************************************************************
*/
M4OSA_ERR M4DECODER_NULL_create(M4OSA_Context *pContext,
                                M4_StreamHandler *pStreamHandler,
                                M4READER_GlobalInterface *pReaderGlobalInterface,
                                M4READER_DataInterface *pReaderDataInterface,
                                M4_AccessUnit* pAccessUnit,
                                M4OSA_Void* pUserData) {

    M4_VideoHandler_Context* pStreamContext = M4OSA_NULL;

    *pContext = M4OSA_NULL;
    pStreamContext = (M4_VideoHandler_Context*)M4OSA_32bitAlignedMalloc (
                        sizeof(M4_VideoHandler_Context), M4DECODER_MPEG4,
                        (M4OSA_Char *)"M4_VideoHandler_Context");
    if (pStreamContext == 0) {
        return M4ERR_ALLOC;
    }

    pStreamContext->m_structSize = sizeof(M4_VideoHandler_Context);
    pStreamContext->m_pNextAccessUnitToDecode = M4OSA_NULL;
    pStreamContext->m_pLibrary              = M4OSA_NULL;
    pStreamContext->m_pVideoStreamhandler   = M4OSA_NULL;
    pStreamContext->m_DecoderId             = -1;
    pStreamContext->m_RendererId            = -1;

    pStreamContext->m_pUserData = M4OSA_NULL;
    pStreamContext->m_bDoRendering = M4OSA_TRUE;
    pStreamContext->m_pVideoFilter = M4OSA_NULL;
    pStreamContext->bYuvWithEffectSet = M4OSA_FALSE;

    *pContext=pStreamContext;
    return M4NO_ERROR;
}

/**
 ************************************************************************
 * @brief   Destroy the instance of the decoder
 * @note    After this call the context is invalid
 *
 * @param   context:    (IN)    Context of the decoder
 *
 * @return  M4NO_ERROR          There is no error
 * @return  M4ERR_PARAMETER     The context is invalid
 ************************************************************************
*/
M4OSA_ERR M4DECODER_NULL_destroy(M4OSA_Context pContext) {

    M4_VideoHandler_Context* pStreamContext = (M4_VideoHandler_Context*)pContext;

    M4OSA_DEBUG_IF1((M4OSA_NULL == pStreamContext),
        M4ERR_PARAMETER, "M4DECODER_NULL_destroy: invalid context pointer");

    free(pStreamContext);

    return M4NO_ERROR;
}

/**
 ************************************************************************
 * @brief   Get an option value from the decoder
 * @note    This function allows the caller to retrieve a property value:
 *
 * @param   context:    (IN)        Context of the decoder
 * @param   optionId:   (IN)        Indicates the option to get
 * @param   pValue:     (IN/OUT)    Pointer to structure or value where
 *                                  option is stored
 *
 * @return  M4NO_ERROR              There is no error
 * @return  M4ERR_PARAMETER         The context is invalid (in DEBUG only)
 * @return  M4ERR_BAD_OPTION_ID     When the option ID is not a valid one
 * @return  M4ERR_STATE             State automaton is not applied
 * @return  M4ERR_NOT_IMPLEMENTED   Function not implemented
 ************************************************************************
*/
M4OSA_ERR M4DECODER_NULL_getOption(M4OSA_Context context,
                                   M4OSA_OptionID optionId,
                                   M4OSA_DataOption  pValue) {

    return M4ERR_NOT_IMPLEMENTED;
}

/**
 ************************************************************************
 * @brief   Set an option value of the decoder
 * @note    Allows the caller to set a property value:
 *
 * @param   context:    (IN)        Context of the decoder
 * @param   optionId:   (IN)        Identifier indicating the option to set
 * @param   pValue:     (IN)        Pointer to structure or value
 *                                  where option is stored
 *
 * @return  M4NO_ERROR              There is no error
 * @return  M4ERR_BAD_OPTION_ID     The option ID is not a valid one
 * @return  M4ERR_STATE             State automaton is not applied
 * @return  M4ERR_PARAMETER         The option parameter is invalid
 ************************************************************************
*/
M4OSA_ERR M4DECODER_NULL_setOption(M4OSA_Context context,
                                   M4OSA_OptionID optionId,
                                   M4OSA_DataOption pValue) {

    M4DECODER_OutputFilter *pFilterOption;

    M4_VideoHandler_Context *pStreamContext =
        (M4_VideoHandler_Context*)context;

    M4OSA_ERR err = M4NO_ERROR;
    M4OSA_UInt32 height = 0;
    M4OSA_UInt8 *p_src,*p_des;
    M4VIFI_ImagePlane* pTempDecYuvData = M4OSA_NULL;

    switch (optionId) {
        case M4DECODER_kOptionID_DecYuvData:
            pStreamContext->pDecYuvData = (M4VIFI_ImagePlane *)pValue;
            break;

        case M4DECODER_kOptionID_YuvWithEffectContiguous:
            pStreamContext->pDecYuvWithEffect = (M4VIFI_ImagePlane *)pValue;
            break;

        case M4DECODER_kOptionID_EnableYuvWithEffect:
            pStreamContext->bYuvWithEffectSet = (M4OSA_Bool)pValue;
            break;

        case M4DECODER_kOptionID_YuvWithEffectNonContiguous:
            pTempDecYuvData =  (M4VIFI_ImagePlane *)pValue;

            p_des = pStreamContext->pDecYuvWithEffect[0].pac_data +
                 pStreamContext->pDecYuvWithEffect[0].u_topleft;
            p_src = pTempDecYuvData[0].pac_data +
                 pTempDecYuvData[0].u_topleft;

            for (height = 0; height<pStreamContext->pDecYuvWithEffect[0].u_height;
             height++) {
                memcpy((void *)p_des, (void *)p_src,
                 pStreamContext->pDecYuvWithEffect[0].u_width);

                p_des += pStreamContext->pDecYuvWithEffect[0].u_stride;
                p_src += pTempDecYuvData[0].u_stride;
            }

            p_des = pStreamContext->pDecYuvWithEffect[1].pac_data +
             pStreamContext->pDecYuvWithEffect[1].u_topleft;
            p_src = pTempDecYuvData[1].pac_data +
             pTempDecYuvData[1].u_topleft;

            for (height = 0; height<pStreamContext->pDecYuvWithEffect[1].u_height;
             height++) {
                memcpy((void *)p_des, (void *)p_src,
                 pStreamContext->pDecYuvWithEffect[1].u_width);

                p_des += pStreamContext->pDecYuvWithEffect[1].u_stride;
                p_src += pTempDecYuvData[1].u_stride;
            }

            p_des = pStreamContext->pDecYuvWithEffect[2].pac_data +
             pStreamContext->pDecYuvWithEffect[2].u_topleft;
            p_src = pTempDecYuvData[2].pac_data +
             pTempDecYuvData[2].u_topleft;

            for (height = 0; height<pStreamContext->pDecYuvWithEffect[2].u_height;
             height++) {
                memcpy((void *)p_des, (void *)p_src,
                 pStreamContext->pDecYuvWithEffect[2].u_width);

                p_des += pStreamContext->pDecYuvWithEffect[2].u_stride;
                p_src += pTempDecYuvData[2].u_stride;
            }
            break;

        case M4DECODER_kOptionID_OutputFilter:
            pFilterOption = (M4DECODER_OutputFilter*)pValue;
            break;

        case M4DECODER_kOptionID_DeblockingFilter:
            err = M4ERR_BAD_OPTION_ID;
            break;

        default:
            err = M4ERR_BAD_OPTION_ID;
            break;
    }
    return err;
}

/**
 ************************************************************************
 * @brief   Decode video Access Units up to a target time
 * @note    Parse and decode the video until it can output a decoded image
 *          for which the composition time is equal or greater to the
 *          passed targeted time.
 *          The data are read from the reader data interface passed to
 *          M4DECODER_MPEG4_create.
 *
 * @param   context:    (IN)        Context of the decoder
 * @param   pTime:      (IN/OUT)    IN: Time to decode up to (in msec)
 *                                  OUT:Time of the last decoded frame (in msec)
 * @param   bJump:      (IN)        0 if no jump occured just before this call
 *                                  1 if a a jump has just been made
 * @return  M4NO_ERROR              there is no error
 * @return  M4ERR_PARAMETER         at least one parameter is not properly set
 * @return  M4WAR_NO_MORE_AU        there is no more access unit to decode (EOS)
 ************************************************************************
*/
M4OSA_ERR M4DECODER_NULL_decode(M4OSA_Context context,
                                M4_MediaTime* pTime, M4OSA_Bool bJump,
                                M4OSA_UInt32 tolerance) {

    // Do nothing; input time stamp itself returned
    return M4NO_ERROR;
}

/**
 ************************************************************************
 * @brief   Renders the video at the specified time.
 * @note
 * @param   context:     (IN)       Context of the decoder
 * @param   pTime:       (IN/OUT)   IN: Time to render to (in msecs)
 *                                  OUT:Time of the rendered frame (in ms)
 * @param   pOutputPlane:(OUT)      Output plane filled with decoded data
 * @param   bForceRender:(IN)       1 if the image must be rendered even it
 *                                  has been rendered already
 *                                  0 if not
 *
 * @return  M4NO_ERROR              There is no error
 * @return  M4ERR_PARAMETER         At least one parameter is not properly set
 * @return  M4ERR_STATE             State automaton is not applied
 * @return  M4ERR_ALLOC             There is no more available memory
 * @return  M4WAR_VIDEORENDERER_NO_NEW_FRAME    If the frame has already been rendered
 ************************************************************************
*/
M4OSA_ERR M4DECODER_NULL_render(M4OSA_Context context, M4_MediaTime* pTime,
                                M4VIFI_ImagePlane* pOutputPlane,
                                M4OSA_Bool bForceRender) {

    M4OSA_ERR err = M4NO_ERROR;
    M4OSA_UInt32 height;
    M4OSA_UInt8 *p_src,*p_des;
    M4_VideoHandler_Context*    pStreamContext =
        (M4_VideoHandler_Context*)context;

    if (pStreamContext->bYuvWithEffectSet == M4OSA_TRUE) {

        p_des = pOutputPlane[0].pac_data + pOutputPlane[0].u_topleft;
        p_src = pStreamContext->pDecYuvWithEffect[0].pac_data +
         pStreamContext->pDecYuvWithEffect[0].u_topleft;

        for (height = 0; height<pOutputPlane[0].u_height; height++) {
            memcpy((void *)p_des, (void *)p_src, pOutputPlane[0].u_width);
            p_des += pOutputPlane[0].u_stride;
            p_src += pStreamContext->pDecYuvWithEffect[0].u_stride;
        }

        p_des = pOutputPlane[1].pac_data + pOutputPlane[1].u_topleft;
        p_src = pStreamContext->pDecYuvWithEffect[1].pac_data +
         pStreamContext->pDecYuvWithEffect[1].u_topleft;

        for (height = 0; height<pOutputPlane[1].u_height; height++) {
            memcpy((void *)p_des, (void *)p_src, pOutputPlane[1].u_width);
            p_des += pOutputPlane[1].u_stride;
            p_src += pStreamContext->pDecYuvWithEffect[1].u_stride;
        }

        p_des = pOutputPlane[2].pac_data + pOutputPlane[2].u_topleft;
        p_src = pStreamContext->pDecYuvWithEffect[2].pac_data +
         pStreamContext->pDecYuvWithEffect[2].u_topleft;

        for (height = 0; height<pOutputPlane[2].u_height; height++) {
            memcpy((void *)p_des, (void *)p_src, pOutputPlane[2].u_width);
            p_des += pOutputPlane[2].u_stride;
            p_src += pStreamContext->pDecYuvWithEffect[2].u_stride;
        }
    } else {

        p_des = pOutputPlane[0].pac_data + pOutputPlane[0].u_topleft;
        p_src = pStreamContext->pDecYuvData[0].pac_data +
         pStreamContext->pDecYuvData[0].u_topleft;

        for (height = 0; height<pOutputPlane[0].u_height; height++) {
            memcpy((void *)p_des, (void *)p_src, pOutputPlane[0].u_width);
            p_des += pOutputPlane[0].u_stride;
            p_src += pStreamContext->pDecYuvData[0].u_stride;
        }

        p_des = pOutputPlane[1].pac_data + pOutputPlane[1].u_topleft;
        p_src = pStreamContext->pDecYuvData[1].pac_data +
         pStreamContext->pDecYuvData[1].u_topleft;

        for (height = 0; height<pOutputPlane[1].u_height; height++) {
            memcpy((void *)p_des, (void *)p_src, pOutputPlane[1].u_width);
            p_des += pOutputPlane[1].u_stride;
            p_src += pStreamContext->pDecYuvData[1].u_stride;
        }

        p_des = pOutputPlane[2].pac_data + pOutputPlane[2].u_topleft;
        p_src = pStreamContext->pDecYuvData[2].pac_data +
         pStreamContext->pDecYuvData[2].u_topleft;

        for (height = 0; height<pOutputPlane[2].u_height; height++) {
            memcpy((void *)p_des,(void *)p_src,pOutputPlane[2].u_width);
            p_des += pOutputPlane[2].u_stride;
            p_src += pStreamContext->pDecYuvData[2].u_stride;
        }
    }
    return err;
}

/**
 ************************************************************************
 * @brief Retrieves the interface implemented by the decoder
 * @param pDecoderType        : Pointer to a M4DECODER_VideoType
 *                             (allocated by the caller)
 *                             that will be filled with the decoder type
 * @param pDecoderInterface   : Address of a pointer that will be set to
 *                              the interface implemented by this decoder.
 *                              The interface is a structure allocated by
 *                              this function and must be freed by the caller.
 *
 * @returns : M4NO_ERROR  if OK
 *            M4ERR_ALLOC if allocation failed
 ************************************************************************
*/
M4OSA_ERR M4DECODER_NULL_getInterface (M4DECODER_VideoType *pDecoderType,
                            M4DECODER_VideoInterface **pDecoderInterface) {

    *pDecoderInterface =
        (M4DECODER_VideoInterface*)M4OSA_32bitAlignedMalloc(
         sizeof(M4DECODER_VideoInterface),
         M4DECODER_MPEG4, (M4OSA_Char *)"M4DECODER_VideoInterface");

    if (M4OSA_NULL == *pDecoderInterface) {
        return M4ERR_ALLOC;
    }

    *pDecoderType = M4DECODER_kVideoTypeYUV420P;

    (*pDecoderInterface)->m_pFctCreate    = M4DECODER_NULL_create;
    (*pDecoderInterface)->m_pFctDestroy   = M4DECODER_NULL_destroy;
    (*pDecoderInterface)->m_pFctGetOption = M4DECODER_NULL_getOption;
    (*pDecoderInterface)->m_pFctSetOption = M4DECODER_NULL_setOption;
    (*pDecoderInterface)->m_pFctDecode    = M4DECODER_NULL_decode;
    (*pDecoderInterface)->m_pFctRender    = M4DECODER_NULL_render;

    return M4NO_ERROR;
}