/**
 * @file hv_mw_TypecTask.c
 * @brief typec task source file
 * @details This file provides the following functions: \n
 *          (1) event dispatch \n
 *          (2) task handle \n
 *          (3) Feed functions \n
 *
 * @author HiView SoC Software Team
 * @version 1.0.0
 * @date 2022-08-25
 * @copyright Copyright(c),2022-8, Hiview Software. All rights reserved.
 * @par History:
 * <table>
 * <tr><th>Author                         <th>Date         <th>Change Description
 * <tr><td>Hiview SoC Software Team       <td>2022-08-25   <td>init
 * </table>
*/

#include "hv_vos_Comm.h"
#include "hv_comm_Define.h"
#include "hv_comm_Event.h"
#include "hv_comm_Assert.h"
#include "hv_comm_Timer.h"

#include "hv_mw_SystemManager.h"
#include "hv_mw_DisplayPort.h"
#include "hv_mw_TypecTask.h"
#include "hv_mw_DisplayPort.h"
#include "hv_drv_TypecStdInc.h"
#include "hv_drv_TypecPortable.h"
#include "hv_drv_Gpio.h"
#include "hv_drv_DisplayPort.h"
#include "FreeRTOS.h"
#include "task.h"
#include "hv_mw_UrgentTask.h"
#include "hv_mw_PowerManager.h"
#include "hv_drv_Flash.h"
#ifdef CONFIG_USER_USB_BILLBOARD_ON
#include "hv_mw_UsbTask.h"
#endif

#define TYPEC_PD_DONE_TIMEOUT_MS    1000

static TypecState s_enCurrentTypecState = TYPEC_STATE_IDLE;
static TypecPlatformCB s_stTypecPlatformCB = {0};
static HV_TIMER_S *pstVdmConfigTimer;
static HV_TIMER_S* pstNotifyPWRForDetachTimer = NULL;
static HV_TIMER_S* pstTypecTaskStartTimer = NULL;

static Status Hv_Mw_Typec_EventHandle(TypecEvent *pstEvent);

static const TypecStateMachine s_astTypecStateMachine[] =
{
    {TYPEC_STATE_IDLE,      TYPEC_EVENT_GO,        Hv_Mw_Typec_EventHandle,      TYPEC_STATE_RUNNING},
    {TYPEC_STATE_IDLE,      TYPEC_EVENT_REPLUG,    Hv_Mw_Typec_EventHandle,      TYPEC_STATE_RUNNING},
    {TYPEC_STATE_RUNNING,   TYPEC_EVENT_GO,        Hv_Mw_Typec_EventHandle,      TYPEC_STATE_RUNNING},
    {TYPEC_STATE_RUNNING,   TYPEC_EVENT_STOP,      Hv_Mw_Typec_EventHandle,      TYPEC_STATE_IDLE},
    {TYPEC_STATE_RUNNING,   TYPEC_EVENT_REPLUG,    Hv_Mw_Typec_EventHandle,      TYPEC_STATE_RUNNING},
};

#if (HV_TYPEC_CONFIG_TYPEC_MODE == TYPEC_MODE_SINK)
extern VOID Hv_Mw_EventHub_TaskStartInit(VOID);
#endif 
extern Status Hv_Mw_DpRx_Reset(LinkPortIndex enPortIndex);

static void Hv_Mw_Typec_PdDoneTimerCB (UCHAR8 ucTypecPortNum, UCHAR8 ucPESubState)
{
    HV_LOG_INFO(TYPEC, "Typec set idle task with PdDoneTimerCB !!!");
}

static Status Hv_Mw_Typec_SendEventToSystemQueue(SystemRevEventType enEventType, USHORT16 usEventId, UINT32 uiValue)
{
    SystemEvent stSystemEvent;
    HV_VOS_QUEUE_S *pSystemQueue = Hv_Mw_System_GetSystemQueue();
    
    HV_ASSERT_VALID_PTR(pSystemQueue);

    HV_MEMSET(&stSystemEvent, 0 ,sizeof(stSystemEvent));
    stSystemEvent.enEventType = enEventType;
    stSystemEvent.usEventId = usEventId;
    stSystemEvent.uiValue = uiValue;
    stSystemEvent.ucPortIndex = (uiValue == 0) ? HV_BOARD_CONFIG_TYPEC0_TO_DP : HV_BOARD_CONFIG_TYPEC1_TO_DP;
    HV_ASSERT_TRUE(Hv_Vos_QueueSend(pSystemQueue, &stSystemEvent));

    return HV_SUCCESS;
}

