/* SPDX-License-Identifier: LGPL-2.1-only OR BSD-3-Clause */
/******************************************************************************
 *
 * This file is provided under a dual license.  When you use or
 * distribute this software, you may choose to be licensed under
 * version 2.1 of the GNU Lesser General Public License
 * ("LGPLv2.1 License") or BSD License.
 *
 * LGPLv2.1 License
 *
 * Copyright(C) 2019 MediaTek Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of version 2.1 of the GNU Lesser General Public
 * License as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html for more
 * details.
 *
 * BSD LICENSE
 *
 * Copyright(C) 2019 MediaTek Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *  * Neither the name of the copyright holder nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *****************************************************************************/


#if defined(LINUX_TURNKEY_SOLUTION) && defined(CC_ENABLE_VOMX)

#include "vomx_if.h"

#include "x_typedef.h"
#include "x_os.h"
#include "x_printf.h"
#include "x_assert.h"
#include "vdec_mod.h"
#include "vdec_omx_debug.h"


//#include "drv_thread.h"
static HANDLE_T          _hMainTask = NULL_HANDLE;
static HANDLE_T          _hCommandQ = NULL_HANDLE;
static HANDLE_T    _hTaskExitMutex = NULL_HANDLE;
static BOOL _fgVOMXTaskExist = FALSE;

static void vVDOOmxThread(void* pvArg)
{
    INT32 i4Ret = 0;
    UINT16 u2MsgIdx;
    SIZE_T zMsgSize;
    VDO_OMX_MSG_T rMsg;

    VOMX_Printf("[VDO][OMX] vVDOOmxThread\n");

    while (TRUE)
    {
        // wait FillThisBuffer/EmptyThisBuffer/SendCommand event
   //     if (pHandle->fgEmptying || pHandle->fgFilling)
   //     {
  //          i4Ret = x_msg_q_receive_timeout(&u2MsgIdx, (void *)&rMsg, &zMsgSize, &(pHandle->hCommandQ), 1, 5);
  //          VOMX_Printf("[VDO][OMX] x_msg_q_receive_timeout: i4Ret = 0x%X.\n", i4Ret);
 //       }
 //       else
        {
            zMsgSize = sizeof(VDO_OMX_MSG_T);
            i4Ret = x_msg_q_receive(&u2MsgIdx, (void *)&rMsg, &zMsgSize, &_hCommandQ, 1, X_MSGQ_OPTION_WAIT);
            if(i4Ret != OSR_OK)
            {
				LOG(3,"[VDO][OMX] x_msg_q_receive: i4Ret = 0x%X.\n", i4Ret);
            }
        }

        if (OSR_OK == i4Ret)
        {
            if (VDO_OMX_CMD_TERMINATE == rMsg.eCmd)
            {
                // exiting
                LOG(5, "[VDO][OMX]receive CMD_TERMINATE\n");
                break;
            }

            switch (rMsg.eCmd)
            {
            case VDO_OMX_CMD_SET_STATE:
                i4VDOOmxCompSetState(rMsg.pHandle, rMsg.eStateTo);
                break;
            case VDO_OMX_CMD_EMPTY_BUFFER:
//                i4VDOOmxQueueEmptyBuf(pHandle, rMsg.hHeader);
                break;
            case VDO_OMX_CMD_FILL_BUFFER:
//                i4VDOOmxQueueFillBuf(pHandle, rMsg.hHeader);
                break;
            case VDO_OMX_CMD_FLUSH:
   //             i4VDOOmxCompFlush(pHandle, rMsg.fgInput, rMsg.fgOutput);
                break;
            case VDO_OMX_CMD_BUFFER_ALLOCATED:
            case VDO_OMX_CMD_BUFFER_FREED:
                break;
            case VDO_OMX_CMD_PORT_ENABLE:
                i4VDOOmxCompPortEnable(rMsg.pHandle, rMsg.fgInput, rMsg.fgOutput);
                break;
            case VDO_OMX_CMD_PORT_DISABLE:
                i4VDOOmxCompPortDisable(rMsg.pHandle, rMsg.fgInput, rMsg.fgOutput);
                break;
            default:
                break;
            }
        }

        // check if state transition done
        if (rMsg.pHandle && rMsg.pHandle->fgStateTransing)
        {
            i4VDOOmxCompCheckStateTransition(rMsg.pHandle);
        }

        // do empty buffer
//	        if (pHandle->fgEmptying)
//	        {
//	//            i4VDOOmxDoEmptyBuf(pHandle);
//	        }
//	
//	        // do fill buffer
//	        if (pHandle->fgFilling)
//	        {
//	 //           i4VDOOmxDoFillBuf(pHandle);
//	        }
    }

    // delete message queue
    x_msg_q_delete(_hCommandQ);
    _hCommandQ = NULL_HANDLE;

	// unlock mainTask thread exit semaphore
	VERIFY(x_sema_unlock(_hTaskExitMutex) == OSR_OK);

    x_thread_exit();
}


