/*
 * Project Name MFC DRIVER IN WINCE
 * Copyright  2009 Samsung Electronics Co, Ltd. All Rights Reserved.
 *
 * This file implements MFC driver.
 *
 * @name MFC DRIVER Module (MfcDecAPI.c)
 * @author Jiyoung Shin (idon.shin@samsung.com)
 * @date 2009/08/05
 */

#include <windows.h>
#include "MfcInterface.h"
#include "CMMAPI.h"
#include "MfcLogMsg.h"
#include "SsbSipMfcApi.h"

#define	 STRM_BUF_SIZE 1024000
#define USR_DATA_START_CODE 		(0x000001B2)
#define VOP_START_CODE				(0x000001B6)
#define MP4_START_CODE				(0x000001)



static void getAByte(char *buff, int *code)
{
	int byte;

	*code = (*code<<8);
	byte = (int)*buff;
	byte &= 0xFF;
	*code |= byte;
}

static MFC_PACKED_MODE isPBPacked(_MFCLIB *pCtx)
{
	char	*strmBuffer = NULL, *userData = NULL;
	int		startCode = 0xFFFFFFFF;

	strmBuffer = (char*)pCtx->virStrmBuf;
    LOG_MSG(LOG_TRACE, "isPBPacked", "++\n");

	while(1)
	{
		while(startCode != USR_DATA_START_CODE)
		{
			if(startCode == VOP_START_CODE)
			{
				LOG_MSG(LOG_TRACE, "isPBPacked", "VOP START Found !!.....return\n");
				LOG_MSG(LOG_WARNING, "isPBPacked", "Non Packed PB\n");
				return MFC_UNPACKED_PB;
			}
			getAByte(strmBuffer, &startCode);
			strmBuffer++;
		}
		LOG_MSG(LOG_TRACE, "isPBPacked", "User Data Found !!\n");

		do{
			if(*strmBuffer == 'p')
			{
				LOG_MSG(LOG_WARNING, "isPBPacked", "Packed PB\n");
				return MFC_PACKED_PB;
			}
			getAByte(strmBuffer, &startCode);
			strmBuffer++;
		}while((startCode >> 8) != MP4_START_CODE);
	}

	LOG_MSG(LOG_WARNING, "isPBPacked", "Non Packed PB\n");
	return MFC_UNPACKED_PB;
}

void *SsbSipMfcDecOpen()
{
	_MFCLIB			*pCTX;
	HANDLE			hMFCOpen, hCMMOpen;

	hMFCOpen = CreateFile(L"MFC1:",
					   GENERIC_READ|GENERIC_WRITE,
					   0,
					   NULL,
					   OPEN_EXISTING,
					   FILE_ATTRIBUTE_NORMAL,
					   NULL);
	if (hMFCOpen == INVALID_HANDLE_VALUE)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcDecOpen", "MFC Open failure\n");
		return NULL;
	}

	pCTX = (_MFCLIB *) malloc(sizeof(_MFCLIB));
	if (pCTX == NULL)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcDecOpen", "malloc failed.\n");
		CloseHandle(hMFCOpen);
		return NULL;
	}

	//CMM init
	hCMMOpen = CreateFile(L"CMM1:",
					   GENERIC_READ|GENERIC_WRITE,
					   0,
					   NULL,
					   OPEN_EXISTING,
					   FILE_ATTRIBUTE_NORMAL,
					   NULL);
	if (hCMMOpen == INVALID_HANDLE_VALUE)
	{
		if(pCTX)
			free(pCTX);

		LOG_MSG(LOG_ERROR, "SsbSipMfcDecOpen", "CMM Open failure\n");
		return NULL;
	}
	
	memset(pCTX, 0, sizeof(_MFCLIB));

	pCTX->hMFC   = hMFCOpen;
	pCTX->hCMM = hCMMOpen;
	pCTX->proc_id = GetCurrentProcessId();

	printf("== Open\npCTX->hMFC : 0x%x\n", pCTX->hMFC);
	printf("pCTX->hCMM : 0x%x\n", pCTX->hCMM);
	return (void *) pCTX;
}