static Status Hv_Mw_Typec_EventHandle(TypecEvent *pstEvent)
{
    HV_ASSERT_VALID_PTR(pstEvent);
    Status uiRet = HV_SUCCESS;
    USHORT16 usEventId = pstEvent->usEventId;

    switch (usEventId)
    {
        case TYPEC_EVENT_GO:
        {
            break;
        }
        case TYPEC_EVENT_STOP:
        {
            break;
        }
        case TYPEC_EVENT_REPLUG:
        {
            break;
        }
        default:
        {
            break;
        }
    }

    return uiRet;
}

static VOID Hv_Mw_Typec_RunStateMachine(TypecEvent *pstEvent)
{
    HV_ASSERT_VALID_PTR_VOID(pstEvent);
    
    /* call Typec state machine handle */
    HV_LOG_DEBUG(TYPEC, "Type-c state = %d, EventId = %d", s_enCurrentTypecState, pstEvent->usEventId);

    for (USHORT16 usIndex = 0; usIndex < ARRAY_SIZE(s_astTypecStateMachine); ++usIndex)
    {
        if ((s_enCurrentTypecState == s_astTypecStateMachine[usIndex].usAcceptStates)
           && (pstEvent->usEventId == s_astTypecStateMachine[usIndex].enEventId))
        {
            Status uiRet = s_astTypecStateMachine[usIndex].pfActionHandle(pstEvent);
            
            if (HV_SUCCESS == uiRet)
            {
                s_enCurrentTypecState = s_astTypecStateMachine[usIndex].enNextState;
            }
            else
            {
                s_enCurrentTypecState = TYPEC_STATE_IDLE;
            }
            
            //HV_LOG_INFO(TYPEC, "Type-c state = %d, EventId = %d", s_enCurrentTypecState, pstEvent->usEventId);
            
            Hv_PSF_RUN();
            
            break;
        }
    }

    return;
}

VOID Hv_Mw_Typec_DetachHandler(UCHAR8 ucTypecPortNum)
{
    HV_LOG_INFO(TYPEC, "Hv_Mw_Typec_DetachHandler!!!");
#if (HV_TYPEC_GOODCRC_SW_EN == 1)
    /* 进入BIST Test Data模式后把硬件回复GoodCRC和接收Rx Msg全部关闭， 测试完毕后再打开 */
    if ( ((UPD_RegReadByte(ucTypecPortNum, UPD_REG_RW_TX_MSG_CONTROL) & (BIT(0) | BIT(2))) == 0) &&
        ((UPD_RegReadByte(ucTypecPortNum, UPD_REG_RW_RX_IRQMASK) & UPD_RX_MSG_RECEIVE_MASKBIT) == 0) )
    {
        /* SW GoodCRC*/
        UPD_RegByteClearBit(ucTypecPortNum, UPD_REG_RW_TX_MSG_CONTROL, (BIT(0) | BIT(2)));
        UPD_RegByteSetBit(ucTypecPortNum, UPD_REG_RW_RX_IRQMASK, UPD_RX_MSG_RECEIVE_MASKBIT);
    }
#endif

#if (HV_PROJECT_CONFIG_TYPEC_TASK_DYN == HV_CONFIG_ON)
    vTaskPrioritySet(NULL, TYPEC_NORMAL_TASK_PRIORITY);
#endif

    const PowerManager *pstPowerManager = Hv_Mw_Power_GetManagerPtr();
    PowerManager stPowerManager;
    HV_MEMCPY(&stPowerManager, pstPowerManager, sizeof(PowerManager));
    stPowerManager.bPowerDeliveryFlag = HV_FALSE;
    Hv_Mw_Power_UpdatePowerManagerInfo(stPowerManager);

    /**
     * This function handles the power management for specific laptop models, such as the Victus by HP Gaming Laptop 15-fa1143TX with RTX4060 Laptop GPU.
     * During a pseudo-standby state, these laptops may temporarily detach and then reattach power delivery (PD) interactions upon startup.
     * It is important not to process the first detach event, as doing so would transition the laptop into a true standby state.
     * Entering a true standby state during startup can prevent the motherboard from waking up properly, causing issues that the board cannot be waken up.
     */
    if(pstPowerManager->enCurrentPowerState == POWER_STATE_STANDBY)
    {
        /* 在fake standby的状态下， 如果遇到detach后立刻attach的case(比如连接macbook的时候， macbook关机)， 为了避免
        detach收到后进入standby， 增加下面的处理， 延迟500ms进入standby */
        Hv_Vos_Timer_Start(pstNotifyPWRForDetachTimer);
    }
    else
    {
    #if (HV_TYPEC_CONFIG_SSPWD_EN == TYPEC_FUNCTION_ON)
        /* solution for Samsung mobile phone's waterdrop issue */
        Hv_Drv_Typec_DpAuxPowerOff();
    #endif
        // Hv_Mw_Typec_SendEventToSystemQueue(SYSTEM_REV_EVENT_TYPE_POWER, POWER_EVENT_ID_TYPE_C_PLUG_OUT, ucTypecPortNum);
        /* nodify DP that type-c cable detach */
        Hv_Mw_Typec_SendEventToSystemQueue(SYSTEM_REV_EVENT_TYPE_INPUTPORT, InputPortEventId_DP_CableDisconnect, ucTypecPortNum);
    }

#ifdef CONFIG_USER_USB_BILLBOARD_ON
    if(USB_MODE_DEVICE == Hv_Mw_UsbGetWorkMode())
    {
        Hv_Mw_UsbBillboard_TaskStop();
    }
#endif
}