INT32 i4VDOOmxTaskCreate(void)
{
    char acBuf[32];
    static UINT16 u2TaskId = 0;
    if(_fgVOMXTaskExist)
        return VDecOMX_OK;
    // Create mainTask thread exit semaphore
	VERIFY(x_sema_create(&_hTaskExitMutex, X_SEMA_TYPE_BINARY, X_SEMA_STATE_LOCK) == OSR_OK);

    // create command queue
    x_memset(acBuf, 0, 32 * sizeof(char));
    x_snprintf(acBuf, sizeof(acBuf), "VOMXCQ%d", u2TaskId);

	x_msg_q_create(&_hCommandQ, acBuf, sizeof(VDO_OMX_MSG_T), 128);
    
    VOMX_Printf("[VDO][OMX] i4VDOOmxTaskCreate\n");
    // create video OpenMAX main thread
    x_memset(acBuf, 0, 32 * sizeof(char));
    x_snprintf(acBuf, sizeof(acBuf), "VOMXTskThread%d", u2TaskId++);
    x_thread_create(&_hMainTask, acBuf, VDOOMX_STACK_SIZE, VDOOMX_THREAD_PRIORITY, vVDOOmxThread, sizeof(void*), NULL);
    _fgVOMXTaskExist = TRUE;

    return VDecOMX_OK;
}


INT32 i4VDOOmxTaskDestroy(void)
{
    VDO_OMX_MSG_T rMsgTerminate;
    if(!_fgVOMXTaskExist)
        return VDecOMX_OK;
    rMsgTerminate.pHandle = NULL;
    rMsgTerminate.eCmd = VDO_OMX_CMD_TERMINATE;
    i4VDOOmxTaskSendCommand(&rMsgTerminate);

	// wait for mainTask thread exit
	VERIFY(x_sema_lock_timeout(_hTaskExitMutex, 500) == OSR_OK);//timeout value change to 2000, temp solution
	
    // delete mainTask thread exit semaphore
	VERIFY(x_sema_delete(_hTaskExitMutex) == OSR_OK);
    _fgVOMXTaskExist = FALSE;

    return VDecOMX_OK;
}

INT32 i4VDOOmxTaskSendCommand(VDO_OMX_MSG_T *prMsg)
{
    INT32 i4Ret = 0;

    if (NULL_HANDLE != _hCommandQ)
    {
    	int u4Count = 1000;
		while (u4Count--)
		{
        	i4Ret = x_msg_q_send(_hCommandQ, prMsg, sizeof(VDO_OMX_MSG_T), 0);
			if (OSR_OK == i4Ret)
			{
                if(prMsg->eCmd == VDO_OMX_CMD_TERMINATE)
                {
                    LOG(5, "[VDO][OMX]send CMD_TERMINATE\n");
                }
				break;
			}
			if((u4Count % 100) == 0)
			{
                LOG(0, "[VDO][OMX]OSR_ERR(0x%x)(%d)\n", i4Ret, u4Count);
			}
			x_thread_delay(10);
		}
    }

    return i4Ret;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////
//	static HANDLE_T _hVDOOmxSem = NULL_HANDLE;
//	
//	INT32 i4VDOOmxTaskSemInit(void)
//	{
//	    INT32 i4Ret = 0;
//	
//	    // create semaphore, only one semaphore for this module
//	    if (NULL_HANDLE == _hVDOOmxSem)
//	    {
//	        i4Ret = x_sema_create(&_hVDOOmxSem, X_SEMA_TYPE_BINARY, X_SEMA_STATE_UNLOCK);
//	    }
//	
//	    return i4Ret;
//	}
//	
//	
//	INT32 i4VDOOmxTaskSemUninit(void)
//	{
//	    INT32 i4Ret = 0;
//	
//	    // delete semaphore
//	    if (NULL_HANDLE != _hVDOOmxSem)
//	    {
//	        i4Ret = x_sema_delete(_hVDOOmxSem);
//	        _hVDOOmxSem = NULL_HANDLE;
//	    }
//	
//	    return i4Ret;
//	}


//	INT32 i4VDOOmxTaskLockSem(void)
//	{
//	    INT32 i4Ret = 0;
//	
//	    if (NULL_HANDLE != _hVDOOmxSem)
//	    {
//	        i4Ret = x_sema_lock(_hVDOOmxSem, X_SEMA_OPTION_WAIT);
//	    }
//	
//	    return i4Ret;
//	}
//	
//	
//	INT32 i4VDOOmxTaskUnlockSem(void)
//	{
//	    INT32 i4Ret = 0;
//	
//	    if (NULL_HANDLE != _hVDOOmxSem)
//	    {
//	        i4Ret = x_sema_unlock(_hVDOOmxSem);
//	    }
//	
//	    return i4Ret;
//	}

#endif