SSBSIP_MFC_ERROR_CODE SsbSipMfcDecInit (void *openHandle, SSBSIP_MFC_CODEC_TYPE codec_type, int Frameleng)
{
	MFC_ARGS			DecArg;
	_MFCLIB 			*pCTX;
	int frameBufSize;
	CMM_ALLOC_PRAM_T	CMMParam;
	int 				phyFrmBuf;
	int					virFrmBuf;
	BOOL				r;
	int					packedPB = MFC_UNPACKED_PB;

	if (openHandle == NULL)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcDecSetConfig", "openHandle is NULL\n");
		return MFC_RET_DEC_INIT_FAIL;
	}

	pCTX  = (_MFCLIB *) openHandle;
	memset(&DecArg, 0x00, sizeof(DecArg));

	if ((codec_type < H264_DEC) || (codec_type > H263_ENC))
	{

		LOG_MSG(LOG_ERROR, "SsbSipMfcDecOpen", "Undefined codec type.\n");
		return MFC_RET_DEC_INIT_FAIL;
	}
	pCTX->codec_type = codec_type;

/*
    if((pCTX->codec_type == MPEG4_DEC) || (pCTX->codec_type == XVID_DEC)
        ||(pCTX->codec_type == DIVX311_DEC) || (pCTX->codec_type == DIVX412_DEC)
        ||(pCTX->codec_type == DIVX502_DEC)|| (pCTX->codec_type == DIVX503_DEC))
        packedPB = isPBPacked(pCTX);
*/
	DecArg.args.dec_init.in_codecType = pCTX->codec_type;
	DecArg.args.dec_init.in_strmSize = Frameleng;
	DecArg.args.dec_init.in_strmBuf = pCTX->phyStrmBuf;
	DecArg.args.dec_init.in_packedPB = packedPB;

	r = DeviceIoControl(pCTX->hMFC, IOCTL_MFC_DEC_INIT,
							&DecArg, sizeof(MFC_ARGS),
							NULL, 0,
							NULL,
							NULL);

	if (DecArg.ret_code != MFC_RET_OK)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcDecInit", "IOCTL_MFC_DEC_INIT failed(ret : %d)\n", DecArg.ret_code);
		return DecArg.ret_code;
	}

	if (DecArg.args.dec_init.out_dpb_y_size <= 0 || DecArg.args.dec_init.out_dpb_cb_size <= 0)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcDecInit", "MFC out_dpb_cnt error \n");
		return MFC_RET_DEC_INIT_FAIL;
	}

	pCTX->decOutInfo.img_width = DecArg.args.dec_init.out_img_width;
	pCTX->decOutInfo.img_height = DecArg.args.dec_init.out_img_height;
	pCTX->decOutInfo.buf_width = DecArg.args.dec_init.out_buf_width;
	pCTX->decOutInfo.buf_height = DecArg.args.dec_init.out_buf_height;

	// allocation of Y Frame buffer
	CMMParam.size = (unsigned int)DecArg.args.dec_init.out_dpb_y_size;
	CMMParam.cacheFlag = 0;
	CMMParam.dramLocation = DRAM_1;

	LOG_MSG(LOG_WARNING, "", "out_buf_width : %d out_buf_height : %d out_dpb_y_size : %d\n", DecArg.args.dec_init.out_buf_width, DecArg.args.dec_init.out_buf_height, DecArg.args.dec_init.out_dpb_y_size);

	r = DeviceIoControl(pCTX->hCMM, IOCTL_CODEC_MEM_ALLOC, (PBYTE)&CMMParam, 
							sizeof(CMM_ALLOC_PRAM_T *), &virFrmBuf, sizeof(virFrmBuf), 
							NULL, NULL);
	if(r == FALSE)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcDecInit", "IOCTL_CODEC_MEM_ALLOC failed\n");
		return MFC_RET_DEC_INIT_FAIL;
	}

	r = DeviceIoControl(pCTX->hCMM, IOCTL_CODEC_GET_PHY_ADDR, (PBYTE)virFrmBuf, 
							sizeof(virFrmBuf), &phyFrmBuf, sizeof(phyFrmBuf), 
							NULL, NULL);
	if(r == FALSE)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcDecInit", "IOCTL_CODEC_GET_PHY_ADDR failed\n");
		return MFC_RET_DEC_INIT_FAIL;
	}

	pCTX->phyYFrmBuf = phyFrmBuf;
	pCTX->virYFrmBuf = virFrmBuf;
	pCTX->sizeYFrmBuf = DecArg.args.dec_init.out_dpb_y_size;
	memset((void *)virFrmBuf, 0xFF, pCTX->sizeYFrmBuf);
	
	// allocation of CbCr Frame buffer
	CMMParam.size = (unsigned int)DecArg.args.dec_init.out_dpb_cb_size;
	CMMParam.cacheFlag = 0;
	CMMParam.dramLocation = DRAM_0;

	LOG_MSG(LOG_WARNING, "", "out_buf_width : %d out_buf_height : %d out_dpb_cb_size : %d\n", DecArg.args.dec_init.out_buf_width, DecArg.args.dec_init.out_buf_height, DecArg.args.dec_init.out_dpb_cb_size);

	r = DeviceIoControl(pCTX->hCMM, IOCTL_CODEC_MEM_ALLOC, (PBYTE)&CMMParam, 
							sizeof(CMM_ALLOC_PRAM_T *), &virFrmBuf, sizeof(virFrmBuf), 
							NULL, NULL);
	if(r == FALSE)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcDecInit", "IOCTL_CODEC_MEM_ALLOC failed\n");
		return MFC_RET_DEC_INIT_FAIL;
	}

	r = DeviceIoControl(pCTX->hCMM, IOCTL_CODEC_GET_PHY_ADDR, (PBYTE)virFrmBuf, 
							sizeof(virFrmBuf), &phyFrmBuf, sizeof(phyFrmBuf), 
							NULL, NULL);
	if(r == FALSE)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcDecInit", "IOCTL_CODEC_GET_PHY_ADDR failed\n");
		return MFC_RET_DEC_INIT_FAIL;
	}

    pCTX->phyCbFrmBuf = phyFrmBuf;
    pCTX->virCbFrmBuf = virFrmBuf;
    pCTX->sizeCbFrmBuf = DecArg.args.dec_init.out_dpb_cb_size;
    memset((void *)virFrmBuf, 0xFF, pCTX->sizeCbFrmBuf);

    DecArg.args.dec_buff_init.in_codecType = pCTX->codec_type;
    DecArg.args.dec_buff_init.in_strmBuf = pCTX->phyStrmBuf;
    DecArg.args.dec_buff_init.in_strmSize = Frameleng;
    DecArg.args.dec_buff_init.in_frmYBuf = pCTX->phyYFrmBuf;
    DecArg.args.dec_buff_init.in_frmYSize = pCTX->sizeYFrmBuf;
    DecArg.args.dec_buff_init.in_frmCbBuf = pCTX->phyCbFrmBuf;
    DecArg.args.dec_buff_init.in_frmCbSize = pCTX->sizeCbFrmBuf;

    LOG_MSG(LOG_WARNING, "SsbSipMfcDecInit", "IOCTL_MFC_DEC_BUFFER_INIT\n");
 
    r = DeviceIoControl(pCTX->hMFC, IOCTL_MFC_DEC_BUFFER_INIT,
                        &DecArg, sizeof(MFC_ARGS),
                        NULL, 0,
                        NULL,
                        NULL);

    if (DecArg.ret_code != MFC_RET_OK)
    {
        LOG_MSG(LOG_ERROR, "SsbSipMfcDecInit", "IOCTL_MFC_DEC_BUFFER_INIT failed(ret : %d)\n", DecArg.ret_code);
        return DecArg.ret_code;
    }
    
	return MFC_RET_OK;
}