VOID Hv_Mw_Typec_AttachHandler(UCHAR8 ucTypecPortNum)
{
#if (HV_TYPEC_GOODCRC_SW_EN == 1)
    /* SW GoodCRC*/
    UPD_RegByteClearBit(ucTypecPortNum, UPD_REG_RW_TX_MSG_CONTROL, (BIT(0) | BIT(2)));
    UPD_RegByteSetBit(ucTypecPortNum, UPD_REG_RW_RX_IRQMASK, UPD_RX_MSG_RECEIVE_MASKBIT);
#endif
    const PowerManager *pstPowerManager = Hv_Mw_Power_GetManagerPtr();
    PowerManager stPowerManager;
    HV_MEMCPY(&stPowerManager, pstPowerManager, sizeof(PowerManager));
    stPowerManager.bPowerDeliveryFlag = HV_TRUE;
    Hv_Mw_Power_UpdatePowerManagerInfo(stPowerManager);

    Hv_Vos_Timer_Rstop(pstNotifyPWRForDetachTimer);

    HV_PSF_HOOK_SEND_EVENT_TO_QUEUE(TYPEC_EVENT_GO, 0);
#if (HV_PROJECT_CONFIG_TYPEC_TASK_DYN == HV_CONFIG_ON)
    vTaskPrioritySet(NULL, TYPEC_URGENT_TASK_PRIORITY);
#else
    vTaskPrioritySet(NULL, TYPEC_NORMAL_TASK_PRIORITY);
#endif

    // Hv_Mw_Typec_SendEventToSystemQueue(SYSTEM_REV_EVENT_TYPE_POWER, POWER_EVENT_ID_TYPE_C_DETAIL_PD_DONE, ucTypecPortNum);
    HV_LOG_INFO(TYPEC, "Hv_Mw_Typec_AttachHandler!!!");
}

VOID Hv_Mw_Typec_PdDoneHandler(UCHAR8 ucTypecPortNum)
{
    HV_LOG_INFO(TYPEC, "Hv_Mw_Typec_PdDoneHandler!!!");
}


static void Hv_Typec_VdmConfigTimer_Proc(void *data, USHORT16 usTimerId) 
{
    /* nodify DP cable detection */
    Hv_Mw_Typec_SendEventToSystemQueue(SYSTEM_REV_EVENT_TYPE_INPUTPORT, InputPortEventId_DP_CableConnect, 0);
}

static void Hv_Typec_StartTask_Proc(void *data, USHORT16 usTimerId) 
{
    HV_PSF_HOOK_SEND_EVENT_TO_QUEUE(TYPEC_EVENT_GO, 0);
}

static void Hv_Typec_DetachTimer_Proc(void *data, USHORT16 usTimerId) 
{
    const PowerManager *pstPowerManager = Hv_Mw_Power_GetManagerPtr();
    if (pstPowerManager->bPowerDeliveryFlag == HV_FALSE)
    {
        /* set power state to database for pm. */
        HV_COMMON_DATABASE_SET(TimeRec, ucPmPowerMode, POWER_STATE_STANDBY);
        HV_VOS_QUEUE_S *pstEventHubQueue = Hv_Mw_System_GetEventHubQueue();
        HV_ASSERT_VALID_PTR_VOID(pstEventHubQueue);
        Event stEvent = {0};
        stEvent.usEventId = EVENT_HUB_REV_MSG_ID_ENTER_STANDBY;
        stEvent.usValue = 0;
        HV_ASSERT_TRUE_VOID(Hv_Vos_QueueSend(pstEventHubQueue, &stEvent));
        HV_LOG_INFO(PM, "send standby message to EventHub success!!!");
    }
}