SSBSIP_MFC_ERROR_CODE SsbSipMfcDecExe(void *openHandle, int lengthBufFill)
{
	_MFCLIB 			*pCTX;
	MFC_ARGS			DecArg;
	int					ret;
	int					Yoffset;
	int					Coffset;

	if (openHandle == NULL)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcDecExe", "openHandle is NULL\n");
		return MFC_RET_DEC_EXE_ERR;
	}

	if ((lengthBufFill < 0) || (lengthBufFill > MAX_DECODER_INPUT_BUFFER_SIZE))
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcDecExe", "lengthBufFill is invalid. (lengthBufFill=%d)\n", lengthBufFill);
		return MFC_RET_DEC_EXE_ERR;
	}
	
	pCTX  = (_MFCLIB *) openHandle;
	memset(&DecArg, 0x00, sizeof(DecArg));
	
	DecArg.args.dec_exe.in_codecType = pCTX->codec_type;
    DecArg.args.dec_exe.in_strmBuf = pCTX->phyStrmBuf;
    DecArg.args.dec_exe.in_strmSize = lengthBufFill;
	DecArg.args.dec_exe.in_frmYBuf = pCTX->phyYFrmBuf;
	DecArg.args.dec_exe.in_frmYSize = pCTX->sizeYFrmBuf;
	DecArg.args.dec_exe.in_frmCbBuf = pCTX->phyCbFrmBuf;
	DecArg.args.dec_exe.in_frmCbSize = pCTX->sizeCbFrmBuf;


	ret = DeviceIoControl(pCTX->hMFC, IOCTL_MFC_DEC_EXE,
						&DecArg, sizeof(MFC_DEC_EXE_ARG),
						NULL, 0,
						NULL,
						NULL);

	if (DecArg.ret_code != MFC_RET_OK)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcDecExe", "IOCTL_MFC_DEC_EXE failed(ret : %d)\n", DecArg.ret_code);
		return DecArg.ret_code;
	}

	Yoffset = DecArg.args.dec_exe.out_displayYAddr - DecArg.args.dec_exe.in_frmYBuf;
	Coffset = DecArg.args.dec_exe.out_displayCAddr - DecArg.args.dec_exe.in_frmCbBuf;

    pCTX->decOutInfo.YPhyAddr = (void*)(DecArg.args.dec_exe.out_displayYAddr);
    pCTX->decOutInfo.CPhyAddr = (void*)(DecArg.args.dec_exe.out_displayCAddr);
    pCTX->decOutInfo.YVirAddr = (void*)(pCTX->virYFrmBuf + Yoffset);
    pCTX->decOutInfo.CVirAddr = (void*)(pCTX->virCbFrmBuf + Coffset);
    pCTX->decOutInfo.consumedByte = DecArg.args.dec_exe.out_consumedByte;
    pCTX->decOutInfo.res_change= DecArg.args.dec_exe.out_res_change;
    pCTX->displayStatus = DecArg.args.dec_exe.out_displayStatus;

	return DecArg.ret_code;
}


SSBSIP_MFC_ERROR_CODE SsbSipMfcDecClose(void *openHandle)
{
	_MFCLIB  *pCTX;

	if (openHandle == NULL)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcDecClose", "openHandle is NULL\n");
		return MFC_RET_CLOSE_FAIL;
	}

	pCTX  = (_MFCLIB *) openHandle;

	printf("== Close\npCTX->hMFC : 0x%x\n", pCTX->hMFC);
	printf("pCTX->hCMM : 0x%x\n", pCTX->hCMM);
	CloseHandle(pCTX->hMFC);
	CloseHandle(pCTX->hCMM);
	free(pCTX);

	return MFC_RET_OK;
}

void  *SsbSipMfcDecGetInBuf (void *openHandle, void **phyInBuf, int inputBufferSize)
{
	_MFCLIB 			*pCTX;
	CMM_ALLOC_PRAM_T	CMMParam;
	BOOL				r;
	int					frameBufSize;
	int					phyStrmBuf, virStrmBuf;

	if (inputBufferSize < 0)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcDecGetInBuf", "inputBufferSize is invalid. (inputBufferSize=%d)\n", inputBufferSize);
		return NULL;
	}
		
	if (openHandle == NULL)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcDecSetConfig", "openHandle is NULL\n");
		return NULL;
	}

	pCTX  = (_MFCLIB *) openHandle;

	// CMM alloc
	CMMParam.size = (unsigned int)inputBufferSize;
	CMMParam.cacheFlag = 0;
	CMMParam.dramLocation = DRAM_0;
	
	r = DeviceIoControl(pCTX->hCMM, IOCTL_CODEC_MEM_ALLOC, (PBYTE)&CMMParam, 
							sizeof(CMM_ALLOC_PRAM_T *), &virStrmBuf, sizeof(virStrmBuf), 
							NULL, NULL);
	if(r == FALSE)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcDecInit", "IOCTL_CODEC_MEM_ALLOC failed\n");
		return NULL;
	}
	
	r = DeviceIoControl(pCTX->hCMM, IOCTL_CODEC_GET_PHY_ADDR, (PBYTE)virStrmBuf, 
							sizeof(virStrmBuf), &phyStrmBuf, sizeof(phyStrmBuf), 
							NULL, NULL);
	if(r == FALSE)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcDecGetInBuf", "IOCTL_CODEC_GET_PHY_ADDR failed\n");
		return NULL;
	}
	pCTX->phyStrmBuf = phyStrmBuf;
	pCTX->virStrmBuf = virStrmBuf;
	pCTX->sizeStrmBuf = inputBufferSize;

	*phyInBuf = (void *)phyStrmBuf;

	return (void*)virStrmBuf;
}