VOID Hv_Mw_Typec_VdmDoneHandler(UCHAR8 ucTypecPortNum)
{
    HV_LOG_DEBUG(TYPEC, "Hv_Mw_Typec_VdmDoneHandler!!!");
    /* if necessary */
    Hv_Mw_Typec_SendEventToSystemQueue(SYSTEM_REV_EVENT_TYPE_POWER, POWER_EVENT_ID_TYPE_C_DETAIL_VDM_DONE, ucTypecPortNum);
    /* 150ms delay to notify DP cable in */
    Hv_Vos_Timer_Start(pstVdmConfigTimer);
}

VOID Hv_Mw_Typec_VdmExitHandler(UCHAR8 ucTypecPortNum)
{
    HV_LOG_DEBUG(TYPEC, "Hv_Mw_Typec_VdmExitHandler!!!");
    /* nodify DP that type-c cable detach */
    Hv_Mw_Typec_SendEventToSystemQueue(SYSTEM_REV_EVENT_TYPE_INPUTPORT, InputPortEventId_DP_CableDisconnect, ucTypecPortNum);
}

VOID Hv_Mw_Typec_VdmFailedHandler(UCHAR8 ucTypecPortNum)
{
    HV_LOG_DEBUG(TYPEC, "Hv_Mw_Typec_VdmFailedHandler!!!");
    /* need to do: switch to Billboard */
#ifdef CONFIG_USER_USB_BILLBOARD_ON
    Hv_Mw_SendEvtToUsb(USB_EVENT_START_BILLBOARD, 0);
#endif
}

/**
 * @brief Set DP lane rientation and PinAssignment.
 * @param[in] ucTypecPortNum  Typec port numbuer.
 * @param[in] ucOrientation . 
 * @param[in] ucPinAssignment 0(C), 1(D), 2(E). 
 * @return VOID
 */
VOID Hv_Mw_Typec_VdmConfigHandler(UCHAR8 ucTypecPortNum, UCHAR8 ucOrientation, UCHAR8 ucPinAssignment)
{
#if (HV_TYPEC_CONFIG_SSPWD_EN == TYPEC_FUNCTION_ON)
    /* solution for Samsung mobile phone's waterdrop issue */
    Hv_Drv_Typec_DpAuxPowerOn();
#endif
    if(ucOrientation < 2)
    {
        HV_LOG_INFO(TYPEC, "vdm config handler ucOrientation = %d \n", ucOrientation);
        Hv_Mw_DpRx_SetTypecOrientation(HV_BOARD_CONFIG_TYPEC0_TO_DP, ucOrientation, ucPinAssignment);
    }
    else
    {
        /* Do Nothing*/
    }
    
    return;
}

VOID Hv_Mw_Typec_SimulateUnplugPlug(VOID)
{
    Hv_Drv_Typec_SimulateUnplugPlug();

    return;
}

VOID Hv_Mw_Typec_AMESetDpHpd(UCHAR8 ucTypecPortNum, BOOL bHpdEn)
{
    App_AMESetDpHpd(ucTypecPortNum, bHpdEn);
    return;
}

VOID Hv_Mw_Typec_AMESetDpIrqHpd(UCHAR8 ucTypecPortNum)
{
    App_AMESetDpIrqHpd(ucTypecPortNum);
    return;
}

BOOL Hv_Mw_Typec_AMEGetStatus(UCHAR8 ucTypecPortNum)
{
    return App_AMEGetStatus(ucTypecPortNum); 
}

#if (HV_PROJECT_CONFIG_SMART_POWER == HV_CONFIG_ON)
VOID Hv_Mw_Typec_UpdateHighPower(UCHAR8 u8HighPower)
{
    static UCHAR8 su8Power_pre = 0;

    if (u8HighPower == 1 && su8Power_pre == 0)
    {
        HV_LOG_INFO(TYPEC, "set TypecSourcePower 90W");
        su8Power_pre = 1;
        Hv_Drv_Typec_Update_SourcePDOs(0, 1);

        /*发送hardreset， 重新握手*/
        Hv_Drv_TypecSendHardReset(0);
    }
    else if (u8HighPower == 0 && su8Power_pre == 1)
    {
        HV_LOG_INFO(TYPEC, "set TypecSourcePower 65W");
        su8Power_pre = 0;
        Hv_Drv_Typec_Update_SourcePDOs(0, 0);

        /*发送hardreset， 重新握手*/
        Hv_Drv_TypecSendHardReset(0);
    }
}
#endif