SSBSIP_MFC_ERROR_CODE SsbSipMfcDecSetInBuf (void *openHandle, void *phyInBuf, void *virInBuf, int inputBufferSize)
{
	_MFCLIB 			*pCTX;

	if (openHandle == NULL)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcDecSetConfig", "openHandle is NULL\n");
		return MFC_RET_DEC_SET_INBUF_FAIL;
	}

	pCTX  = (_MFCLIB *) openHandle;

	pCTX->phyStrmBuf = (int)phyInBuf;
	pCTX->virStrmBuf = (int)virInBuf;
	pCTX->sizeStrmBuf = inputBufferSize;
	return MFC_RET_OK;
}

SSBSIP_MFC_DEC_OUTBUF_STATUS SsbSipMfcDecGetOutBuf(void *openHandle, SSBSIP_MFC_DEC_OUTPUT_INFO *output_info)
{
	_MFCLIB 						*pCTX;
	
	if (openHandle == NULL)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcDecGetOutBuf", "openHandle is NULL\n");
		return MFC_GETOUTBUF_DISPLAY_END;
	}

	pCTX  = (_MFCLIB *) openHandle;

    output_info->YPhyAddr = pCTX->decOutInfo.YPhyAddr;
    output_info->CPhyAddr = pCTX->decOutInfo.CPhyAddr;

    output_info->YVirAddr = pCTX->decOutInfo.YVirAddr;
    output_info->CVirAddr = pCTX->decOutInfo.CVirAddr;

    output_info->img_width= pCTX->decOutInfo.img_width;
    output_info->img_height= pCTX->decOutInfo.img_height;

    output_info->buf_width= pCTX->decOutInfo.buf_width;
    output_info->buf_height= pCTX->decOutInfo.buf_height;

    output_info->consumedByte = pCTX->decOutInfo.consumedByte;
    output_info->res_change = pCTX->decOutInfo.res_change;

	if(pCTX->displayStatus == DISPLAY_END)
		return MFC_GETOUTBUF_DISPLAY_END;

	if((pCTX->displayStatus == DECODING_DISPLAY) && (output_info->YPhyAddr != 0))
		return MFC_GETOUTBUF_DISPLAY_DECODING;

	if((pCTX->displayStatus == DISPLAY_ONLY) && (output_info->YPhyAddr != 0))
		return MFC_GETOUTBUF_DISPLAY_ONLY;

	return MFC_GETOUTBUF_DECODING_ONLY;
}


SSBSIP_MFC_ERROR_CODE SsbSipMfcDecSetConfig(void *openHandle, SSBSIP_MFC_DEC_CONF conf_type, void *value)
{
	_MFCLIB  			*pCTX;
	MFC_ARGS			DecArg;
	BOOL				r;

	if (openHandle == NULL)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcDecSetConfig", "openHandle is NULL\n");
		return MFC_RET_DEC_SET_CONF_FAIL;
	}
	if (value == NULL)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcDecSetConfig", "value is NULL\n");
		return MFC_RET_DEC_SET_CONF_FAIL;
	}

	pCTX  = (_MFCLIB *) openHandle;
	memset(&DecArg, 0x00, sizeof(DecArg));

	DecArg.args.set_config.in_config_param = conf_type;
	DecArg.args.set_config.in_config_value[0]  = *((unsigned int *) value);
	DecArg.args.set_config.in_config_value[1]  = 0;

	r = DeviceIoControl(pCTX->hMFC, IOCTL_MFC_SET_CONFIG,
							&DecArg, sizeof(MFC_ARGS),
							NULL, 0,
							NULL,
							NULL);

	if (DecArg.ret_code != MFC_RET_OK)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcDecSetConfig", "IOCTL_MFC_SET_CONFIG failed(ret : %d)\n", DecArg.ret_code);
		return DecArg.ret_code;
	}
	return DecArg.ret_code;
}

SSBSIP_MFC_ERROR_CODE SsbSipMfcDecGetConfig(void *openHandle, SSBSIP_MFC_DEC_CONF conf_type, void *value)
{
    _MFCLIB             *pCTX;
    MFC_ARGS            DecArg;
    BOOL                r;

    if (openHandle == NULL)
    {
        LOG_MSG(LOG_ERROR, "SsbSipMfcDecGetConfig", "openHandle is NULL\n");
        return MFC_RET_DEC_GET_CONF_FAIL;
    }
    if (value == NULL)
    {
        LOG_MSG(LOG_ERROR, "SsbSipMfcDecGetConfig", "value is NULL\n");
        return MFC_RET_DEC_GET_CONF_FAIL;
    }
    pCTX = (_MFCLIB *)openHandle;
    memset(&DecArg, 0x00, sizeof(DecArg));

    DecArg.args.get_config.in_config_param = (int)conf_type;
    r = DeviceIoControl(pCTX->hMFC, IOCTL_MFC_GET_CONFIG,
                        &DecArg, sizeof(MFC_ARGS),
                        NULL, 0,
                        NULL,
                        NULL);

    if (DecArg.ret_code != MFC_RET_OK)
    {
        LOG_MSG(LOG_ERROR, "SsbSipMfcDecSetConfig", "IOCTL_MFC_SET_CONFIG failed(ret : %d)\n", DecArg.ret_code);
        return DecArg.ret_code;
    }

    switch(conf_type){
        case MFC_DEC_GETCONF_CRC_DATA : {
                                            ((int *) value)[0] = DecArg.args.get_config.out_config_value[0];
                                            ((int *) value)[1] = DecArg.args.get_config.out_config_value[1];
                                            ((int *) value)[2] = DecArg.args.get_config.out_config_value[2];
                                            ((int *) value)[3] = DecArg.args.get_config.out_config_value[3];
                                            ((int *) value)[4] = DecArg.args.get_config.out_config_value[4];
                                            ((int *) value)[5] = DecArg.args.get_config.out_config_value[5];
                                            break;
                                         }
        default : break;
    }

    return DecArg.ret_code;
}