VOID Hv_Mw_Typec_PlatformCbInit(VOID)
{
    s_stTypecPlatformCB.DetachHandler     = Hv_Mw_Typec_DetachHandler;
    s_stTypecPlatformCB.AttachHandler     = Hv_Mw_Typec_AttachHandler;
    s_stTypecPlatformCB.PdDoneHandler     = Hv_Mw_Typec_PdDoneHandler;
    s_stTypecPlatformCB.VdmDoneHandler    = Hv_Mw_Typec_VdmDoneHandler;
    s_stTypecPlatformCB.VdmExitHandler    = Hv_Mw_Typec_VdmExitHandler;
    s_stTypecPlatformCB.VdmFailedHandler  = Hv_Mw_Typec_VdmFailedHandler;
    s_stTypecPlatformCB.VdmConfigHandler  = Hv_Mw_Typec_VdmConfigHandler;
    s_stTypecPlatformCB.GetTypecQueue     = Hv_Mw_System_GetTypecQueue;
    s_stTypecPlatformCB.DpRxReset         = Hv_Mw_DpRx_Reset;

    Hv_Drv_Typec_FlatformCbInit(&s_stTypecPlatformCB);
    return;
}

VOID Hv_Mw_Typec_TaskStartInit(VOID)
{
#if CONFIG_HOOK_DEBUG_MSG
    Hv_Drv_Typec_DebugMemInit();
#endif
    Hv_Mw_Typec_PlatformCbInit();
    Hv_Drv_Typec_PSF_Init();

    HV_TIMER_INIT_PARAMS stTypecTaskStartTimerParam = \
    {"TypecTaskStartTimer", 10, HV_FALSE, NULL, Hv_Typec_StartTask_Proc, EVENT_TIMER_MAX};
    pstTypecTaskStartTimer = Hv_Vos_Timer_Create(&stTypecTaskStartTimerParam);
    Hv_Vos_Timer_Start(pstTypecTaskStartTimer);

    HV_TIMER_INIT_PARAMS stVDMConfigTimerParam = 
        {"VDMConfigTimerParam", 500, HV_FALSE, NULL, Hv_Typec_VdmConfigTimer_Proc, EVENT_TIMER_MAX};
    pstVdmConfigTimer = Hv_Vos_Timer_Create(&stVDMConfigTimerParam);

    HV_TIMER_INIT_PARAMS stTypecNotifyDetachParam =
        {"TypecNotifyPWRForDetach", 500, HV_FALSE, NULL, Hv_Typec_DetachTimer_Proc, EVENT_TIMER_MAX};
    pstNotifyPWRForDetachTimer = Hv_Vos_Timer_Create(&stTypecNotifyDetachParam);

    return;
}

VOID Hv_Mw_Typec_DpSignalLoss(VOID)
{
    Hv_Drv_Typec_DpSignalLoss();
    return;
}

#if ((HV_TYPEC_CONFIG_TYPEC_MODE == TYPEC_MODE_DRP) || (HV_TYPEC_CONFIG_TYPEC_MODE == TYPEC_MODE_SRC))
VOID Hv_Mw_Typec_GetSourceCapabilities(UCHAR8 ucTypecPortNum, UCHAR8 *pucSrcPDOCnt, UINT32 *puiDataObj)
{
    App_GetSourceCapabilities(ucTypecPortNum, pucSrcPDOCnt, puiDataObj);
    return;
}
#endif

VOID Hv_Mw_TypecTaskHandle(VOID* arg)
{
    TypecEvent stEventMsg = {0};

    Hv_Vos_MSleep(2000);
    HV_VOS_QUEUE_S* pTypecQueue = Hv_Mw_System_GetTypecQueue();
    HV_ASSERT_VALID_PTR_VOID(pTypecQueue);
    Hv_Mw_Typec_TaskStartInit();

    for (;;)
    {
        if (Hv_Vos_QueueRecv(pTypecQueue, (VOID*)&stEventMsg))
        {
            Hv_Mw_Typec_RunStateMachine(&stEventMsg);
            HV_W32(0x11011A04, Hv_Drv_Typec_GetTaskPriority());
        }

        Hv_Vos_TaskYield();
    }    
    return;
}

