/**
 * @file hv_mw_OsdOperation.c
 * @brief osd MiddleWare operation c file.
 * @details This file provides the following functions: \n
 *          (1) Osd scene/scenepart/window/item/component show/hide functions \n
 *          (2) Osd item state update functions \n
 *          (3) Osd dynamic item/component update functions \n
 *          (4) Osd userdata update functions \n
 *
 * @author HiView SoC Software Team
 * @version 1.0.0
 * @date 2022-09-01
 * @copyright Copyright(c),2022-8, Hiview Software. All rights reserved.
 * @par History:
 * <table>
 * <tr><th>Author       <th>Date            <th>Change Description
 * <tr><td>shanpeng     <td>2022-09-01      <td>create
 * </table>
 */

#include "hv_mw_OsdComm.h"
#include "hv_mw_OsdLog.h"
#include "hv_mw_OsdInit.h"
#include "hv_app_OsdDynamicUpdate.h"
#if (HV_CONFIG_ON == HV_PROJECT_CONFIG_OSD_MIRROR)
#include "hv_app_OsdComponentMirrorUpdate.h"
#endif
#include "hv_mw_OsdOperation.h"
#include "hv_drv_Osd.h"

#if (HV_PROJECT_CONFIG_OSD_FORCE_REDRAW_ALL == HV_CONFIG_OFF)
typedef struct _OsdFontRes
{
    USHORT16 usY;
    USHORT16 usResType;
    UCHAR8 ucBitType;
    UCHAR8 ucCount;
} OsdFontRes;
#endif

#ifdef OSD_CHECK_DISABLE_HIDE_AND_ITEMLIST_AUTO_ALIGN
#define TYPE_NONE   (0)
#define TYPE_CHILD  (1)
#define TYPE_VALUE  (2)

typedef struct _OsdPositionNode
{
    struct LinkedList stNode;
    USHORT16 usX;
    USHORT16 usY;
} OsdPositionNode;
#endif

typedef struct _OsdOperationManager {
    /* 正在聚焦的ItemId */
    USHORT16 usFocusingItemId;
    /* 选择将要聚焦的ItemId */
    USHORT16 usChosenItemId;
    /* Scene的Window可见标志，hide scene时保存，show scene时读取 */
    UINT32 auiSceneVisableFlag[SCENE_ID_MAX];
    /* 当前页ItemList最大个数 */
    USHORT16 usItemNumInPage;
    /* 是否需要重画 */
    BOOL bNeedRedraw;
    /* 是否需要分帧画 */
    BOOL bNeedMultiFrameDraw;
#ifndef OSD_CHECK_DISABLE_BY_EACH_ITEM
    /* Item禁用列表 */
    USHORT16 ausItemDisabledArray[OSD_DISABLE_ITEM_NUM_MAX];
    /* 当前Item禁用数量 */
    USHORT16 usCurrentDisableNum;
#endif
#if (HV_PROJECT_CONFIG_OSD_FORCE_REDRAW_ALL == HV_CONFIG_OFF)
    /* 正在使用的逻辑windowId */
    UCHAR8 ucWinUsing[PHY_WIN_3 + 1];
    /* 记录当前物理win中font的Y及resource */
    OsdFontRes stFontResList[PHY_WIN_3 + 1][OSD_FONT_Y_NUM_MAX];
#endif
#ifdef OSD_CHECK_DISABLE_HIDE_AND_ITEMLIST_AUTO_ALIGN
    struct LinkedList stItemPosList;
    struct LinkedList stItemPosList2;
#endif
} OsdOperationManager;

static OsdOperationManager s_stOsdOpsData;

static const CHAR8* s_apcItemStateNameTbl[] =
{
    [OSD_ITEM_NORMAL]         = "normal",
    [OSD_ITEM_FOCUS]          = "focus",
    [OSD_ITEM_STAY]           = "stay",
    [OSD_ITEM_SELECT]         = "select",
    [OSD_ITEM_SELECT_FOCUS]   = "select&focus",
    [OSD_ITEM_DISABLED]       = "disabled",
};

#ifdef HV_SCALER_DEBUG_VERSION
static UINT32 s_uiDebugDynamicCount = 0;
static UINT32 s_uiDebugDynamicDataSize = 0;
#endif

#define SET_SCENE_VISABLE_FLAG(id, x)        (s_stOsdOpsData.auiSceneVisableFlag[id] |= (BIT_0 << (x)))
#define GET_SCENE_VISABLE_FLAG(id, x)        ((s_stOsdOpsData.auiSceneVisableFlag[id] >> (x)) & BIT_0)
#define CLEAR_SCENE_VISABLE_FLAG(id)         (s_stOsdOpsData.auiSceneVisableFlag[id] = 0x0)

BOOL Hv_Mw_OsdOpsIsRedraw(VOID)
{
#if (HV_PROJECT_CONFIG_OSD_FORCE_REDRAW_ALL == HV_CONFIG_OFF)
    return s_stOsdOpsData.bNeedRedraw;
#else
    return HV_TRUE;
#endif
}

VOID Hv_Mw_OsdOpsClearRedrawFlag(VOID)
{
    s_stOsdOpsData.bNeedRedraw = HV_FALSE;
}

VOID Hv_Mw_OsdOpsForceRedraw(BOOL bRedraw)
{
    OSD_LOG_DEBUG("Force redraw = %d", bRedraw);
    s_stOsdOpsData.bNeedRedraw = bRedraw;
}

#if (HV_PROJECT_CONFIG_OSD_ROTATION == HV_CONFIG_ON)
static VOID Hv_Mw_OsdAppendComponentBox(OsdComponentNode *pstNode)
{
    if (OSD_ROTATION_NONE == Hv_Drv_OsdGetRotateType())
    {
        return;
    }

    HV_ASSERT_VALID_PTR_VOID(pstNode);

#if (HV_PROJECT_CONFIG_OSD_CUSTOM_GROUP_SUPPORT == HV_CONFIG_ON)
    if (OSD_CP_TYPE_CUSTOM_GROUP == pstNode->stCp.enType)
    {
        OsdComponentNode *pstSubNode = NULL;
        OsdCustomGroup *pstCG = (OsdCustomGroup *)Hv_Mw_OsdGetComponentPropAddr(pstNode->stCp);
        HV_ASSERT_VALID_PTR_VOID(pstCG);
        for (UCHAR8 ucId = 0; ucId < pstCG->ucComponentCount; ucId++)
        {
            pstSubNode = Hv_Mw_OsdComponentGetNode(pstCG->astComponents[ucId]);
            HV_ASSERT_VALID_PTR_VOID(pstSubNode);
            Hv_Mw_OsdAppendComponentBox(pstSubNode);
        }
        return;
    }
    else
#endif
    {
        OsdWindow *pstWindow = Hv_Mw_OsdComponentGetWindow(pstNode->stCp);
        HV_ASSERT_VALID_PTR_VOID(pstWindow);
        HV_ASSERT_VALID_PTR_VOID(pstWindow->pstScene);
        Hv_Drv_OsdSetRotationPartFlag(pstWindow->pstScene->ucId);
    }

    if (CP_IS_FONT(pstNode->stCp))
    {
        Hv_Drv_OsdFont_CalculateComponentBox(pstNode, HV_FALSE);
        Hv_Drv_OsdAppendDrawAreaBox(pstNode->stBox);
        OSD_LOG_DEBUG("Append %s's box(%d, %d), %d x %d", CP_NAME(pstNode->stCp),
                      pstNode->stBox.usX, pstNode->stBox.usY, pstNode->stBox.usW, pstNode->stBox.usH);
    }
    else
    {
        OsdBox stBox = {0};
        VOID *pvCpData = (NULL != pstNode->pvDynamicData) ? pstNode->pvDynamicData : pstNode->pvCpData;
        switch (pstNode->stCp.enType)
        {
            case OSD_CP_TYPE_RECT:
            {
                stBox.usW = ((OsdRect*)pvCpData)->usWidth;
                stBox.usH = ((OsdRect*)pvCpData)->usHeight;
                stBox.usX = ((OsdRect*)pvCpData)->usX;
                stBox.usY = ((OsdRect*)pvCpData)->usY;
                break;
            }

            case OSD_CP_TYPE_RECTGRID:
            {
                stBox.usW = ((OsdRectGrid*)pvCpData)->usWidth;
                stBox.usH = ((OsdRectGrid*)pvCpData)->usHeight;
                stBox.usX = ((OsdRectGrid*)pvCpData)->usX;
                stBox.usY = ((OsdRectGrid*)pvCpData)->usY;
                break;
            }

            case OSD_CP_TYPE_LINEGRID:
            {
                stBox.usW = ((OsdLineGrid*)pvCpData)->usWidth;
                stBox.usH = ((OsdLineGrid*)pvCpData)->usHeight;
                stBox.usX = ((OsdLineGrid*)pvCpData)->usX;
                stBox.usY = ((OsdLineGrid*)pvCpData)->usY;
                break;
            }

            case OSD_CP_TYPE_LINE:
            {
                stBox.usW = ((OsdLine*)pvCpData)->usWidth;
                stBox.usH = ((OsdLine*)pvCpData)->usHeight;
                stBox.usX = ((OsdLine*)pvCpData)->usX;
                stBox.usY = ((OsdLine*)pvCpData)->usY;
                break;
            }

            default:
            {
                break;
            }
        }
        Hv_Drv_OsdAppendDrawAreaBox(stBox);
        OSD_LOG_DEBUG("Append %s's box(%d, %d), %d x %d", CP_NAME(pstNode->stCp),
                      stBox.usX, stBox.usY, stBox.usW, stBox.usH);
    }
    Hv_Drv_OsdSetRotationPartFlag(SCENE_ID_MAX);
}
#endif

#if (HV_PROJECT_CONFIG_OSD_FORCE_REDRAW_ALL == HV_CONFIG_OFF)

static VOID Hv_Mw_OsdRecordFontRes(UCHAR8 ucWinPhyId, OsdComponentNode *pstNode)
{
    HV_ASSERT_TRUE_VOID(ucWinPhyId <= PHY_WIN_3);
    HV_ASSERT_VALID_PTR_VOID(pstNode);

    UCHAR8 ucRowNum = Hv_Drv_OsdFont_GetComponentRows(pstNode);
    USHORT16 usCpY = Hv_Drv_OsdFont_GetComponentY(pstNode);
    USHORT16 usResType = 0;
    UCHAR8 ucBitType = 0;
    UCHAR8 ucResH = 0;
    UCHAR8 ucResV = 0;
    USHORT16 usY = 0;
    USHORT16 usUnusedId = OSD_FONT_Y_NUM_MAX;
    BOOL bFound = HV_FALSE;

    Hv_Drv_OsdFont_GetComponentResType(pstNode, &usResType, &ucBitType);
    Hv_Drv_OsdResource_GetSize(usResType, &ucResH, &ucResV);
    OSD_LOG_VERBOSE("Record win-%d: %s: Y=%d, RowNum=%d, Resource: ResSize=%dx%d, %d-bit",
                   ucWinPhyId, CP_NAME(pstNode->stCp), usCpY, ucRowNum, ucResH, ucResV, Hv_Drv_OsdResource_GetBits(ucBitType));
    for (UCHAR8 ucRow = 0; ucRow < ucRowNum; ucRow++)
    {
        usY = usCpY + ucRow * ucResV;
        bFound = HV_FALSE;
        for (UCHAR8 ucId = 0; ucId < OSD_FONT_Y_NUM_MAX; ucId++)
        {
            /* Find Y */
            if (usY == s_stOsdOpsData.stFontResList[ucWinPhyId][ucId].usY)
            {
                bFound = HV_TRUE;
                if ((ucBitType != s_stOsdOpsData.stFontResList[ucWinPhyId][ucId].ucBitType) ||
                    (usResType != s_stOsdOpsData.stFontResList[ucWinPhyId][ucId].usResType))
                {
                    OSD_LOG_VERBOSE("    Y = %d: New resource type: %d-bit, %dx%d, need redraw",
                                   usY, Hv_Drv_OsdResource_GetBits(ucBitType), ucResH, ucResV);
                    s_stOsdOpsData.stFontResList[ucWinPhyId][ucId].ucBitType = ucBitType;
                    s_stOsdOpsData.stFontResList[ucWinPhyId][ucId].usResType = usResType;
                    s_stOsdOpsData.bNeedRedraw = HV_TRUE;
                }
                s_stOsdOpsData.stFontResList[ucWinPhyId][ucId].ucCount++;
                OSD_LOG_VERBOSE("    Found Y = %d in [%d], count = %d", usY, ucId, s_stOsdOpsData.stFontResList[ucWinPhyId][ucId].ucCount);
                break;
            }

            if (OSD_FONT_Y_NUM_MAX == usUnusedId)
            {
                if (INVALID_PARAM_USHORT16 == s_stOsdOpsData.stFontResList[ucWinPhyId][ucId].usY)
                {
                    usUnusedId = ucId;
                }
            }
        }

        if (!bFound)
        {
            HV_ASSERT_TRUE_VOID(usUnusedId < OSD_FONT_Y_NUM_MAX);
            s_stOsdOpsData.stFontResList[ucWinPhyId][usUnusedId].usY = usY;
            s_stOsdOpsData.stFontResList[ucWinPhyId][usUnusedId].ucBitType = ucBitType;
            s_stOsdOpsData.stFontResList[ucWinPhyId][usUnusedId].usResType = usResType;
            s_stOsdOpsData.stFontResList[ucWinPhyId][usUnusedId].ucCount = 1;
            OSD_LOG_VERBOSE("    New Y=%d in [%d], need redraw", usY, usUnusedId);
            usUnusedId = OSD_FONT_Y_NUM_MAX;
            s_stOsdOpsData.bNeedRedraw = HV_TRUE;
        }
    }
}

static VOID Hv_Mw_OsdDeleteFontRes(UCHAR8 ucWinPhyId, OsdComponentNode *pstNode)
{
    HV_ASSERT_TRUE_VOID(ucWinPhyId <= PHY_WIN_3);
    HV_ASSERT_VALID_PTR_VOID(pstNode);

    UCHAR8 ucRowNum = Hv_Drv_OsdFont_GetComponentRows(pstNode);
    USHORT16 usCpY = Hv_Drv_OsdFont_GetComponentY(pstNode);
    USHORT16 usResType = 0;
    UCHAR8 ucBitType = 0;
    UCHAR8 ucResH = 0;
    UCHAR8 ucResV = 0;
    USHORT16 usY = 0;
    UCHAR8 ucId = 0;

    Hv_Drv_OsdFont_GetComponentResType(pstNode, &usResType, &ucBitType);
    Hv_Drv_OsdResource_GetSize(usResType, &ucResH, &ucResV);
    OSD_LOG_VERBOSE("Delete win-%d: %s: Y=%d, RowNum=%d, Resource: ResSize=%dx%d, %d-bit",
                   ucWinPhyId, CP_NAME(pstNode->stCp), usCpY, ucRowNum, ucResH, ucResV, Hv_Drv_OsdResource_GetBits(ucBitType));
    for (UCHAR8 ucRow = 0; ucRow < ucRowNum; ucRow++)
    {
        usY = usCpY + ucRow * ucResV;
        for (ucId = 0; ucId < OSD_FONT_Y_NUM_MAX; ucId++)
        {
            /* Find Y */
            if (usY == s_stOsdOpsData.stFontResList[ucWinPhyId][ucId].usY)
            {
                HV_ASSERT_TRUE_VOID(s_stOsdOpsData.stFontResList[ucWinPhyId][ucId].ucCount > 0);
                if (s_stOsdOpsData.stFontResList[ucWinPhyId][ucId].ucCount > 0)
                {
                    s_stOsdOpsData.stFontResList[ucWinPhyId][ucId].ucCount--;
                }
                OSD_LOG_VERBOSE("    Found Y = %d in [%d], count = %d", usY, ucId, s_stOsdOpsData.stFontResList[ucWinPhyId][ucId].ucCount);
                break;
            }
        }
        if (ucId >= OSD_FONT_Y_NUM_MAX)
        {
            OSD_LOG_WARN("No Y = %d in FontResList", usY);
        }
    }
}

static VOID Hv_Mw_OsdInitFontResList(UCHAR8 ucWinPhyId)
{
    HV_ASSERT_TRUE_VOID(ucWinPhyId <= PHY_WIN_3);

    OSD_LOG_DEBUG("Init FontResList[%d]", ucWinPhyId);
    for (UCHAR8 ucId = 0; ucId < OSD_FONT_Y_NUM_MAX; ucId++)
    {
        s_stOsdOpsData.stFontResList[ucWinPhyId][ucId].usY = INVALID_PARAM_USHORT16;
        s_stOsdOpsData.stFontResList[ucWinPhyId][ucId].ucBitType = INVALID_PARAM_UHCAR8;
        s_stOsdOpsData.stFontResList[ucWinPhyId][ucId].usResType = INVALID_PARAM_USHORT16;
        s_stOsdOpsData.stFontResList[ucWinPhyId][ucId].ucCount = 0;
    }
}

static VOID Hv_Mw_OsdClearFontResList(OsdWindow *pstWindow)
{
    HV_ASSERT_VALID_PTR_VOID(pstWindow);
    HV_ASSERT_TRUE_VOID(pstWindow->ucPhyId <= PHY_WIN_3);

    if (pstWindow->ucId == s_stOsdOpsData.ucWinUsing[pstWindow->ucPhyId])
    {
        Hv_Mw_OsdInitFontResList(pstWindow->ucPhyId);
        s_stOsdOpsData.ucWinUsing[pstWindow->ucPhyId] = WIN_ID_MAX;
    }
}

static VOID Hv_Mw_OsdUpdateFontResList(OsdWindow *pstWindow)
{
    HV_ASSERT_VALID_PTR_VOID(pstWindow);
    HV_ASSERT_TRUE_VOID(pstWindow->ucPhyId <= PHY_WIN_3);

    Hv_Mw_OsdInitFontResList(pstWindow->ucPhyId);
    s_stOsdOpsData.ucWinUsing[pstWindow->ucPhyId] = pstWindow->ucId;

    OSD_LOG_DEBUG("Update FontResList[%d]", pstWindow->ucPhyId);
    OsdComponentNode *pstNode = pstWindow->stFontShowListHead.pstNode;
    for (; pstNode != NULL; pstNode = pstNode->pstNext)
    {
        Hv_Mw_OsdRecordFontRes(pstWindow->ucPhyId, pstNode);
    }
}

VOID Hv_Mw_OsdResetFontResList(VOID)
{
    OSD_LOG_DEBUG("Reset all FontResList");

    for (UCHAR8 ucWinPhyId = 0; ucWinPhyId <= PHY_WIN_3; ucWinPhyId++)
    {
        for (UCHAR8 ucId = 0; ucId < OSD_FONT_Y_NUM_MAX; ucId++)
        {
            if (0 == s_stOsdOpsData.stFontResList[ucWinPhyId][ucId].ucCount)
            {
                s_stOsdOpsData.stFontResList[ucWinPhyId][ucId].usY = INVALID_PARAM_USHORT16;
                s_stOsdOpsData.stFontResList[ucWinPhyId][ucId].ucBitType = INVALID_PARAM_UHCAR8;
                s_stOsdOpsData.stFontResList[ucWinPhyId][ucId].usResType = INVALID_PARAM_USHORT16;
            }
        }
    }
}

#ifdef HV_SCALER_DEBUG_VERSION
VOID Hv_Mw_OsdPrintFontResList(VOID)
{
    UCHAR8 ucResH = 0;
    UCHAR8 ucResV = 0;
    UCHAR8 ucResBit = 0;

    OSD_LOG_INFO("Font resource list (max num = %d):", OSD_FONT_Y_NUM_MAX);
    for (UCHAR8 ucWinPhyId = 0; ucWinPhyId <= PHY_WIN_3; ucWinPhyId++)
    {
        OSD_LOG_INFO("  Win-%d", ucWinPhyId);
        for (UCHAR8 ucId = 0; ucId < OSD_FONT_Y_NUM_MAX; ucId++)
        {
            if (0 != s_stOsdOpsData.stFontResList[ucWinPhyId][ucId].ucCount)
            {
                Hv_Drv_OsdResource_GetSize(s_stOsdOpsData.stFontResList[ucWinPhyId][ucId].usResType, &ucResH, &ucResV);
                ucResBit = Hv_Drv_OsdResource_GetBits(s_stOsdOpsData.stFontResList[ucWinPhyId][ucId].ucBitType);
                OSD_LOG_INFO("      [%d] Y = %d, ResType=%dx%d, %d-Bit, Count = %d",
                            ucId, s_stOsdOpsData.stFontResList[ucWinPhyId][ucId].usY,
                            ucResH, ucResV, ucResBit,
                            s_stOsdOpsData.stFontResList[ucWinPhyId][ucId].ucCount);
            }
        }
    }
}
#endif
#endif

BOOL Hv_Mw_OsdOpsIsMultiFrameDraw(VOID)
{
#if (HV_PROJECT_CONFIG_OSD_FORCE_MULTIFRAME_DRAW == HV_CONFIG_OFF)
    return s_stOsdOpsData.bNeedMultiFrameDraw;
#else
    return HV_TRUE;
#endif
}

VOID Hv_Mw_OsdOpsClearMultiFrameDrawFlag(VOID)
{
    s_stOsdOpsData.bNeedMultiFrameDraw = HV_FALSE;
}

VOID Hv_Mw_OsdOpsInit(VOID)
{
    s_stOsdOpsData.usFocusingItemId = ITEM_NONE_ID;
    s_stOsdOpsData.usChosenItemId = ITEM_NONE_ID;
    s_stOsdOpsData.bNeedRedraw = HV_FALSE;
    s_stOsdOpsData.bNeedMultiFrameDraw = HV_FALSE;
    s_stOsdOpsData.usItemNumInPage = OSD_MENU_SHOW_ITEM_NUM_MAX;
    for (UCHAR8 ucId = 0; ucId < SCENE_ID_MAX; ucId++)
    {
        s_stOsdOpsData.auiSceneVisableFlag[ucId] = 0;
    }

#ifndef OSD_CHECK_DISABLE_BY_EACH_ITEM
    HV_MEMSET(s_stOsdOpsData.ausItemDisabledArray, 0, sizeof(s_stOsdOpsData.ausItemDisabledArray));
    s_stOsdOpsData.usCurrentDisableNum = 0;
#endif

#if (HV_PROJECT_CONFIG_OSD_FORCE_REDRAW_ALL == HV_CONFIG_OFF)
    for (UCHAR8 ucId = 0; ucId <= PHY_WIN_3; ucId++)
    {
        Hv_Mw_OsdInitFontResList(ucId);
        s_stOsdOpsData.ucWinUsing[ucId] = WIN_ID_MAX;
    }
#endif

#ifdef OSD_CHECK_DISABLE_HIDE_AND_ITEMLIST_AUTO_ALIGN
    Hv_Comm_LinkedList_Init(&s_stOsdOpsData.stItemPosList);
    Hv_Comm_LinkedList_Init(&s_stOsdOpsData.stItemPosList2);
#endif

#ifdef HV_SCALER_DEBUG_VERSION
    s_uiDebugDynamicCount = 0;
    s_uiDebugDynamicDataSize = 0;
#endif
}

OsdWindow *Hv_Mw_OsdComponentGetWindow(OsdComponent stCp)
{
    VOID *pvCpProp = (VOID *)Hv_Mw_OsdGetComponentPropAddr(stCp);
    HV_ASSERT_VALID_PTR_RET(pvCpProp, NULL);
    UINT32 uiAddr = (UINT32)pvCpProp;
    UCHAR8 ucWindowIndex = WIN_ID_MAX;

    static const USHORT16 s_ausCpWinTbl[] = {
        [OSD_CP_TYPE_NONE] =            0,
        [OSD_CP_TYPE_TEXT] =            offsetof(OsdText, ucWindowIndex),
        [OSD_CP_TYPE_TEXT_LIST] =       offsetof(OsdTextList, ucWindowIndex),
        [OSD_CP_TYPE_NUMBER] =          offsetof(OsdNumber, ucWindowIndex),
        [OSD_CP_TYPE_INTACT_NUMBER] =   0, // TODO
        [OSD_CP_TYPE_ASCII_STRING] =    offsetof(OsdAsciiString, ucWindowIndex),
        [OSD_CP_TYPE_ICON] =            offsetof(OsdIcon, ucWindowIndex),
        [OSD_CP_TYPE_ICON_LIST] =       offsetof(OsdIconList, ucWindowIndex),
        [OSD_CP_TYPE_BMP] =             offsetof(OsdBmp, ucWindowIndex),
        [OSD_CP_TYPE_BMP_LIST] =        offsetof(OsdBmpList, ucWindowIndex),
        [OSD_CP_TYPE_RECT] =            offsetof(OsdRect, ucWindowIndex),
        [OSD_CP_TYPE_RECTGRID] =        offsetof(OsdRectGrid, ucWindowIndex),
        [OSD_CP_TYPE_LINEGRID] =        offsetof(OsdLineGrid, ucWindowIndex),
        [OSD_CP_TYPE_LINE] =            offsetof(OsdLine, ucWindowIndex),
        [OSD_CP_TYPE_GRAPHIC] =         offsetof(OsdGraphic, ucWindowIndex),
    };

    ucWindowIndex = *(UCHAR8 *)(uiAddr + s_ausCpWinTbl[stCp.enType]);
    return OSD_GET_WINDOW(ucWindowIndex);
}

#ifdef HV_SCALER_DEBUG_VERSION
static VOID Hv_Mw_OsdDebugDynamicData(OsdComponent stCp, BOOL bIncrease)
{
    UINT32 uiSize = Hv_Mw_OsdGetComponentPropSize(stCp.enType);

    if (bIncrease)
    {
        s_uiDebugDynamicCount++;
        s_uiDebugDynamicDataSize += uiSize;
    }
    else
    {
        s_uiDebugDynamicCount--;
        s_uiDebugDynamicDataSize -= uiSize;
    }
}

VOID Hv_Mw_OsdPrintAllNodeList(OsdWindow *pstWindow)
{
    HV_ASSERT_VALID_PTR_VOID(pstWindow);
    OsdComponentNode* pstNode = NULL;
    USHORT16 ucNodeCount = 0;

    OSD_LOG_INFO("Window-%d(Phy-%d)'s all NodeList:", pstWindow->ucId, pstWindow->ucPhyId);
    pstNode = pstWindow->stCpShowListHead.pstNode;
    OSD_LOG_INFO(" CpShowList(%d), update(%d):", pstWindow->stCpShowListHead.usTotalNum, pstWindow->stCpShowListHead.usUpdateNum);
    while (pstNode)
    {
        OSD_LOG_INFO("     %s", CP_NAME(pstNode->stCp));
        pstNode = pstNode->pstNext;
    }
    ucNodeCount += pstWindow->stCpShowListHead.usTotalNum;

    pstNode = pstWindow->stFontShowListHead.pstNode;
    OSD_LOG_INFO(" FontShowList(%d), update(%d):", pstWindow->stFontShowListHead.usTotalNum, pstWindow->stFontShowListHead.usUpdateNum);
    while (pstNode)
    {
        OSD_LOG_INFO("     %s", CP_NAME(pstNode->stCp));
#if (HV_PROJECT_CONFIG_OSD_CUSTOM_GROUP_SUPPORT == HV_CONFIG_ON)
        if (OSD_CP_TYPE_CUSTOM_GROUP == pstNode->stCp.enType)
        {
            const OsdCustomGroup *pstCustomGroup = OSD_GET_CUSTOM_GROUP_PROP(pstNode->stCp.usIndex);
            HV_ASSERT_VALID_PTR_VOID(pstCustomGroup);
            for (USHORT16 usId = 0; usId < pstCustomGroup->ucComponentCount; usId++)
            {
                OSD_LOG_INFO("         %s", CP_NAME(pstCustomGroup->astComponents[usId]));
            }
        }
#endif
        pstNode = pstNode->pstNext;
    }
    ucNodeCount += pstWindow->stFontShowListHead.usTotalNum;

    pstNode = pstWindow->stDeleteListHead.pstNode;
    OSD_LOG_INFO(" DeleteList(%d)", pstWindow->stDeleteListHead.usTotalNum);
    while (pstNode)
    {
        OSD_LOG_INFO("     %s", CP_NAME(pstNode->stCp));
        pstNode = pstNode->pstNext;
    }
    ucNodeCount += pstWindow->stDeleteListHead.usTotalNum;

    pstNode = pstWindow->stFontDeleteListHead.pstNode;
    OSD_LOG_INFO(" FontDeleteList(%d)", pstWindow->stFontDeleteListHead.usTotalNum);
    while (pstNode)
    {
        OSD_LOG_INFO("     %s", CP_NAME(pstNode->stCp));
#if (HV_PROJECT_CONFIG_OSD_CUSTOM_GROUP_SUPPORT == HV_CONFIG_ON)
        if (OSD_CP_TYPE_CUSTOM_GROUP == pstNode->stCp.enType)
        {
            const OsdCustomGroup *pstCustomGroup = OSD_GET_CUSTOM_GROUP_PROP(pstNode->stCp.usIndex);
            HV_ASSERT_VALID_PTR_VOID(pstCustomGroup);
            for (USHORT16 usId = 0; usId < pstCustomGroup->ucComponentCount; usId++)
            {
                OSD_LOG_INFO("         %s", CP_NAME(pstCustomGroup->astComponents[usId]));
            }
        }
#endif
        pstNode = pstNode->pstNext;
    }
    ucNodeCount += pstWindow->stFontDeleteListHead.usTotalNum;

    OSD_LOG_INFO("Node num = %d, size = %dB", ucNodeCount, sizeof(OsdComponentNode) * ucNodeCount);
    OSD_LOG_INFO("Total current dynamic cp num = %d, size = %dB", s_uiDebugDynamicCount, s_uiDebugDynamicDataSize);
}

VOID Hv_Mw_OsdSceneDebugStat(VOID)
{
    OsdScene *pstScene = NULL;
    const OsdSceneProp *pstSceneProp = NULL;
    OsdWindow *pstWindow = NULL;
    OsdItem *pstItem = NULL;
    const OsdItemProp *pstItemProp = NULL;
    UCHAR8 ucWinId = 0;
    USHORT16 usItemId = 0;
    UCHAR8 ucId = 0;
    UCHAR8 ucWindowStartId = 0;
    UCHAR8 ucWindowEndId = 0;
    const OsdComponent *pstCpList = NULL;

    OSD_LOG_INFO("====================================================================");
    if (ITEM_NONE_ID == OSD_GET_FOCUSING())
    {
        OSD_LOG_INFO("*****  No focusing item  *****");
    }
    else
    {
        OSD_LOG_INFO("*****  Current focusing item = %s(%d)  *****", ITEM_NAME(OSD_GET_FOCUSING()), OSD_GET_FOCUSING());
    }
    OSD_LOG_INFO("--------------------------------------------------------------------");
    OSD_LOG_INFO("Current visable scene:");
    for (UCHAR8 ucSceneId = 0; ucSceneId < SCENE_ID_MAX; ucSceneId++)
    {
        pstScene = OSD_GET_SCENE(ucSceneId);
        pstSceneProp = OSD_GET_SCENE_PROP(ucSceneId);
        HV_ASSERT_VALID_PTR_VOID(pstScene);
        HV_ASSERT_VALID_PTR_VOID(pstSceneProp);
        if (pstScene->bVisable)
        {
            OSD_LOG_INFO("--------------------------------------------------");
            OSD_LOG_INFO("Scene(%d) %s:", ucSceneId, SCENE_NAME(ucSceneId));
            OSD_LOG_INFO("  position(%d, %d), width = %d, height = %d", pstScene->usX, pstScene->usY, pstScene->usWidth, pstScene->usHeight);
            OSD_LOG_INFO("Visable items:");
            for (usItemId = pstSceneProp->usItemStartId; usItemId < pstSceneProp->usItemStartId + pstSceneProp->usItemNum; usItemId++)
            {
                pstItem = OSD_GET_ITEM(usItemId);
                HV_ASSERT_VALID_PTR_VOID(pstItem);
                pstItemProp = OSD_GET_ITEM_PROP(usItemId);
                HV_ASSERT_VALID_PTR_VOID(pstItemProp);
                if (pstItem->bVisable)
                {
                    OSD_LOG_INFO("    %s(%d): state=%s, pos=(%d, %d), cursor=%d",
                                  ITEM_NAME(usItemId), usItemId, s_apcItemStateNameTbl[pstItem->ucState],
                                  pstItem->usX, pstItem->usY, pstItemProp->ucCursorStyleId);
#if 1
                    OSD_LOG_INFO("        components contain:");
                    pstCpList = Hv_Mw_OsdGetItemStyle(pstItem->usId, pstItem->ucState);
                    if (NULL != pstCpList)
                    {
                        for (USHORT16 usCpCnt = 0; pstCpList[usCpCnt].enType != OSD_CP_TYPE_NONE; usCpCnt++)
                        {
                            OSD_LOG_INFO("            %s", CP_NAME(pstCpList[usCpCnt]));
#if (HV_PROJECT_CONFIG_OSD_CUSTOM_GROUP_SUPPORT == HV_CONFIG_ON)
                            if (OSD_CP_TYPE_CUSTOM_GROUP == pstCpList[usCpCnt].enType)
                            {
                                const OsdCustomGroup *pstCustomGroup = OSD_GET_CUSTOM_GROUP_PROP(pstCpList[usCpCnt].usIndex);
                                HV_ASSERT_VALID_PTR_VOID(pstCustomGroup);
                                for (USHORT16 usId = 0; usId < pstCustomGroup->ucComponentCount; usId++)
                                {
                                    OSD_LOG_INFO("                %s", CP_NAME(pstCustomGroup->astComponents[usId]));
                                }
                            }
#endif
                        }
                    }
#endif
                    if (E_USERDATA_NONE != pstItemProp->usUserdata)
                    {
                        OSD_LOG_INFO("        bind Userdata: %s", Hv_App_GetUserdataName(pstItemProp->usUserdata));
                        if (pstItemProp->bIsValue)
                        {
                            OSD_LOG_INFO("            bind Value=%d", pstItemProp->usUserdataValue);
                        }
                    }
                }
            }
#if 1
            for (ucId = 0; ucId < pstSceneProp->ucFramePartNum; ucId++)
            {
                ucWindowStartId = pstSceneProp->astFramePart[ucId].ucWindowStartId;
                ucWindowEndId = ucWindowStartId + pstSceneProp->astFramePart[ucId].ucWindowNum;
                for (ucWinId = ucWindowStartId; ucWinId < ucWindowEndId; ucWinId++)
                {
                    pstWindow = OSD_GET_WINDOW(ucWinId);
                    HV_ASSERT_VALID_PTR_VOID(pstWindow);
                    if (pstWindow->bVisable)
                    {
                        OSD_LOG_INFO("--------------------------------------------------");
                        Hv_Mw_OsdPrintAllNodeList(pstWindow);
                    }
                }
            }
#endif
        }
    }
    OSD_LOG_INFO("====================================================================");
}
#endif

static VOID *Hv_Mw_OsdComponentCreateDynamicData(OsdComponent stCp)
{
    VOID *pvCpAddr = (VOID *)Hv_Mw_OsdGetComponentPropAddr(stCp);
    HV_ASSERT_VALID_PTR_RET(pvCpAddr, NULL);
    VOID *pvDynCp = NULL;
    UINT32 uiSize = Hv_Mw_OsdGetComponentPropSize(stCp.enType);

    OSD_LOG_VERBOSE("Create dynamic data for %s", CP_NAME(stCp));
    pvDynCp = HV_MALLOC(uiSize);
    if (pvDynCp)
    {
        HV_MEMCPY(pvDynCp, pvCpAddr, uiSize);
    }

    return pvDynCp;
}

static UINT32 Hv_Mw_OsdComponentGetDynamicFlag(OsdComponent stCp)
{
    VOID *pvCpAddr = (VOID *)Hv_Mw_OsdGetComponentPropAddr(stCp);
    HV_ASSERT_VALID_PTR_RET(pvCpAddr, 0);
#if 0
    UINT32 uiAddr = (UINT32)pvCpProp;

    static const USHORT16 s_ausCpDynamicFlagTbl[] = {
        [OSD_CP_TYPE_NONE] =            0,
        [OSD_CP_TYPE_TEXT] =            offsetof(OsdText, uiDynamicFlag),
        [OSD_CP_TYPE_TEXT_LIST] =       offsetof(OsdTextList, uiDynamicFlag),
        [OSD_CP_TYPE_NUMBER] =          offsetof(OsdNumber, uiDynamicFlag),
        [OSD_CP_TYPE_INTACT_NUMBER] =   0, // TODO
        [OSD_CP_TYPE_ASCII_STRING] =    offsetof(OsdAsciiString, uiDynamicFlag),
        [OSD_CP_TYPE_ICON] =            offsetof(OsdIcon, uiDynamicFlag),
        [OSD_CP_TYPE_ICON_LIST] =       offsetof(OsdIconList, uiDynamicFlag),
        [OSD_CP_TYPE_BMP] =             offsetof(OsdBmp, uiDynamicFlag),
        [OSD_CP_TYPE_BMP_LIST] =        offsetof(OsdBmpList, uiDynamicFlag),
        [OSD_CP_TYPE_RECT] =            offsetof(OsdRect, uiDynamicFlag),
        [OSD_CP_TYPE_RECTGRID] =        offsetof(OsdRectGrid, uiDynamicFlag),
        [OSD_CP_TYPE_LINEGRID] =        offsetof(OsdLineGrid, uiDynamicFlag),
        [OSD_CP_TYPE_LINE] =            offsetof(OsdLine, uiDynamicFlag),
        [OSD_CP_TYPE_GRAPHIC] =         offsetof(OsdGraphic, uiDynamicFlag),
    };

    return *(UINT32 *)(uiAddr + s_ausCpDynamicFlagTbl[stCp.enType]);
#else
    switch (stCp.enType)
    {
        case OSD_CP_TYPE_TEXT:
        {
            return ((OsdText *)pvCpAddr)->uiDynamicFlag;
        }
        case OSD_CP_TYPE_TEXT_LIST:
        {
            return ((OsdTextList *)pvCpAddr)->uiDynamicFlag;
        }
        case OSD_CP_TYPE_NUMBER:
        {
            return ((OsdNumber *)pvCpAddr)->uiDynamicFlag;
        }
        case OSD_CP_TYPE_ASCII_STRING:
        {
            return ((OsdAsciiString *)pvCpAddr)->uiDynamicFlag;
        }
        case OSD_CP_TYPE_ICON:
        {
            return ((OsdIcon *)pvCpAddr)->uiDynamicFlag;
        }
        case OSD_CP_TYPE_ICON_LIST:
        {
            return ((OsdIconList *)pvCpAddr)->uiDynamicFlag;
        }
        case OSD_CP_TYPE_BMP:
        {
            return ((OsdBmp *)pvCpAddr)->uiDynamicFlag;
        }
        case OSD_CP_TYPE_BMP_LIST:
        {
            return ((OsdBmpList *)pvCpAddr)->uiDynamicFlag;
        }
        case OSD_CP_TYPE_GRAPHIC:
        {
            return ((OsdGraphic *)pvCpAddr)->uiDynamicFlag;
        }
        case OSD_CP_TYPE_RECT:
        {
            return ((OsdRect *)pvCpAddr)->uiDynamicFlag;
        }
        case OSD_CP_TYPE_RECTGRID:
        {
            return ((OsdRectGrid *)pvCpAddr)->uiDynamicFlag;
        }
        case OSD_CP_TYPE_LINEGRID:
        {
            return ((OsdLineGrid *)pvCpAddr)->uiDynamicFlag;
        }
        case OSD_CP_TYPE_LINE:
        {
            return ((OsdLine *)pvCpAddr)->uiDynamicFlag;
        }
        default:
        {
            return 0;
        }
    }
#endif
}

static OsdComponentNode *Hv_Mw_OsdComponentFindNode(OsdNodeHead *pstHead, OsdComponent stCp)
{
    HV_ASSERT_VALID_PTR_RET(pstHead, NULL);
    HV_ASSERT_TRUE_RET(stCp.enType != OSD_CP_TYPE_NONE, NULL);
    OsdComponentNode* pstNode = NULL;

    pstNode = pstHead->pstNode;
    while (pstNode)
    {
        if (CP_COMPARE(stCp, pstNode->stCp))
        {
            return pstNode;
        }
        pstNode = pstNode->pstNext;
    }
    return NULL;
}

OsdComponentNode *Hv_Mw_OsdComponentGetNode(OsdComponent stCp)
{
    OsdWindow *pstWindow = Hv_Mw_OsdComponentGetWindow(stCp);
    HV_ASSERT_VALID_PTR_RET(pstWindow, NULL);
    OsdComponentNode *pstNode = NULL;

    OSD_LOG_VERBOSE("Find %s's Node", CP_NAME(stCp));
    if (CP_IS_FONT(stCp))
    {
        pstNode = Hv_Mw_OsdComponentFindNode(&pstWindow->stFontShowListHead, stCp);
    }
    else
    {
        pstNode = Hv_Mw_OsdComponentFindNode(&pstWindow->stCpShowListHead, stCp);
    }

    return pstNode;
}

VOID *Hv_Mw_OsdComponentGetDynamicProp(OsdComponent stCp)
{
    OsdComponentNode *pstNode = Hv_Mw_OsdComponentGetNode(stCp);

    OSD_LOG_VERBOSE("Get %s's dynamic prop data", CP_NAME(stCp));

    return pstNode ? pstNode->pvDynamicData : NULL;
}

static OsdComponentNode* Hv_Mw_OsdComponentCreateNode(OsdComponent stCp)
{
    OsdComponentNode* pstNode = NULL;
    UINT32 uiDynamicFlag = 0;

    OSD_LOG_VERBOSE("Create node for %s", CP_NAME(stCp));
    pstNode = (OsdComponentNode*)HV_MALLOC(sizeof(OsdComponentNode));
    HV_ASSERT_VALID_PTR_RET(pstNode, NULL);

    HV_MEMSET(pstNode, 0, sizeof(OsdComponentNode));
    pstNode->stCp = stCp;
    pstNode->pvCpData = (VOID*)Hv_Mw_OsdGetComponentPropAddr(stCp);
    pstNode->pstNext = NULL;

    uiDynamicFlag = Hv_Mw_OsdComponentGetDynamicFlag(stCp);
    if (uiDynamicFlag != 0)
    {
        OSD_LOG_VERBOSE("%s's dynamic flag = %d", CP_NAME(stCp), uiDynamicFlag);
        pstNode->pvDynamicData = Hv_Mw_OsdComponentCreateDynamicData(stCp);
        if (NULL == pstNode->pvDynamicData)
        {
            OSD_LOG_ERROR("Create dynamic data failed!");
        }
#ifdef HV_SCALER_DEBUG_VERSION
        else
        {
            Hv_Mw_OsdDebugDynamicData(stCp, HV_TRUE);
        }
#endif
    }
    else
    {
        pstNode->pvDynamicData = NULL;
    }

#if (HV_CONFIG_ON == HV_PROJECT_CONFIG_OSD_MIRROR)
    pstNode->bIsMirrored = HV_FALSE;
#endif
    return pstNode;
}

static VOID Hv_Mw_OsdComponentDestroyNode(OsdComponentNode* pstNode)
{
    HV_ASSERT_VALID_PTR_VOID(pstNode);

    OSD_LOG_DEBUG("Destroy %s's node", CP_NAME(pstNode->stCp));
    if (pstNode->pvDynamicData)
    {
        OSD_LOG_DEBUG("Free %s's dynamic data", CP_NAME(pstNode->stCp));
        HV_FREE(pstNode->pvDynamicData);
        pstNode->pvDynamicData = NULL;
#ifdef HV_SCALER_DEBUG_VERSION
        Hv_Mw_OsdDebugDynamicData(pstNode->stCp, HV_FALSE);
#endif
    }
    HV_FREE(pstNode);
}

static OsdComponentNode *Hv_Mw_OsdComponentCutNode(OsdNodeHead *pstHead, OsdComponent stCp)
{
    HV_ASSERT_VALID_PTR_RET(pstHead, NULL);
    HV_ASSERT_TRUE_RET(stCp.enType != OSD_CP_TYPE_NONE, NULL);
    OsdComponentNode* pstNode = NULL;
    OsdComponentNode* pstNodePrev = NULL;
    USHORT16 usCount = 0;

    pstNode = pstHead->pstNode;
    while (pstNode)
    {
        if (CP_COMPARE(stCp, pstNode->stCp))
        {
            if (pstNodePrev)
            {
                pstNodePrev->pstNext = pstNode->pstNext;
            }
            else
            {
                pstHead->pstNode = pstNode->pstNext;
            }

            if (pstHead->pstLastNode == pstNode)
            {
                pstHead->pstLastNode = pstNodePrev;
            }
            pstNode->pstNext = NULL;
            pstHead->usTotalNum--;
            if (usCount < pstHead->usUpdateNum)
            {
                pstHead->usUpdateNum--;
            }

            return pstNode;
        }
        pstNodePrev = pstNode;
        pstNode = pstNode->pstNext;
        usCount++;
    }
    return NULL;
}

static VOID Hv_Mw_OsdComponentAddNodeToHead(OsdNodeHead *pstHead, OsdComponentNode* pstNode)
{
    HV_ASSERT_VALID_PTR_VOID(pstHead);
    HV_ASSERT_VALID_PTR_VOID(pstNode);

    OSD_LOG_VERBOSE("Add %s to head", CP_NAME(pstNode->stCp));
    if (pstHead->pstNode)
    {
        pstNode->pstNext = pstHead->pstNode;
        pstHead->pstNode = pstNode;
        pstHead->usTotalNum++;
    }
    else
    {
        pstHead->pstNode = pstNode;
        pstHead->pstLastNode = pstNode;
        pstNode->pstNext = NULL;
        pstHead->usTotalNum = 1;
    }
}

static VOID Hv_Mw_OsdComponentAddNodeToTail(OsdNodeHead *pstHead, OsdComponentNode* pstNode)
{
    HV_ASSERT_VALID_PTR_VOID(pstHead);
    HV_ASSERT_VALID_PTR_VOID(pstNode);

    OSD_LOG_VERBOSE("Add %s to tail", CP_NAME(pstNode->stCp));
    if (pstHead->pstLastNode)
    {
        pstHead->pstLastNode->pstNext = pstNode;
        pstNode->pstNext = NULL;
        pstHead->pstLastNode = pstNode;
        pstHead->usTotalNum++;
    }
    else
    {
        pstHead->pstNode = pstNode;
        pstHead->pstLastNode = pstNode;
        pstNode->pstNext = NULL;
        pstHead->usTotalNum = 1;
    }
}

static VOID Hv_Mw_OsdComponentAppendNode(OsdWindow *pstWindow, OsdComponent stCp)
{
    HV_ASSERT_VALID_PTR_VOID(pstWindow);
    HV_ASSERT_TRUE_VOID(stCp.enType != OSD_CP_TYPE_NONE);
    OsdNodeHead* pstShowHead = NULL;
    OsdNodeHead* pstDeleteHead = NULL;
    OsdComponentNode* pstNewNode = NULL;

    if (CP_IS_FONT(stCp))
    {
        pstShowHead = &pstWindow->stFontShowListHead;
        pstDeleteHead = &pstWindow->stFontDeleteListHead;
    }
    else
    {
        pstShowHead = &pstWindow->stCpShowListHead;
        pstDeleteHead = &pstWindow->stDeleteListHead;
    }
    HV_ASSERT_VALID_PTR_VOID(pstShowHead);
    HV_ASSERT_VALID_PTR_VOID(pstDeleteHead);

    /* Cut CP node from DeleteList */
    pstNewNode = Hv_Mw_OsdComponentCutNode(pstDeleteHead, stCp);
    if (NULL == pstNewNode)
    {
        /* Cut CP node from ShowList , check exist */
        pstNewNode = Hv_Mw_OsdComponentCutNode(pstShowHead, stCp);
        if (NULL == pstNewNode)
        {
            pstNewNode = Hv_Mw_OsdComponentCreateNode(stCp);
            HV_ASSERT_VALID_PTR_VOID(pstNewNode);
#if (HV_PROJECT_CONFIG_OSD_FORCE_REDRAW_ALL == HV_CONFIG_OFF)
            if (CP_IS_FONT(stCp))
            {
                /* append font's Y, if new, need redraw */
                Hv_Mw_OsdRecordFontRes(pstWindow->ucPhyId, pstNewNode);
            }
#endif
        }
        else
        {
            OSD_LOG_VERBOSE("Cut %s from win-%d's ShowList", CP_NAME(stCp), pstWindow->ucId);
        }
    }
    else
    {
#if (HV_PROJECT_CONFIG_OSD_FORCE_REDRAW_ALL == HV_CONFIG_OFF)
        if (CP_IS_FONT(stCp))
        {
            /* append font's Y, if new, need redraw */
            Hv_Mw_OsdRecordFontRes(pstWindow->ucPhyId, pstNewNode);
        }
#endif
        OSD_LOG_VERBOSE("Cut %s from win-%d's DeleteList", CP_NAME(stCp), pstWindow->ucId);
    }

    Hv_Mw_OsdComponentAddNodeToHead(pstShowHead, pstNewNode);
    pstShowHead->usUpdateNum++;

    pstWindow->bModify = HV_TRUE;
    if (pstWindow->pstScene)
    {
        pstWindow->pstScene->bModify = HV_TRUE;
    }
}

static VOID Hv_Mw_OsdComponentDeleteNode(OsdWindow *pstWindow, OsdComponent stCp)
{
    HV_ASSERT_VALID_PTR_VOID(pstWindow);
    HV_ASSERT_TRUE_VOID(stCp.enType != OSD_CP_TYPE_NONE);
    OsdComponentNode* pstNode = NULL;

    /* Cut CP node from ShowList */
    if (CP_IS_FONT(stCp))
    {
        pstNode = Hv_Mw_OsdComponentCutNode(&pstWindow->stFontShowListHead, stCp);
        if (NULL == pstNode)
        {
            return;
        }
        OSD_LOG_VERBOSE("Move %s to win-%d's FontDeleteList", CP_NAME(stCp), pstWindow->ucId);
        Hv_Mw_OsdComponentAddNodeToTail(&pstWindow->stFontDeleteListHead, pstNode);

#if (HV_PROJECT_CONFIG_OSD_FORCE_REDRAW_ALL == HV_CONFIG_OFF)
        /* Delete font's Y */
        Hv_Mw_OsdDeleteFontRes(pstWindow->ucPhyId, pstNode);
#endif
    }
    else
    {
        pstNode = Hv_Mw_OsdComponentCutNode(&pstWindow->stCpShowListHead, stCp);
        if (NULL == pstNode)
        {
            return;
        }
        OSD_LOG_VERBOSE("Move %s to win-%d's DeleteList", CP_NAME(stCp), pstWindow->ucId);
        Hv_Mw_OsdComponentAddNodeToTail(&pstWindow->stDeleteListHead, pstNode);
    }

    pstWindow->bModify = HV_TRUE;
    if (pstWindow->pstScene)
    {
        pstWindow->pstScene->bModify = HV_TRUE;
    }
}

VOID Hv_Mw_OsdDynamicAsciiStringUpdate(OsdAsciiStringId enIndex, UCHAR8* pucString, UCHAR8 ucStringLength)
{
    HV_ASSERT_VALID_PTR_VOID(pucString);

    const OsdAsciiString *pstAsciiStringProp = OSD_GET_ASCII_STRING_PROP(enIndex);
    HV_ASSERT_VALID_PTR_VOID(pstAsciiStringProp);
    OsdAsciiString *pstAsciiStringDynamic = NULL;
    UINT32 uiDynamicFlag = 0;
    UCHAR8 ucLen = MIN(ucStringLength, OSD_ASCII_STRING_LENGTH_MAX);
    OsdComponent stCp = {0};

    Hv_Mw_OsdComponentShow(COMPONENT(OSD_CP_TYPE_ASCII_STRING, enIndex));
    stCp.enType = OSD_CP_TYPE_ASCII_STRING;
    stCp.usIndex = enIndex;
    pstAsciiStringDynamic = Hv_Mw_OsdComponentGetDynamicProp(stCp);
    HV_ASSERT_VALID_PTR_VOID(pstAsciiStringDynamic);

    uiDynamicFlag = pstAsciiStringProp->uiDynamicFlag;

    if (uiDynamicFlag & DYN_RESOURCE)
    {
        for (UCHAR8 ucCount = 0; ucCount < ucLen; ucCount++)
        {
            pstAsciiStringDynamic->ausString[ucCount] = (USHORT16)(pucString[ucCount]);
        }
        pstAsciiStringDynamic->ucStringLength = ucLen;
        OSD_LOG_DEBUG("Custom string(%d): '%s' len=%d", enIndex, pucString, ucLen);

        uiDynamicFlag &= ~DYN_RESOURCE;
        if (uiDynamicFlag == 0)
        {
            return;
        }
    }
}

VOID Hv_Mw_OsdAsciiStringComponentShow(USHORT16 usId, const CHAR8 *fmt, ...)
{
    UCHAR8 aucAsciiString[OSD_ASCII_STRING_LENGTH_MAX] = {0};
    UCHAR8 ucAsciiStringLen = 0;

    va_list va;
    va_start(va, fmt);
    ucAsciiStringLen = Hv_Vos_vsnprintf(aucAsciiString, OSD_ASCII_STRING_LENGTH_MAX, fmt, va);
    va_end(va);

    Hv_Mw_OsdDynamicAsciiStringUpdate(usId, aucAsciiString, ucAsciiStringLen);
    OSD_LOG_INFO("Ascii string(%d): '%s' len=%d", usId, aucAsciiString, ucAsciiStringLen);
}

static VOID Hv_Mw_OsdDynamicComponentUpdate(OsdComponent stCp, UserdataId enUserdata)
{
    HV_ASSERT_TRUE_VOID(stCp.enType < OSD_CP_TYPE_MAX);
    VOID *pstCpProp = Hv_Mw_OsdComponentGetDynamicProp(stCp);
    if (NULL == pstCpProp)
    {
        return;
    }

    if (E_USERDATA_NONE != enUserdata)
    {
        UserdataProp stProp = { 0 };
        Hv_App_GetOsdUserdataProp(enUserdata, &stProp);

        /* Update by userdata */
        static const VOID (*s_apfUpdateDynamicCpByUserdataFuncTbl[OSD_CP_TYPE_MAX])(VOID *, USHORT16, UserdataProp*) = {
            [OSD_CP_TYPE_NONE] =            NULL,
            [OSD_CP_TYPE_TEXT] =            Hv_App_Cb_OsdUpdateTextByUserdata,
            [OSD_CP_TYPE_TEXT_LIST] =       Hv_App_Cb_OsdUpdateTextListByUserdata,
            [OSD_CP_TYPE_NUMBER] =          Hv_App_Cb_OsdUpdateNumberByUserdata,
            [OSD_CP_TYPE_INTACT_NUMBER] =   NULL, // TODO
            [OSD_CP_TYPE_ASCII_STRING] =    Hv_App_Cb_OsdUpdateAsciiStringByUserdata,
            [OSD_CP_TYPE_ICON] =            Hv_App_Cb_OsdUpdateIconByUserdata,
            [OSD_CP_TYPE_ICON_LIST] =       Hv_App_Cb_OsdUpdateIconListByUserdata,
            [OSD_CP_TYPE_BMP] =             Hv_App_Cb_OsdUpdateBmpByUserdata,
            [OSD_CP_TYPE_BMP_LIST] =        Hv_App_Cb_OsdUpdateBmpListByUserdata,
            [OSD_CP_TYPE_RECT] =            Hv_App_Cb_OsdUpdateRectByUserdata,
            [OSD_CP_TYPE_RECTGRID] =        Hv_App_Cb_OsdUpdateRectGridByUserdata,
            [OSD_CP_TYPE_LINEGRID] =        Hv_App_Cb_OsdUpdateLineGridByUserdata,
            [OSD_CP_TYPE_LINE] =            Hv_App_Cb_OsdUpdateLineByUserdata,
#if (HV_PROJECT_CONFIG_OSD_GRAPHIC == HV_CONFIG_ON)
            [OSD_CP_TYPE_GRAPHIC] =        NULL,   //Hv_App_Cb_OsdUpdateGraphicByUserdata,
#endif
        };

        if (NULL != s_apfUpdateDynamicCpByUserdataFuncTbl[stCp.enType])
        {
            OSD_LOG_VERBOSE("Update %s by userdata-%d", CP_NAME(stCp), enUserdata);
            s_apfUpdateDynamicCpByUserdataFuncTbl[stCp.enType](pstCpProp, stCp.usIndex, &stProp);
        }
    }

    /* Update custom */
    static const VOID (*s_apfUpdateDynamicCpCustomFuncTbl[OSD_CP_TYPE_MAX])(VOID *, USHORT16) = {
        [OSD_CP_TYPE_NONE] =            NULL,
        [OSD_CP_TYPE_TEXT] =            Hv_App_Cb_OsdUpdateTextCustom,
        [OSD_CP_TYPE_TEXT_LIST] =       Hv_App_Cb_OsdUpdateTextListCustom,
        [OSD_CP_TYPE_NUMBER] =          Hv_App_Cb_OsdUpdateNumberCustom,
        [OSD_CP_TYPE_INTACT_NUMBER] =   NULL, // TODO
        [OSD_CP_TYPE_ASCII_STRING] =    Hv_App_Cb_OsdUpdateAsciiStringCustom,
        [OSD_CP_TYPE_ICON] =            Hv_App_Cb_OsdUpdateIconCustom,
        [OSD_CP_TYPE_ICON_LIST] =       Hv_App_Cb_OsdUpdateIconListCustom,
        [OSD_CP_TYPE_BMP] =             Hv_App_Cb_OsdUpdateBmpCustom,
        [OSD_CP_TYPE_BMP_LIST] =        Hv_App_Cb_OsdUpdateBmpListCustom,
        [OSD_CP_TYPE_RECT] =            Hv_App_Cb_OsdUpdateRectCustom,
        [OSD_CP_TYPE_RECTGRID] =        Hv_App_Cb_OsdUpdateRectGridCustom,
        [OSD_CP_TYPE_LINEGRID] =        Hv_App_Cb_OsdUpdateLineGridCustom,
        [OSD_CP_TYPE_LINE] =            Hv_App_Cb_OsdUpdateLineCustom,
#if (HV_PROJECT_CONFIG_OSD_GRAPHIC == HV_CONFIG_ON)
        [OSD_CP_TYPE_GRAPHIC] =         NULL,   //Hv_App_Cb_OsdUpdateGraphicCustom,
#endif
    };

    if (NULL != s_apfUpdateDynamicCpCustomFuncTbl[stCp.enType])
    {
        OSD_LOG_VERBOSE("Update %s custom", CP_NAME(stCp));
        s_apfUpdateDynamicCpCustomFuncTbl[stCp.enType](pstCpProp, stCp.usIndex);
    }
}

#if (HV_CONFIG_ON == HV_PROJECT_CONFIG_OSD_MIRROR)
static VOID Hv_Mw_OsdDynamicComponentMirrorUpdate(OsdComponent stCp, USHORT16 usItemX, USHORT16 usItemWidth)
{
    OsdComponentNode* pstNode = NULL;
    pstNode = Hv_Mw_OsdComponentGetNode(stCp);

    if (pstNode->bIsMirrored)
    {
        return;
    }

    OSD_LOG_DEBUG("Update CP-%d-%d Mirror", stCp.enType, stCp.usIndex);
    HV_ASSERT_TRUE_VOID(stCp.enType < OSD_CP_TYPE_MAX);

    static const VOID (*s_auiCpDynamicMirrorFunctionTbl[])(UINT32, USHORT16, USHORT16) = {
        [OSD_CP_TYPE_NONE] =            NULL,
        [OSD_CP_TYPE_TEXT] =            Hv_App_Cb_OsdDynamicTextMirrorUpdate,
        [OSD_CP_TYPE_TEXT_LIST] =       Hv_App_Cb_OsdDynamicTextListMirrorUpdate,
        [OSD_CP_TYPE_NUMBER] =          Hv_App_Cb_OsdDynamicNumberMirrorUpdate,
        [OSD_CP_TYPE_INTACT_NUMBER] =   NULL, // TODO
        [OSD_CP_TYPE_ASCII_STRING] =    Hv_App_Cb_OsdDynamicAsciiStringMirrorUpdate,
        [OSD_CP_TYPE_ICON] =            Hv_App_Cb_OsdDynamicIconMirrorUpdate,
        [OSD_CP_TYPE_ICON_LIST] =       Hv_App_Cb_OsdDynamicIconListMirrorUpdate,
        [OSD_CP_TYPE_BMP] =             Hv_App_Cb_OsdDynamicBmpMirrorUpdate,
        [OSD_CP_TYPE_BMP_LIST] =        Hv_App_Cb_OsdDynamicBmpListMirrorUpdate,
        [OSD_CP_TYPE_RECT] =            Hv_App_Cb_OsdDynamicRectMirrorUpdate,
        [OSD_CP_TYPE_RECTGRID] =        Hv_App_Cb_OsdDynamicRectGridMirrorUpdate,
        [OSD_CP_TYPE_LINEGRID] =        Hv_App_Cb_OsdDynamicLineGridMirrorUpdate,
        [OSD_CP_TYPE_LINE] =            Hv_App_Cb_OsdDynamicLineMirrorUpdate,
        [OSD_CP_TYPE_GRAPHIC] =         Hv_App_Cb_OsdDynamicGraphicMirrorUpdate,
    };

    if (NULL != s_auiCpDynamicMirrorFunctionTbl[stCp.enType])
    {
        s_auiCpDynamicMirrorFunctionTbl[stCp.enType](stCp.usIndex, usItemX, usItemWidth);
    }
    pstNode->bIsMirrored = HV_TRUE;
}
#endif

VOID Hv_Mw_OsdComponentUpdateByUserdata(OsdComponent stCp, UserdataId enUserdata)
{
    OsdWindow *pstWindow = Hv_Mw_OsdComponentGetWindow(stCp);
    HV_ASSERT_VALID_PTR_VOID(pstWindow);
    HV_ASSERT_VALID_PTR_VOID(pstWindow->pstScene);

    if (!pstWindow->pstScene->bVisable)
    {
        OSD_LOG_WARN("Can not update component %s, when %s is not visable",
                     CP_NAME(stCp), SCENE_NAME(pstWindow->pstScene->ucId));
        return;
    }

    OSD_LOG_VERBOSE("Show %s in Window-%d", CP_NAME(stCp), pstWindow->ucId);
    Hv_Mw_OsdComponentAppendNode(pstWindow, stCp);

    pstWindow->bVisable = HV_TRUE;

#if (HV_CONFIG_ON== HV_PROJECT_CONFIG_OSD_MIRROR)
    Hv_Mw_OsdDynamicComponentMirrorUpdate(stCp, 0, 0);
#endif

    Hv_Mw_OsdDynamicComponentUpdate(stCp, enUserdata);
}

#if (HV_CONFIG_ON == HV_PROJECT_CONFIG_OSD_MIRROR)
VOID Hv_Mw_OsdDynamicComponentWithItemUpdateShow(OsdComponent stCp, UserdataId enUserdata, USHORT16 usItemX, USHORT16 usItemWidth)
{
    Hv_Mw_OsdComponentShow(stCp);
    Hv_Mw_OsdDynamicComponentMirrorUpdate(stCp, usItemX, usItemWidth);
}
#endif

#if (HV_PROJECT_CONFIG_OSD_CUSTOM_GROUP_SUPPORT == HV_CONFIG_ON)
VOID Hv_Mw_OsdComponentShowCustomGroup(OsdComponent stCp)
{
    HV_ASSERT_TRUE_VOID(stCp.enType == OSD_CP_TYPE_CUSTOM_GROUP);
    OsdComponentNode *pstInnerNode = NULL;
    OsdCustomGroup *pstCG = NULL;
    OsdCustomGroupInfo stCGInfo = {0};
    UCHAR8 ucIndex = 0;

    pstCG = (OsdCustomGroup *)Hv_Mw_OsdGetComponentPropAddr(stCp);
    HV_ASSERT_VALID_PTR_VOID(pstCG);

    stCGInfo.usX = pstCG->usX;
    stCGInfo.usY = pstCG->usY;
    stCGInfo.usWidth = pstCG->usWidth;
    stCGInfo.usAlignType = pstCG->usAlignType;
    stCGInfo.ucComponentCount = pstCG->ucComponentCount;
    HV_MEMCPY(stCGInfo.aucGapWidth, pstCG->aucGapWidth, sizeof(pstCG->aucGapWidth));

    for (ucIndex = 0; ucIndex < pstCG->ucComponentCount; ++ucIndex)
    {
        Hv_Mw_OsdComponentShow(pstCG->astComponents[ucIndex]);
        pstInnerNode = Hv_Mw_OsdComponentGetNode(pstCG->astComponents[ucIndex]);
        stCGInfo.apComponents[ucIndex] = pstInnerNode;

        if (NULL == pstInnerNode)
        {
            stCGInfo.ucComponentCount = ucIndex;
            break;
        }

        pstInnerNode->bInCustomGroup = HV_TRUE;
    }

    Hv_Drv_OsdFont_PrepareCustomGroup(stCp, &stCGInfo);

    return;
}

VOID Hv_Mw_OsdComponentHideCustomGroup(OsdComponent stCp)
{
    HV_ASSERT_TRUE_VOID(stCp.enType == OSD_CP_TYPE_CUSTOM_GROUP);
    OsdCustomGroup *pstCG = (OsdCustomGroup *)Hv_Mw_OsdGetComponentPropAddr(stCp);
    HV_ASSERT_VALID_PTR_VOID(pstCG);

    for (UCHAR8 ucIndex = 0; ucIndex < pstCG->ucComponentCount; ++ucIndex)
    {
        Hv_Mw_OsdComponentHide(pstCG->astComponents[ucIndex]);
    }

    if (OSD_CP_TYPE_CUSTOM_GROUP == stCp.enType)
    {
        Hv_Drv_OsdFont_RemoveCustomGroup(stCp);
    }
}

static VOID Hv_Mw_OsdComponentUpdateStateColor(OsdComponent stCp, UCHAR8 ucState);
static VOID Hv_Mw_OsdCustomGroupUpdateStateColor(OsdComponent stCp, UCHAR8 ucState)
{
    OsdCustomGroup *pstCG = NULL;
    UCHAR8 ucIndex = 0;

    pstCG = (OsdCustomGroup *)Hv_Mw_OsdGetComponentPropAddr(stCp);
    HV_ASSERT_VALID_PTR_VOID(pstCG);

    for (ucIndex = 0; ucIndex < pstCG->ucComponentCount; ++ucIndex)
    {
        Hv_Mw_OsdComponentUpdateStateColor(pstCG->astComponents[ucIndex], ucState);
    }

    return;
}

VOID Hv_Mw_OsdCustomGroupSetPosition(OsdComponent stCp, USHORT16 usX, USHORT16 usY, BOOL bIsOffset)
{
    HV_ASSERT_TRUE_VOID(stCp.enType == OSD_CP_TYPE_CUSTOM_GROUP);
    OsdCustomGroup *pstCG = (OsdCustomGroup *)Hv_Mw_OsdGetComponentPropAddr(stCp);
    HV_ASSERT_VALID_PTR_VOID(pstCG);
    OsdCustomGroupInfo stCGInfo = {0};

    stCGInfo.usX = pstCG->usX;
    stCGInfo.usY = pstCG->usY;
    stCGInfo.usWidth = pstCG->usWidth;
    stCGInfo.usAlignType = pstCG->usAlignType;
    stCGInfo.ucComponentCount = pstCG->ucComponentCount;
    HV_MEMCPY(stCGInfo.aucGapWidth, pstCG->aucGapWidth, sizeof(pstCG->aucGapWidth));

    for (UCHAR8 ucId = 0; ucId < pstCG->ucComponentCount; ucId++)
    {
        stCGInfo.apComponents[ucId] = Hv_Mw_OsdComponentGetNode(pstCG->astComponents[ucId]);
        Hv_Mw_OsdComponentSetPosition(pstCG->astComponents[ucId], usX, usY, bIsOffset);
    }

    stCGInfo.usX = usX;
    stCGInfo.usY = usY;
    Hv_Drv_OsdFont_PrepareCustomGroup(stCp, &stCGInfo);
}
#endif

VOID Hv_Mw_OsdComponentShow(OsdComponent stCp)
{
#if (HV_PROJECT_CONFIG_OSD_CUSTOM_GROUP_SUPPORT == HV_CONFIG_ON)
    if (stCp.enType == OSD_CP_TYPE_CUSTOM_GROUP)
    {
        Hv_Mw_OsdComponentShowCustomGroup(stCp);
        return;
    }
#endif

    OsdWindow *pstWindow = Hv_Mw_OsdComponentGetWindow(stCp);

    HV_ASSERT_VALID_PTR_VOID(pstWindow);
    HV_ASSERT_VALID_PTR_VOID(pstWindow->pstScene);

    if (!pstWindow->pstScene->bVisable)
    {
        OSD_LOG_WARN("Can not show component %s, when %s is not visable",
                     CP_NAME(stCp), SCENE_NAME(pstWindow->pstScene->ucId));
        return;
    }

    OSD_LOG_VERBOSE("Show %s in Window-%d", CP_NAME(stCp), pstWindow->ucId);

    Hv_Mw_OsdComponentAppendNode(pstWindow, stCp);

    pstWindow->bVisable = HV_TRUE;

    Hv_Mw_OsdDynamicComponentUpdate(stCp, E_USERDATA_NONE);
}

VOID Hv_Mw_OsdComponentHide(OsdComponent stCp)
{
#if (HV_PROJECT_CONFIG_OSD_CUSTOM_GROUP_SUPPORT == HV_CONFIG_ON)
    if (stCp.enType == OSD_CP_TYPE_CUSTOM_GROUP)
    {
        Hv_Mw_OsdComponentHideCustomGroup(stCp);
        return;
    }
#endif

    OsdWindow *pstWindow = Hv_Mw_OsdComponentGetWindow(stCp);
    HV_ASSERT_VALID_PTR_VOID(pstWindow);

    OSD_LOG_VERBOSE("Hide %s in Window-%d", CP_NAME(stCp), pstWindow->ucId);
#if (HV_PROJECT_CONFIG_OSD_ROTATION == HV_CONFIG_ON)
    OsdComponentNode *pstNode = Hv_Mw_OsdComponentGetNode(stCp);
    if (NULL != pstNode)
    {
        Hv_Mw_OsdAppendComponentBox(pstNode);
    }
#endif

    Hv_Mw_OsdComponentDeleteNode(pstWindow, stCp);
}

static VOID Hv_Mw_OsdComponentUpdateStateColor(OsdComponent stCp, UCHAR8 ucState)
{
#if (HV_PROJECT_CONFIG_OSD_CUSTOM_GROUP_SUPPORT == HV_CONFIG_ON)
    if (OSD_CP_TYPE_CUSTOM_GROUP == stCp.enType)
    {
        Hv_Mw_OsdCustomGroupUpdateStateColor(stCp, ucState);
        return;
    }
#endif

    if ((Hv_Mw_OsdComponentGetDynamicFlag(stCp) & DYN_STATE_COLOR) == 0)
    {
        return;
    }

    VOID *pvDynCp = Hv_Mw_OsdComponentGetDynamicProp(stCp);
    if (NULL == pvDynCp)
    {
        return;
    }

    OSD_LOG_VERBOSE("Update %s's color by state-%s", CP_NAME(stCp), s_apcItemStateNameTbl[ucState]);
    switch (stCp.enType)
    {
        case OSD_CP_TYPE_TEXT:
        {
            ((OsdText *)pvDynCp)->ucColorIndex = Hv_Mw_OsdGetDynamicStateColor((UCHAR8)((OsdText *)pvDynCp)->uiDynamicIndex, ucState);
            break;
        }
        case OSD_CP_TYPE_TEXT_LIST:
        {
            ((OsdTextList *)pvDynCp)->ucColorIndex = Hv_Mw_OsdGetDynamicStateColor((UCHAR8)((OsdTextList *)pvDynCp)->uiDynamicIndex, ucState);
            break;
        }
        case OSD_CP_TYPE_NUMBER:
        {
            ((OsdNumber *)pvDynCp)->ucColorIndex = Hv_Mw_OsdGetDynamicStateColor((UCHAR8)((OsdNumber *)pvDynCp)->uiDynamicIndex, ucState);
            break;
        }
        case OSD_CP_TYPE_ASCII_STRING:
        {
            ((OsdAsciiString *)pvDynCp)->ucColorIndex = Hv_Mw_OsdGetDynamicStateColor((UCHAR8)((OsdAsciiString *)pvDynCp)->uiDynamicIndex, ucState);
            break;
        }
        case OSD_CP_TYPE_ICON:
        {
            ((OsdIcon *)pvDynCp)->ucColorIndex = Hv_Mw_OsdGetDynamicStateColor((UCHAR8)((OsdIcon *)pvDynCp)->uiDynamicIndex, ucState);
            break;
        }
        case OSD_CP_TYPE_ICON_LIST:
        {
            ((OsdIconList *)pvDynCp)->ucColorIndex = Hv_Mw_OsdGetDynamicStateColor((UCHAR8)((OsdIconList *)pvDynCp)->uiDynamicIndex, ucState);
            break;
        }
        default:
        {
            break;
        }
    }
}

static const USHORT16 s_ausCpXTbl[] = {
    [OSD_CP_TYPE_NONE] =            0,
    [OSD_CP_TYPE_TEXT] =            offsetof(OsdText, usX),
    [OSD_CP_TYPE_TEXT_LIST] =       offsetof(OsdTextList, usX),
    [OSD_CP_TYPE_NUMBER] =          offsetof(OsdNumber, usX),
    [OSD_CP_TYPE_INTACT_NUMBER] =   0, // TODO
    [OSD_CP_TYPE_ASCII_STRING] =    offsetof(OsdAsciiString, usX),
    [OSD_CP_TYPE_ICON] =            offsetof(OsdIcon, usX),
    [OSD_CP_TYPE_ICON_LIST] =       offsetof(OsdIconList, usX),
    [OSD_CP_TYPE_BMP] =             offsetof(OsdBmp, usX),
    [OSD_CP_TYPE_BMP_LIST] =        offsetof(OsdBmpList, usX),
    [OSD_CP_TYPE_RECT] =            offsetof(OsdRect, usX),
    [OSD_CP_TYPE_RECTGRID] =        offsetof(OsdRectGrid, usX),
    [OSD_CP_TYPE_LINEGRID] =        offsetof(OsdLineGrid, usX),
    [OSD_CP_TYPE_LINE] =            offsetof(OsdLine, usX),
    [OSD_CP_TYPE_GRAPHIC] =         offsetof(OsdGraphic, usX),
};

static const USHORT16 s_ausCpYTbl[] = {
    [OSD_CP_TYPE_NONE] =            0,
    [OSD_CP_TYPE_TEXT] =            offsetof(OsdText, usY),
    [OSD_CP_TYPE_TEXT_LIST] =       offsetof(OsdTextList, usY),
    [OSD_CP_TYPE_NUMBER] =          offsetof(OsdNumber, usY),
    [OSD_CP_TYPE_INTACT_NUMBER] =   0, // TODO
    [OSD_CP_TYPE_ASCII_STRING] =    offsetof(OsdAsciiString, usY),
    [OSD_CP_TYPE_ICON] =            offsetof(OsdIcon, usY),
    [OSD_CP_TYPE_ICON_LIST] =       offsetof(OsdIconList, usY),
    [OSD_CP_TYPE_BMP] =             offsetof(OsdBmp, usY),
    [OSD_CP_TYPE_BMP_LIST] =        offsetof(OsdBmpList, usY),
    [OSD_CP_TYPE_RECT] =            offsetof(OsdRect, usY),
    [OSD_CP_TYPE_RECTGRID] =        offsetof(OsdRectGrid, usY),
    [OSD_CP_TYPE_LINEGRID] =        offsetof(OsdLineGrid, usY),
    [OSD_CP_TYPE_LINE] =            offsetof(OsdLine, usY),
    [OSD_CP_TYPE_GRAPHIC] =         offsetof(OsdGraphic, usY),
};

VOID Hv_Mw_OsdComponentGetPosition(USHORT16 usItemId, OsdComponent stCp, USHORT16 usX, USHORT16 usY)
{
    if (stCp.enType == OSD_CP_TYPE_NONE)
    {
        return;
    }

    OsdWindow *pstWindow = Hv_Mw_OsdComponentGetWindow(stCp);
    HV_ASSERT_VALID_PTR_VOID(pstWindow);
    OsdComponentNode *pstNode = NULL;
    VOID *pvCpProp = NULL;
    VOID *pvDynCp = NULL;

    if (CP_IS_FONT(stCp))
    {
        pstNode = Hv_Mw_OsdComponentFindNode(&pstWindow->stFontShowListHead, stCp);
    }
    else
    {
        pstNode = Hv_Mw_OsdComponentFindNode(&pstWindow->stCpShowListHead, stCp);
    }
    HV_ASSERT_VALID_PTR_VOID(pstNode);

    pvCpProp = pstNode->pvCpData;
    pvDynCp = pstNode->pvDynamicData;

    OsdItem* pstItem = OSD_GET_ITEM(usItemId);
    const OsdItemProp* pstItemProp = OSD_GET_ITEM_PROP(usItemId);

    if ((NULL != pstItem) && (NULL != pstItemProp) && (0 != pstItemProp->ucIsDynamicPos))
    {
        usX = pstItem->usX + *(USHORT16 *)((UINT32)pvCpProp + s_ausCpXTbl[stCp.enType]);
        usY = pstItem->usY + *(USHORT16 *)((UINT32)pvCpProp + s_ausCpYTbl[stCp.enType]);
    }
    else
    {
        if (NULL != pvDynCp)
        {
            usX = *(USHORT16 *)((UINT32)pvDynCp + s_ausCpXTbl[stCp.enType]);
            usY = *(USHORT16 *)((UINT32)pvDynCp + s_ausCpYTbl[stCp.enType]);
        }
        else
        {
            usX = *(USHORT16 *)((UINT32)pvCpProp + s_ausCpXTbl[stCp.enType]);
            usY = *(USHORT16 *)((UINT32)pvCpProp + s_ausCpYTbl[stCp.enType]);
        }
    }

    OSD_LOG_DEBUG("Get %s's pos=(%d, %d)", CP_NAME(stCp), usX, usY);
}

VOID Hv_Mw_OsdComponentSetPosition(OsdComponent stCp, USHORT16 usX, USHORT16 usY, BOOL bIsOffset)
{
    if (stCp.enType == OSD_CP_TYPE_NONE)
    {
        return;
    }

    OsdWindow *pstWindow = Hv_Mw_OsdComponentGetWindow(stCp);
    HV_ASSERT_VALID_PTR_VOID(pstWindow);
    OsdComponentNode *pstNode = NULL;
    VOID *pvCpProp = NULL;
    VOID *pvDynCp = NULL;

    if (CP_IS_FONT(stCp))
    {
#if (HV_PROJECT_CONFIG_OSD_CUSTOM_GROUP_SUPPORT == HV_CONFIG_ON)
        if (OSD_CP_TYPE_CUSTOM_GROUP == stCp.enType)
        {
            Hv_Mw_OsdCustomGroupSetPosition(stCp, usX, usY, bIsOffset);
            return;
        }
        else
#endif
        {
            pstNode = Hv_Mw_OsdComponentFindNode(&pstWindow->stFontShowListHead, stCp);
            HV_ASSERT_VALID_PTR_VOID(pstNode);
#if (HV_PROJECT_CONFIG_OSD_FORCE_REDRAW_ALL == HV_CONFIG_OFF)
        /* delete old positon */
            Hv_Mw_OsdDeleteFontRes(pstWindow->ucPhyId, pstNode);
#endif
        }
    }
    else
    {
        pstNode = Hv_Mw_OsdComponentFindNode(&pstWindow->stCpShowListHead, stCp);
        HV_ASSERT_VALID_PTR_VOID(pstNode);
    }
#if (HV_PROJECT_CONFIG_OSD_ROTATION == HV_CONFIG_ON)
    Hv_Mw_OsdAppendComponentBox(pstNode);
#endif

    pvCpProp = pstNode->pvCpData;
    pvDynCp = pstNode->pvDynamicData;
    OSD_LOG_DEBUG("Set %s's pos=(%d, %d)", CP_NAME(stCp), usX, usY);

    if (bIsOffset)
    {
        if (INVALID_PARAM_USHORT16 != usX)
        {
            *(USHORT16 *)((UINT32)pvDynCp + s_ausCpXTbl[stCp.enType]) = usX + *(USHORT16 *)((UINT32)pvCpProp + s_ausCpXTbl[stCp.enType]);
        }
        if (INVALID_PARAM_USHORT16 != usY)
        {
            *(USHORT16 *)((UINT32)pvDynCp + s_ausCpYTbl[stCp.enType]) = usY + *(USHORT16 *)((UINT32)pvCpProp + s_ausCpYTbl[stCp.enType]);
        }
    }
    else
    {
        if (INVALID_PARAM_USHORT16 != usX)
        {
            *(USHORT16 *)((UINT32)pvDynCp + s_ausCpXTbl[stCp.enType]) = usX;
        }
        if (INVALID_PARAM_USHORT16 != usY)
        {
            *(USHORT16 *)((UINT32)pvDynCp + s_ausCpYTbl[stCp.enType]) = usY;
        }
    }

#if (HV_PROJECT_CONFIG_OSD_FORCE_REDRAW_ALL == HV_CONFIG_OFF)
    if (CP_IS_FONT(stCp))
    {
        /* append new positon */
        Hv_Mw_OsdRecordFontRes(pstWindow->ucPhyId, pstNode);
    }
#endif
}

/* item不动，仅cursor移动 */
VOID Hv_Mw_OsdCursorSetPositon(UCHAR8 ucCursorId, USHORT16 usItemX, USHORT16 usItemY, UCHAR8 ucItemState)
{
    const OsdComponent* pstCpList = Hv_Mw_OsdGetCursorStyle(ucCursorId, ucItemState);
    if (NULL == pstCpList)
    {
        return;
    }

    OSD_LOG_DEBUG("Show Cursor-%d(%d)", ucCursorId, ucItemState);
    for (USHORT16 usCpCnt = 0; pstCpList[usCpCnt].enType != OSD_CP_TYPE_NONE; usCpCnt++)
    {
        if ( (OSD_CP_TYPE_RECT == pstCpList[usCpCnt].enType)
           || (OSD_CP_TYPE_RECTGRID == pstCpList[usCpCnt].enType)
           || (OSD_CP_TYPE_LINE == pstCpList[usCpCnt].enType)
           || (OSD_CP_TYPE_LINEGRID == pstCpList[usCpCnt].enType))
        {
            Hv_Mw_OsdComponentShow(pstCpList[usCpCnt]);
        }
        else
        {
            Hv_Mw_OsdComponentShow(pstCpList[usCpCnt]);
        }

        /* 更新cursor属性：pos,color */
        Hv_Mw_OsdComponentSetPosition(pstCpList[usCpCnt], usItemX, usItemY, HV_TRUE);
        Hv_Mw_OsdComponentUpdateStateColor(pstCpList[usCpCnt], ucItemState);
    }
}

static VOID Hv_Mw_OsdCursorShow(UCHAR8 ucCursorId, USHORT16 usItemX, USHORT16 usItemY, UCHAR8 ucItemState)
{
    const OsdComponent* pstCpList = Hv_Mw_OsdGetCursorStyle(ucCursorId, ucItemState);
    if (NULL == pstCpList)
    {
        return;
    }

    OSD_LOG_DEBUG("Show Cursor-%d(%d)", ucCursorId, ucItemState);
    for (USHORT16 usCpCnt = 0; pstCpList[usCpCnt].enType != OSD_CP_TYPE_NONE; usCpCnt++)
    {
        Hv_Mw_OsdComponentShow(pstCpList[usCpCnt]);

        /* 更新cursor属性：pos,color */
        Hv_Mw_OsdComponentSetPosition(pstCpList[usCpCnt], usItemX, usItemY, HV_TRUE);
        Hv_Mw_OsdComponentUpdateStateColor(pstCpList[usCpCnt], ucItemState);
    }
}

static VOID Hv_Mw_OsdCursorHide(UCHAR8 ucCursorId, UCHAR8 ucState)
{
    const OsdComponent* pstCpList = Hv_Mw_OsdGetCursorStyle(ucCursorId, ucState);
    if (NULL == pstCpList)
    {
        return;
    }

    OSD_LOG_DEBUG("Hide Cursor-%d(%s)", ucCursorId, s_apcItemStateNameTbl[ucState]);
    for (USHORT16 usCpCnt = 0; pstCpList[usCpCnt].enType != OSD_CP_TYPE_NONE; usCpCnt++)
    {
        Hv_Mw_OsdComponentHide(pstCpList[usCpCnt]);
    }
}

VOID Hv_Mw_OsdCursorShowByItem(USHORT16 usItemId)
{
    OsdItem* pstItem = OSD_GET_ITEM(usItemId);
    HV_ASSERT_VALID_PTR_VOID(pstItem);
    const OsdItemProp* pstItemProp = OSD_GET_ITEM_PROP(usItemId);
    HV_ASSERT_VALID_PTR_VOID(pstItemProp);

    if (pstItem->bVisable)
    {
        OSD_LOG_DEBUG("Show cursor %s, state=%s", ITEM_NAME(pstItem->usId), s_apcItemStateNameTbl[pstItem->ucState]);
        Hv_Mw_OsdCursorShow(pstItemProp->ucCursorStyleId, pstItem->usX, pstItem->usY, pstItem->ucState);
    }
}

VOID Hv_Mw_OsdCursorHideByItem(USHORT16 usItemId)
{
    OsdItem* pstItem = OSD_GET_ITEM(usItemId);
    HV_ASSERT_VALID_PTR_VOID(pstItem);
    const OsdItemProp* pstItemProp = OSD_GET_ITEM_PROP(usItemId);
    HV_ASSERT_VALID_PTR_VOID(pstItemProp);

    if (pstItem->bVisable)
    {
        OSD_LOG_DEBUG("Hide cursor %s, state=%s", ITEM_NAME(pstItem->usId), s_apcItemStateNameTbl[pstItem->ucState]);
        Hv_Mw_OsdCursorHide(pstItemProp->ucCursorStyleId, pstItem->ucState);
    }
}

static VOID Hv_Mw_OsdDynamicItemUpdate(USHORT16 usItemId, UserdataId enUserdata)
{
    UserdataProp* pstUserdata = NULL;
    OsdItem* pstItem = OSD_GET_ITEM(usItemId);
    HV_ASSERT_VALID_PTR_VOID(pstItem);

#if (HV_CONFIG_ON== HV_PROJECT_CONFIG_OSD_MIRROR)
    if (bRTLShow == HV_FALSE)
#endif
    {
        if (E_USERDATA_NONE == enUserdata)
        {
            return;
        }
    }

    if (pstItem->bVisable)
    {
        OSD_LOG_DEBUG("Update dynamic %s by userdata-%d", ITEM_NAME(pstItem->usId), enUserdata);
        const OsdComponent* pstCpList = Hv_Mw_OsdGetItemStyle(pstItem->usId, pstItem->ucState);
        if (pstCpList)
        {
            for (UCHAR8 ucCpCnt = 0; pstCpList[ucCpCnt].enType != OSD_CP_TYPE_NONE; ucCpCnt++)
            {
#if (HV_CONFIG_ON == HV_PROJECT_CONFIG_OSD_MIRROR)
                if (bRTLShow)
                {
                    Hv_Mw_OsdDynamicComponentWithItemUpdateShow(pstCpList[ucCpCnt], enUserdata, pstItem->usX, pstItem->usWidth);
                }
                else
                {
#endif
                    Hv_Mw_OsdComponentUpdateByUserdata(pstCpList[ucCpCnt], enUserdata);
#if (HV_CONFIG_ON == HV_PROJECT_CONFIG_OSD_MIRROR)
                }
#endif
            }
        }
    }
    else
    {
        OSD_LOG_ERROR("%s is not visable", ITEM_NAME(usItemId));
    }
}

static VOID Hv_Mw_OsdItemJustShow(USHORT16 usItemId)
{
    OsdItem* pstItem = OSD_GET_ITEM(usItemId);
    HV_ASSERT_VALID_PTR_VOID(pstItem);
    const OsdItemProp* pstItemProp = OSD_GET_ITEM_PROP(usItemId);
    HV_ASSERT_VALID_PTR_VOID(pstItemProp);

    if (!pstItem->bVisable)
    {
        OSD_LOG_DEBUG("Show %s, state=%s", ITEM_NAME(pstItem->usId), s_apcItemStateNameTbl[pstItem->ucState]);

        /* 显示item中当前状态对应的所有组件 */
        const OsdComponent* pstCpList = Hv_Mw_OsdGetItemStyle(usItemId, pstItem->ucState);
        if (pstCpList)
        {
#if (HV_CONFIG_ON == HV_PROJECT_CONFIG_OSD_MIRROR)
            if (bRTLShow)
            {
                OsdWindow *pstWindow = Hv_Mw_OsdComponentGetWindow(pstCpList[0]);
                pstItem->usX = pstWindow->usFontAreaWidth - pstItemProp->usX - pstItemProp->usWidth;
            }
            else
            {
                pstItem->usX = pstItemProp->usX;
            }
#endif
            for (USHORT16 usCpCnt = 0; pstCpList[usCpCnt].enType != OSD_CP_TYPE_NONE; usCpCnt++)
            {
                Hv_Mw_OsdComponentShow(pstCpList[usCpCnt]);
                if (pstItemProp->ucIsDynamicPos)
                {
                    Hv_Mw_OsdComponentSetPosition(pstCpList[usCpCnt], pstItem->usX, pstItem->usY, HV_TRUE);
                }
                /* 设置component其他动态属性：color... */
                Hv_Mw_OsdComponentUpdateStateColor(pstCpList[usCpCnt], pstItem->ucState);
            }
        }
        pstItem->bVisable = HV_TRUE;

        /* 显示当前状态对应的cursor组件 */
        Hv_Mw_OsdCursorShow(pstItemProp->ucCursorStyleId, pstItem->usX, pstItem->usY, pstItem->ucState);
    }
    else
    {
        OSD_LOG_DEBUG("%s is already visable", ITEM_NAME(usItemId));
    }

    /* 根据Userdata更新item内组件 */
    UserdataId enUserdata = OSD_GET_USERDATA(usItemId);
#if (HV_CONFIG_ON == HV_PROJECT_CONFIG_OSD_MIRROR)
    OSD_LOG_DEBUG("%s update by mirror", ITEM_NAME(usItemId));
    Hv_Mw_OsdDynamicItemUpdate(usItemId, enUserdata);
#else
    if (enUserdata != E_USERDATA_NONE)
    {
        Hv_Mw_OsdDynamicItemUpdate(usItemId, enUserdata);
    }
#endif
}

VOID Hv_Mw_OsdItemSetPosition(USHORT16 usItemId, USHORT16 usX, USHORT16 usY)
{
    OsdItem* pstItem = OSD_GET_ITEM(usItemId);
    const OsdItemProp* pstItemProp = OSD_GET_ITEM_PROP(usItemId);
    HV_ASSERT_VALID_PTR_VOID(pstItem);
    HV_ASSERT_VALID_PTR_VOID(pstItemProp);
    HV_ASSERT_TRUE_VOID(pstItemProp->ucIsDynamicPos);
    UCHAR8 ucScene = ITEM_GET_SCENEID(usItemId);
    OsdScene *pstScene = OSD_GET_SCENE(ucScene);
    HV_ASSERT_VALID_PTR_VOID(pstScene);
    BOOL bVisable = pstItem->bVisable && pstScene->bVisable;

    if (bVisable)
    {
        Hv_Mw_OsdItemHide(usItemId);
    }

    OSD_LOG_DEBUG("Set %s's pos=(%d, %d)", ITEM_NAME(usItemId), usX, usY);
    pstItem->usX = usX;
    pstItem->usY = usY;

    if (bVisable)
    {
        Hv_Mw_OsdItemJustShow(usItemId);
    }
}

VOID Hv_Mw_OsdItemMoveToPrevPosition(USHORT16 usItemId)
{
    OsdItem* pstPrevItem = OSD_GET_ITEM(PREV_ITEM(usItemId));
    HV_ASSERT_VALID_PTR_VOID(pstPrevItem);

    Hv_Mw_OsdItemSetPosition(usItemId, pstPrevItem->usX, pstPrevItem->usY);
}

VOID Hv_Mw_OsdItemMoveToNextPosition(USHORT16 usItemId)
{
    OsdItem* pstNextItem = OSD_GET_ITEM(PREV_ITEM(usItemId));
    HV_ASSERT_VALID_PTR_VOID(pstNextItem);

    Hv_Mw_OsdItemSetPosition(usItemId, pstNextItem->usX, pstNextItem->usY);
}

BOOL Hv_Mw_OsdItemIsShowing(USHORT16 usItemId)
{
    OsdItem* pstItem = OSD_GET_ITEM(usItemId);
    HV_ASSERT_VALID_PTR_RET(pstItem, HV_FALSE);

    return pstItem->bVisable;
}

VOID Hv_Mw_OsdItemShow(USHORT16 usItemId)
{
    OsdItem* pstItem = OSD_GET_ITEM(usItemId);
    if (NULL == pstItem)
    {
        return;
    }

#ifdef OSD_CHECK_DISABLE_HIDE_AND_ITEMLIST_AUTO_ALIGN
    if (Hv_App_Cb_ItemCheckDisableHide(usItemId))
    {
        if (pstItem->bVisable)
        {
            Hv_Mw_OsdItemHide(usItemId);
        }
        OSD_LOG_DEBUG("Check %s need disable hide, do not show", ITEM_NAME(usItemId));
        return;
    }
#endif

#ifdef OSD_CHECK_DISABLE_BY_EACH_ITEM
    if (Hv_App_Cb_ItemCheckDisable(usItemId))
    {
        if (OSD_ITEM_DISABLED != pstItem->ucState)
        {
            OSD_LOG_DEBUG("Check %s need disable", ITEM_NAME(usItemId));
            if (pstItem->bVisable)
            {
                Hv_Mw_OsdItemHide(usItemId);
            }
            pstItem->ucState = OSD_ITEM_DISABLED;
        }
    }
    else if (OSD_ITEM_DISABLED == pstItem->ucState)
    {
        OSD_LOG_DEBUG("Check %s no need disable, recover to normal", ITEM_NAME(usItemId));
        if (pstItem->bVisable)
        {
            Hv_Mw_OsdItemHide(usItemId);
        }
        pstItem->ucState = OSD_ITEM_NORMAL;
    }
#endif

    Hv_Mw_OsdItemJustShow(usItemId);
}

VOID Hv_Mw_OsdItemHide(USHORT16 usItemId)
{
    OsdItem* pstItem = OSD_GET_ITEM(usItemId);
    if (NULL == pstItem)
    {
        return;
    }
    const OsdItemProp* pstItemProp = OSD_GET_ITEM_PROP(usItemId);
    HV_ASSERT_VALID_PTR_VOID(pstItemProp);

    if (pstItem->bVisable)
    {
        OSD_LOG_DEBUG("Hide %s, state=%s", ITEM_NAME(pstItem->usId), s_apcItemStateNameTbl[pstItem->ucState]);
        /* 隐藏item中当前状态对应的所有组件 */
        const OsdComponent* pstCpList = Hv_Mw_OsdGetItemStyle(usItemId, pstItem->ucState);
        if (pstCpList)
        {
            for (USHORT16 usCpCnt = 0; pstCpList[usCpCnt].enType != OSD_CP_TYPE_NONE; usCpCnt++)
            {
                Hv_Mw_OsdComponentHide(pstCpList[usCpCnt]);
            }
        }
        pstItem->bVisable = HV_FALSE;

        /* 隐藏当前状态对应的cursor组件 */
        Hv_Mw_OsdCursorHide(pstItemProp->ucCursorStyleId, pstItem->ucState);
    }
    else
    {
        OSD_LOG_DEBUG("%s is already unvisable", ITEM_NAME(pstItem->usId));
    }
}

VOID Hv_Mw_OsdItemRefresh(USHORT16 usItemId)
{
    OsdItem* pstItem = OSD_GET_ITEM(usItemId);
    if (NULL != pstItem)
    {
        if (pstItem->bVisable)
        {
            OSD_LOG_DEBUG("Refresh %s", ITEM_NAME(usItemId));
            Hv_Mw_OsdItemHide(usItemId);
            Hv_Mw_OsdItemShow(usItemId);
        }
        else
        {
             OSD_LOG_DEBUG("%s not visable, can not refresh", ITEM_NAME(usItemId));
        }
    }
}

static VOID Hv_Mw_OsdItemUpdateState(OsdItem* pstItem, OsdItemState enState)
{
    HV_ASSERT_VALID_PTR_VOID(pstItem);
    HV_ASSERT_TRUE_VOID(enState < OSD_ITEM_STATE_NUM);
    BOOL bVisable = HV_FALSE;
    OsdScene *pstScene = OSD_GET_SCENE(pstItem->ucSceneId);
    HV_ASSERT_VALID_PTR_VOID(pstScene);

    if (pstItem->ucState == enState)
    {
        OSD_LOG_DEBUG("%s state = %s, do not need to update", ITEM_NAME(pstItem->usId), s_apcItemStateNameTbl[enState]);
        return;
    }

    OSD_LOG_DEBUG("Update %s state = %s to %s", ITEM_NAME(pstItem->usId), s_apcItemStateNameTbl[pstItem->ucState], s_apcItemStateNameTbl[enState]);
    bVisable = pstItem->bVisable && pstScene->bVisable;

    if (bVisable)
    {
        Hv_Mw_OsdItemHide(pstItem->usId);
    }

    pstItem->ucState = enState;

    if (bVisable)
    {
        Hv_Mw_OsdItemJustShow(pstItem->usId);
    }
}

VOID Hv_Mw_OsdItemShowWithState(USHORT16 usItemId, OsdItemState enState)
{
    OsdItem* pstItem = OSD_GET_ITEM(usItemId);
    if (NULL == pstItem)
    {
        return;
    }

    if (pstItem->bVisable)
    {
        Hv_Mw_OsdItemUpdateState(pstItem, enState);
    }
    else
    {
        pstItem->ucState = enState;
        Hv_Mw_OsdItemJustShow(usItemId);
    }
}

VOID Hv_Mw_OsdItemSetState(USHORT16 usItemId, OsdItemState enState)
{
    OsdItem* pstItem = OSD_GET_ITEM(usItemId);
    HV_ASSERT_VALID_PTR_VOID(pstItem);

    Hv_Mw_OsdItemUpdateState(pstItem, enState);
}

OsdItemState Hv_Mw_OsdItemGetState(USHORT16 usItemId)
{
    OsdItem* pstItem = OSD_GET_ITEM(usItemId);
    HV_ASSERT_VALID_PTR_RET(pstItem, OSD_ITEM_NORMAL);

    return (OsdItemState)(pstItem->ucState);
}

BOOL Hv_Mw_OsdItemGetEnable(USHORT16 usItemId)
{
#ifdef OSD_CHECK_DISABLE_BY_EACH_ITEM
#ifdef OSD_CHECK_DISABLE_HIDE_AND_ITEMLIST_AUTO_ALIGN
    return ((!Hv_App_Cb_ItemCheckDisable(usItemId)) && (!Hv_App_Cb_ItemCheckDisableHide(usItemId)));
#else
    return (!Hv_App_Cb_ItemCheckDisable(usItemId));
#endif
#else
    OsdItem* pstItem = OSD_GET_ITEM(usItemId);
    HV_ASSERT_VALID_PTR_RET(pstItem, HV_FALSE);

    return (pstItem->ucState != OSD_ITEM_DISABLED);
#endif
}

static USHORT16 Hv_Mw_OsdItemGetFirstItem(USHORT16 usItem);
static USHORT16 Hv_Mw_OsdItemGetLastItem(USHORT16 usItem);
USHORT16 Hv_Mw_OsdItemFindPrevEnable(USHORT16 usItemId)
{
    if (usItemId >= ITEM_ID_MAX)
    {
        return ITEM_NONE_ID;
    }
    USHORT16 usStartItem = PREV_ITEM(usItemId);
    USHORT16 usUntilId = Hv_Mw_OsdItemGetFirstItem(usItemId);
    USHORT16 usId = ITEM_NONE_ID;

    for (usId = usStartItem; usId >= usUntilId;)
    {
        if (Hv_Mw_OsdItemGetEnable(usId))
        {
            OSD_LOG_DEBUG("Found prev enable: %s", ITEM_NAME(usId));
            return usId;
        }

        usId = PREV_ITEM(usId);

        if ((usId >= usStartItem) || (usId == ITEM_NONE_ID))
        {
            return ITEM_NONE_ID;
        }
    }

    return ITEM_NONE_ID;
}

USHORT16 Hv_Mw_OsdItemFindNextEnable(USHORT16 usItemId)
{
    if (usItemId >= ITEM_ID_MAX)
    {
        return ITEM_NONE_ID;
    }
    USHORT16 usStartItem = NEXT_ITEM(usItemId);
    USHORT16 usUntilId = Hv_Mw_OsdItemGetLastItem(usItemId);
    USHORT16 usId = ITEM_NONE_ID;

    for (usId = usStartItem; usId <= usUntilId;)
    {
        if (Hv_Mw_OsdItemGetEnable(usId))
        {
            OSD_LOG_DEBUG("Found next enable: %s", ITEM_NAME(usId));
            return usId;
        }

        usId = NEXT_ITEM(usId);

        if ((usId <= usStartItem) || (usId == ITEM_NONE_ID))
        {
            return ITEM_NONE_ID;
        }
    }

    return ITEM_NONE_ID;
}

VOID Hv_Mw_OsdItemSetEnable(USHORT16 usItemId, BOOL bEnable)
{
    OsdItem* pstItem = OSD_GET_ITEM(usItemId);
    HV_ASSERT_VALID_PTR_VOID(pstItem);

    if (bEnable && (pstItem->ucState == OSD_ITEM_DISABLED))
    {
        OSD_LOG_DEBUG("Enable %s", ITEM_NAME(usItemId));
        Hv_Mw_OsdItemUpdateState(pstItem, OSD_ITEM_NORMAL);
    }
    else if ((!bEnable) && (pstItem->ucState != OSD_ITEM_DISABLED))
    {
        OSD_LOG_DEBUG("Disable %s", ITEM_NAME(usItemId));
        Hv_Mw_OsdItemUpdateState(pstItem, OSD_ITEM_DISABLED);
    }
}

VOID Hv_Mw_OsdItemEnableList(USHORT16 usItem, BOOL bIncludeSelf, BOOL bEnable)
{
    USHORT16 usStartItem = Hv_Mw_OsdItemGetStartIdInPage(usItem);
    USHORT16 usId = usStartItem;
    USHORT16 usCount = 0;

    OSD_LOG_DEBUG(">>>Set disable(%d) ItemList from %s(%d)", bEnable, ITEM_NAME(usId), usId);
    for (usCount = 0; usCount < s_stOsdOpsData.usItemNumInPage; usCount++)
    {
        if (usId == usItem)
        {
            if (!bIncludeSelf)
            {
                usId = NEXT_ITEM(usId);
                continue;
            }
        }
        Hv_Mw_OsdItemSetEnable(usId, bEnable);
        usId = NEXT_ITEM(usId);
        if ((usId <= usStartItem) || (usId == ITEM_NONE_ID))
        {
            break;
        }
    }
    OSD_LOG_DEBUG("<<<Set disable(%d) ItemList done, num = %d", bEnable, usCount);
}

static USHORT16 Hv_Mw_OsdItemGetFirstItem(USHORT16 usItem)
{
    HV_ASSERT_TRUE_RET((usItem < ITEM_ID_MAX), ITEM_NONE_ID);
    USHORT16 usFirstItem = usItem;

    while (PREV_ITEM(usFirstItem) < usFirstItem)
    {
        usFirstItem = PREV_ITEM(usFirstItem);
    }

    OSD_LOG_DEBUG("First item = %s", ITEM_NAME(usFirstItem));
    return usFirstItem;
}

static USHORT16 Hv_Mw_OsdItemGetLastItem(USHORT16 usItem)
{
    HV_ASSERT_TRUE_RET((usItem < ITEM_ID_MAX), ITEM_NONE_ID);
    USHORT16 usLastItem = usItem;

    while (NEXT_ITEM(usLastItem) > usLastItem)
    {
        usLastItem = NEXT_ITEM(usLastItem);
    }

    OSD_LOG_DEBUG("Last item = %s", ITEM_NAME(usLastItem));
    return usLastItem;
}

USHORT16 Hv_Mw_OsdItemGetShowingNum(USHORT16 usItemId)
{
    if (usItemId >= ITEM_ID_MAX)
    {
        return 0;
    }

    USHORT16 usFirstItem = Hv_Mw_OsdItemGetFirstItem(usItemId);
    USHORT16 usLastItem = Hv_Mw_OsdItemGetLastItem(usItemId);
    HV_ASSERT_TRUE_RET(usItemId >= usFirstItem, 0);
    HV_ASSERT_TRUE_RET(usLastItem >= usFirstItem, 0);

    USHORT16 usShowCnt = 0;
    for (USHORT16 usCurId = usFirstItem; usCurId <= usLastItem; usCurId++)
    {
        if (Hv_Mw_OsdItemIsShowing(usCurId))
        {
            usShowCnt++;
        }
    }

    return usShowCnt;
}

USHORT16 Hv_Mw_OsdItemGetListNum(USHORT16 usItemId)
{
    HV_ASSERT_TRUE_RET((usItemId < ITEM_ID_MAX), 0);

    USHORT16 usFirstItem = Hv_Mw_OsdItemGetFirstItem(usItemId);
    USHORT16 usLastItem = Hv_Mw_OsdItemGetLastItem(usItemId);

    if ((ITEM_NONE_ID == usFirstItem) || (ITEM_NONE_ID == usLastItem) || (usLastItem < usFirstItem))
    {
        return 1;
    }
    else
    {
        return (usLastItem - usFirstItem + 1);
    }
}

USHORT16 Hv_Mw_OsdItemGetChildNum(USHORT16 usItemId)
{
    return Hv_Mw_OsdItemGetListNum(CHILD_ITEM(usItemId));
}

USHORT16 Hv_Mw_OsdItemGetLevel(USHORT16 usItem)
{
    HV_ASSERT_TRUE_RET((usItem < ITEM_ID_MAX), 0);
    USHORT16 usLevel = 0;
    USHORT16 usParentItem = PARENT_ITEM(usItem);
    while (ITEM_NONE_ID != usParentItem)
    {
        usLevel++;
        usParentItem = PARENT_ITEM(usParentItem);
    }
    OSD_LOG_DEBUG("%s, level = %d", ITEM_NAME(usItem), usLevel);
    return usLevel;
}

VOID Hv_Mw_OsdSetItemNumInPage(USHORT16 usNum)
{
    s_stOsdOpsData.usItemNumInPage = (usNum == 0) ? OSD_MENU_SHOW_ITEM_NUM_MAX : usNum;
}

USHORT16 Hv_Mw_OsdGetItemNumInPage(VOID)
{
    return s_stOsdOpsData.usItemNumInPage;
}

#ifdef OSD_CHECK_DISABLE_HIDE_AND_ITEMLIST_AUTO_ALIGN
static BOOL Hv_Mw_OsdItemIsAtSamePageAlign(USHORT16 usItem1, USHORT16 usItem2);
static USHORT16 Hv_Mw_OsdItemGetStartIdInPageAlign(USHORT16 usItemId);
static USHORT16 Hv_Mw_OsdItemGetEndIdInPageAlign(USHORT16 usItemId);
static VOID Hv_Mw_OsdItemShowListAlignWithOther(USHORT16 usItemId, USHORT16 usOther);
static VOID Hv_Mw_OsdItemHideListAlignWithOther(USHORT16 usItemId, USHORT16 usOther);
#endif

USHORT16 Hv_Mw_OsdItemGetStartIdInPage(USHORT16 usItem)
{
    if (usItem >= ITEM_ID_MAX)
    {
        return ITEM_NONE_ID;
    }
#ifdef OSD_CHECK_DISABLE_HIDE_AND_ITEMLIST_AUTO_ALIGN
    return Hv_Mw_OsdItemGetStartIdInPageAlign(usItem);
#else
    USHORT16 usStartItem = Hv_Mw_OsdItemGetFirstItem(usItem);

    HV_ASSERT_TRUE_RET((usItem >= usStartItem), ITEM_NONE_ID);
    while ((usItem - usStartItem + 1) > s_stOsdOpsData.usItemNumInPage)
    {
        usStartItem += s_stOsdOpsData.usItemNumInPage;
    }

    OSD_LOG_DEBUG("Start %s", ITEM_NAME(usStartItem));
    return usStartItem;
#endif
}

USHORT16 Hv_Mw_OsdItemGetEndIdInPage(USHORT16 usItem)
{
    if (usItem >= ITEM_ID_MAX)
    {
        return ITEM_NONE_ID;
    }
#ifdef OSD_CHECK_DISABLE_HIDE_AND_ITEMLIST_AUTO_ALIGN
    return Hv_Mw_OsdItemGetEndIdInPageAlign(usItem);
#else
    USHORT16 usPageStartItem = Hv_Mw_OsdItemGetStartIdInPage(usItem);
    USHORT16 usEndItem = Hv_Mw_OsdItemGetLastItem(usItem);
    HV_ASSERT_TRUE_RET((usPageStartItem <= usEndItem), ITEM_NONE_ID);

    if ((usEndItem - usPageStartItem + 1) > s_stOsdOpsData.usItemNumInPage)
    {
        usEndItem = usPageStartItem + s_stOsdOpsData.usItemNumInPage - 1;
    }

    OSD_LOG_DEBUG("End %s", ITEM_NAME(usEndItem));
    return usEndItem;
#endif
}

USHORT16 Hv_Mw_OsdItemGetBias(USHORT16 usItem)
{
    HV_ASSERT_TRUE_RET((usItem < ITEM_ID_MAX), 0);
    USHORT16 usStartItem = Hv_Mw_OsdItemGetFirstItem(usItem);
    HV_ASSERT_TRUE_RET((usItem >= usStartItem), 0);
    USHORT16 usBias = usItem - usStartItem;
    OSD_LOG_DEBUG("%s, bias = %d", ITEM_NAME(usItem), usBias);
    return usBias;
}

USHORT16 Hv_Mw_OsdItemGetUserdata(USHORT16 usItemId)
{
#ifdef OSD_ITEM_BIND_USERDATA_BY_TOOL
    const OsdItemProp *pstItemProp = OSD_GET_ITEM_PROP(usItemId);
    HV_ASSERT_VALID_PTR_RET(pstItemProp, E_USERDATA_NONE);
    return pstItemProp->usUserdata;
#else
    return Hv_App_GetUserdataByOsdItem(usItemId);
#endif
}

static BOOL Hv_Mw_OsdItemGetIsUserdataValue(USHORT16 usItemId)
{
#ifdef OSD_ITEM_BIND_USERDATA_BY_TOOL
    const OsdItemProp *pstItemProp = OSD_GET_ITEM_PROP(usItemId);
    HV_ASSERT_VALID_PTR_RET(pstItemProp, HV_FALSE);
    return pstItemProp->bIsValue;
#else
    return HV_TRUE;
#endif
}

USHORT16 Hv_Mw_OsdItemGetUserdataValue(USHORT16 usItemId)
{
#ifdef OSD_ITEM_BIND_USERDATA_BY_TOOL
    const OsdItemProp *pstItemProp = OSD_GET_ITEM_PROP(usItemId);
    HV_ASSERT_VALID_PTR_RET(pstItemProp, 0);
    return pstItemProp->bIsValue ? pstItemProp->usUserdataValue : 0;
#else
    return Hv_Mw_OsdItemGetBias(usItemId);
#endif
}

VOID Hv_Mw_OsdItemGetPage(USHORT16 usItem, UCHAR8 *pucCurPage, UCHAR8 *pucTotalPageNum)
{
#ifdef OSD_CHECK_DISABLE_HIDE_AND_ITEMLIST_AUTO_ALIGN
    Hv_Mw_OsdItemGetPageAlign(usItem, pucCurPage, pucTotalPageNum);
#else
    if (usItem >= ITEM_ID_MAX)
    {
        *pucCurPage = 0;
        *pucTotalPageNum = 0;
        return;
    }
    USHORT16 usFirstItem = Hv_Mw_OsdItemGetFirstItem(usItem);
    USHORT16 usLastItem = Hv_Mw_OsdItemGetLastItem(usItem);
    HV_ASSERT_TRUE_VOID(usItem >= usFirstItem);
    HV_ASSERT_TRUE_VOID(usLastItem >= usFirstItem);

    HV_ASSERT_TRUE_VOID(usItem >= usFirstItem);
    *pucCurPage = (UCHAR8)(((usItem - usFirstItem) / s_stOsdOpsData.usItemNumInPage) + 1);
    HV_ASSERT_TRUE_VOID(usLastItem >= usFirstItem);
    *pucTotalPageNum = (UCHAR8)(((usLastItem - usFirstItem) / s_stOsdOpsData.usItemNumInPage) + 1);
    OSD_LOG_DEBUG("%s at page (%d/%d)", ITEM_NAME(usItem), *pucCurPage, *pucTotalPageNum);
#endif
}

VOID Hv_Mw_OsdItemGetPageCheckEnable(USHORT16 usItem, UCHAR8 *pucCurPage, UCHAR8 *pucTotalPageNum)
{
    if (usItem >= ITEM_ID_MAX)
    {
        *pucCurPage = 0;
        *pucTotalPageNum = 0;
        return;
    }

    USHORT16 usFirstItem = Hv_Mw_OsdItemGetFirstItem(usItem);
    USHORT16 usLastItem = Hv_Mw_OsdItemGetLastItem(usItem);

    HV_ASSERT_TRUE_VOID(usItem >= usFirstItem);
    UCHAR8 ucCurPage = (UCHAR8)(((usItem - usFirstItem) / s_stOsdOpsData.usItemNumInPage) + 1);
    HV_ASSERT_TRUE_VOID(usLastItem >= usFirstItem);
    UCHAR8 ucTotalPageNum = (UCHAR8)(((usLastItem - usFirstItem) / s_stOsdOpsData.usItemNumInPage) + 1);

    for (USHORT16 usPageItem = usFirstItem; usPageItem <= usLastItem; usPageItem += s_stOsdOpsData.usItemNumInPage)
    {
        BOOL bHasEnabled = HV_FALSE;
        for (USHORT16 usId = usPageItem; (usId < usPageItem + s_stOsdOpsData.usItemNumInPage) && (usId <= usLastItem); usId++)
        {
            if (Hv_Mw_OsdItemGetEnable(usId))
            {
                bHasEnabled = HV_TRUE;
                break;
            }
        }

        if ((!bHasEnabled) && ((usItem < usPageItem) || (usItem >= usPageItem + s_stOsdOpsData.usItemNumInPage)))
        {
            ucTotalPageNum--;
            if (usItem >= usPageItem + s_stOsdOpsData.usItemNumInPage)
            {
                ucCurPage--;
            }
        }
    }

    *pucCurPage = ucCurPage;
    *pucTotalPageNum = ucTotalPageNum;

    OSD_LOG_DEBUG("%s at page (%d/%d)", ITEM_NAME(usItem), *pucCurPage, *pucTotalPageNum);
}

BOOL Hv_Mw_OsdItemIsAtSamePage(USHORT16 usItem1, USHORT16 usItem2)
{
#ifdef OSD_CHECK_DISABLE_HIDE_AND_ITEMLIST_AUTO_ALIGN
    return Hv_Mw_OsdItemIsAtSamePageAlign(usItem1, usItem2);
#else
    USHORT16 usFirstItem = Hv_Mw_OsdItemGetFirstItem(usItem1);
    if (usFirstItem != Hv_Mw_OsdItemGetFirstItem(usItem2))
    {
        return HV_FALSE;
    }

    HV_ASSERT_TRUE_RET(usItem1 >= usFirstItem, HV_FALSE);
    HV_ASSERT_TRUE_RET(usItem2 >= usFirstItem, HV_FALSE);
    USHORT16 usCurPage1 = ((usItem1 - usFirstItem) / s_stOsdOpsData.usItemNumInPage) + 1;
    USHORT16 usCurPage2 = ((usItem2 - usFirstItem) / s_stOsdOpsData.usItemNumInPage) + 1;

    return (usCurPage1 == usCurPage2);
#endif
}

VOID Hv_Mw_OsdItemShowList(USHORT16 usItem)
{
    if (usItem >= ITEM_ID_MAX)
    {
        return;
    }
#ifdef OSD_CHECK_DISABLE_HIDE_AND_ITEMLIST_AUTO_ALIGN
    Hv_Mw_OsdItemShowListAlign(usItem);
#else
    USHORT16 usStartItem = Hv_Mw_OsdItemGetStartIdInPage(usItem);
    USHORT16 usId = usStartItem;
    USHORT16 usCount = 0;

    OSD_LOG_DEBUG(">>>Show ItemList from %s(%d)", ITEM_NAME(usId), usId);
    for (usCount = 0; usCount < s_stOsdOpsData.usItemNumInPage; usCount++)
    {
        Hv_Mw_OsdItemShow(usId);
        usId = NEXT_ITEM(usId);
        if ((usId <= usStartItem) || (usId == ITEM_NONE_ID))
        {
            break;
        }
    }
    OSD_LOG_DEBUG("<<<Show ItemList done, num = %d", usCount + 1);
#endif
}

USHORT16 Hv_Mw_OsdItemShowListCheckEnable(USHORT16 usItem)
{
    if (usItem >= ITEM_ID_MAX)
    {
        return ITEM_NONE_ID;
    }
    USHORT16 usStartItem = Hv_Mw_OsdItemGetStartIdInPage(usItem);
    USHORT16 usId = usStartItem;
    USHORT16 usCount = 0;

    OSD_LOG_DEBUG("Show ItemList & check enable");
    for (usCount = 0; usCount < Hv_Mw_OsdItemGetListNum(usStartItem); usCount++)
    {
        if (Hv_Mw_OsdItemGetEnable(usId))
        {
            OSD_LOG_DEBUG("%s(%d) is enable", ITEM_NAME(usId), usId);
            break;
        }
        usId = NEXT_ITEM(usId);
        if ((usId <= usStartItem) || (usId == ITEM_NONE_ID))
        {
            return ITEM_NONE_ID;
        }
    }
    Hv_Mw_OsdItemShowList(usId);

    return usId;
}

VOID Hv_Mw_OsdItemHideListCheckEnable(USHORT16 usItem)
{
    if (usItem >= ITEM_ID_MAX)
    {
        return;
    }
    USHORT16 usStartItem = Hv_Mw_OsdItemGetStartIdInPage(usItem);
    USHORT16 usId = usStartItem;
    USHORT16 usCount = 0;

    OSD_LOG_DEBUG("Hide ItemList & check enable");
    for (usCount = 0; usCount < Hv_Mw_OsdItemGetListNum(usStartItem); usCount++)
    {
        if (Hv_Mw_OsdItemGetEnable(usId))
        {
            OSD_LOG_DEBUG("%s(%d) is enable", ITEM_NAME(usId), usId);
            break;
        }
        usId = NEXT_ITEM(usId);
        if ((usId <= usStartItem) || (usId == ITEM_NONE_ID))
        {
            return;
        }
    }
    Hv_Mw_OsdItemHideList(usId);
}

VOID Hv_Mw_OsdItemShowListWithSingleChild(USHORT16 usItem)
{
#ifdef OSD_CHECK_DISABLE_HIDE_AND_ITEMLIST_AUTO_ALIGN
    Hv_Mw_OsdItemShowListAlignWithOther(usItem, TYPE_CHILD);
#else
    USHORT16 usStartItem = Hv_Mw_OsdItemGetStartIdInPage(usItem);
    USHORT16 usId = usStartItem;
    USHORT16 usCount = 0;

    OSD_LOG_DEBUG(">>>UpdateShow ItemList with child from %s(%d)", ITEM_NAME(usId), usId);
    for (usCount = 0; usCount < s_stOsdOpsData.usItemNumInPage; usCount++)
    {
        Hv_Mw_OsdItemShow(usId);
        if (ITEM_NONE_ID != CHILD_ITEM(usId))
        {
            Hv_Mw_OsdItemShow(CHILD_ITEM(usId));
        }
        usId = NEXT_ITEM(usId);
        if ((usId <= usStartItem) || (usId == ITEM_NONE_ID))
        {
            break;
        }
    }
    OSD_LOG_DEBUG("<<<UpdateShow ItemList with child done, num = %d", usCount + 1);
#endif
}

VOID Hv_Mw_OsdItemShowListWithSingleValue(USHORT16 usItem)
{
#ifdef OSD_CHECK_DISABLE_HIDE_AND_ITEMLIST_AUTO_ALIGN
    Hv_Mw_OsdItemShowListAlignWithOther(usItem, TYPE_VALUE);
#else
    USHORT16 usStartItem = Hv_Mw_OsdItemGetStartIdInPage(usItem);
    USHORT16 usId = usStartItem;
    USHORT16 usCount = 0;

    OSD_LOG_DEBUG(">>>UpdateShow ItemList with value from %s(%d)", ITEM_NAME(usId), usId);
    for (usCount = 0; usCount < s_stOsdOpsData.usItemNumInPage; usCount++)
    {
        Hv_Mw_OsdItemShow(usId);
        if (ITEM_NONE_ID != VALUE_ITEM(usId))
        {
            Hv_Mw_OsdItemShow(VALUE_ITEM(usId));
        }
        usId = NEXT_ITEM(usId);
        if ((usId <= usStartItem) || (usId == ITEM_NONE_ID))
        {
            break;
        }
    }
    OSD_LOG_DEBUG("<<<UpdateShow ItemList with value done, num = %d", usCount + 1);
#endif
}

VOID Hv_Mw_OsdItemShowValueList(USHORT16 usItem)
{
#ifdef OSD_CHECK_DISABLE_HIDE_AND_ITEMLIST_AUTO_ALIGN
    Hv_Mw_OsdItemShowListAlign(VALUE_ITEM(usItem));
#else
    USHORT16 usStartItem = Hv_Mw_OsdItemGetStartIdInPage(usItem);
    USHORT16 usId = usStartItem;
    USHORT16 usCount = 0;

    OSD_LOG_DEBUG(">>>UpdateShow ItemValueList from %s(%d)", ITEM_NAME(usId), usId);
    for (usCount = 0; usCount < s_stOsdOpsData.usItemNumInPage; usCount++)
    {
        if (VALUE_ITEM(usId) == ITEM_NONE_ID)
        {
            usId = NEXT_ITEM(usId);
            if ((usId <= usStartItem) || (usId == ITEM_NONE_ID))
            {
                break;
            }
            continue;
        }
        Hv_Mw_OsdItemShow(VALUE_ITEM(usId));
        usId = NEXT_ITEM(usId);
        if ((usId <= usStartItem) || (usId == ITEM_NONE_ID))
        {
            break;
        }
    }
    OSD_LOG_DEBUG("<<<UpdateShow ItemValueList done");
#endif
}

VOID Hv_Mw_OsdItemRefreshList(USHORT16 usItem)
{
    if (usItem >= ITEM_ID_MAX)
    {
        return;
    }
    USHORT16 usStartItem = Hv_Mw_OsdItemGetFirstItem(usItem);
    USHORT16 usId = usStartItem;
    USHORT16 usCount = 0;

    Hv_Mw_OsdItemUpdateSelectList(usItem);

    OSD_LOG_DEBUG(">>>Refresh ItemList from %s(%d)", ITEM_NAME(usId), usId);
    for (usCount = 0; usCount < Hv_Mw_OsdItemGetListNum(usStartItem); usCount++)
    {
        Hv_Mw_OsdItemRefresh(usId);
        usId = NEXT_ITEM(usId);
        if ((usId <= usStartItem) || (usId == ITEM_NONE_ID))
        {
            break;
        }
    }
    OSD_LOG_DEBUG("<<<Refresh ItemList done, num = %d", usCount + 1);
}

VOID Hv_Mw_OsdItemHideList(USHORT16 usItem)
{
    if (usItem >= ITEM_ID_MAX)
    {
        return;
    }
#ifdef OSD_CHECK_DISABLE_HIDE_AND_ITEMLIST_AUTO_ALIGN
    Hv_Mw_OsdItemHideListAlign(usItem);
#else
    USHORT16 usStartItem = Hv_Mw_OsdItemGetStartIdInPage(usItem);
    USHORT16 usId = usStartItem;
    USHORT16 usCount = 0;

    OSD_LOG_DEBUG(">>>Hide ItemList from %s(%d)", ITEM_NAME(usId), usId);
    for (usCount = 0; usCount < s_stOsdOpsData.usItemNumInPage; usCount++)
    {
        Hv_Mw_OsdItemHide(usId);
        usId = NEXT_ITEM(usId);
        if ((usId <= usStartItem) || (usId == ITEM_NONE_ID))
        {
            break;
        }
    }
    OSD_LOG_DEBUG("<<<Hide ItemList done, num = %d", usCount + 1);
#endif
}

VOID Hv_Mw_OsdItemHideListAll(USHORT16 usItem)
{
    USHORT16 usStartItem = Hv_Mw_OsdItemGetFirstItem(usItem);
    USHORT16 usId = usStartItem;
    USHORT16 usCount = 0;

    OSD_LOG_DEBUG(">>>Hide ItemListAll from %s(%d)", ITEM_NAME(usId), usId);
    for (usCount = 0; usCount < Hv_Mw_OsdItemGetListNum(usStartItem); usCount++)
    {
        Hv_Mw_OsdItemHide(usId);
        usId = NEXT_ITEM(usId);
        if ((usId <= usStartItem) || (usId == ITEM_NONE_ID))
        {
            break;
        }
    }
    OSD_LOG_DEBUG("<<<Hide ItemListAll done, num = %d", usCount + 1);
}

VOID Hv_Mw_OsdItemHideListWithSingleChild(USHORT16 usItem)
{
#ifdef OSD_CHECK_DISABLE_HIDE_AND_ITEMLIST_AUTO_ALIGN
    Hv_Mw_OsdItemHideListAlignWithOther(usItem, TYPE_CHILD);
#else
    USHORT16 usStartItem = Hv_Mw_OsdItemGetStartIdInPage(usItem);
    USHORT16 usId = usStartItem;
    USHORT16 usCount = 0;

    OSD_LOG_DEBUG(">>>Hide ItemList with child from %s(%d)", ITEM_NAME(usId), usId);
    for (usCount = 0; usCount < s_stOsdOpsData.usItemNumInPage; usCount++)
    {
        Hv_Mw_OsdItemHide(usId);
        if (ITEM_NONE_ID != CHILD_ITEM(usId))
        {
            Hv_Mw_OsdItemHide(CHILD_ITEM(usId));
        }
        usId = NEXT_ITEM(usId);
        if ((usId <= usStartItem) || (usId == ITEM_NONE_ID))
        {
            break;
        }
    }
    OSD_LOG_DEBUG("<<<Hide ItemList with child done, num = %d", usCount + 1);
#endif
}

VOID Hv_Mw_OsdItemHideListWithSingleValue(USHORT16 usItem)
{
#ifdef OSD_CHECK_DISABLE_HIDE_AND_ITEMLIST_AUTO_ALIGN
    Hv_Mw_OsdItemHideListAlignWithOther(usItem, TYPE_VALUE);
#else
    USHORT16 usStartItem = Hv_Mw_OsdItemGetStartIdInPage(usItem);
    USHORT16 usId = usStartItem;
    USHORT16 usCount = 0;

    OSD_LOG_DEBUG(">>>Hide ItemList with value from %s(%d)", ITEM_NAME(usId), usId);
    for (usCount = 0; usCount < s_stOsdOpsData.usItemNumInPage; usCount++)
    {
        Hv_Mw_OsdItemHide(usId);
        if (ITEM_NONE_ID != VALUE_ITEM(usId))
        {
            Hv_Mw_OsdItemHide(VALUE_ITEM(usId));
        }
        usId = NEXT_ITEM(usId);
        if ((usId <= usStartItem) || (usId == ITEM_NONE_ID))
        {
            break;
        }
    }
    OSD_LOG_DEBUG("<<<Hide ItemList with value done, num = %d", usCount + 1);
#endif
}

VOID Hv_Mw_OsdItemHideValueList(USHORT16 usItem)
{
#ifdef OSD_CHECK_DISABLE_HIDE_AND_ITEMLIST_AUTO_ALIGN
    Hv_Mw_OsdItemHideListAlign(VALUE_ITEM(usItem));
#else
    USHORT16 usStartItem = Hv_Mw_OsdItemGetStartIdInPage(usItem);
    USHORT16 usId = usStartItem;
    USHORT16 usCount = 0;

    OSD_LOG_DEBUG(">>>Hide ItemValueList from %s(%d)", ITEM_NAME(usId), usId);
    for (usCount = 0; usCount < s_stOsdOpsData.usItemNumInPage; usCount++)
    {
        if (VALUE_ITEM(usId) == ITEM_NONE_ID)
        {
            usId = NEXT_ITEM(usId);
            continue;
        }
        Hv_Mw_OsdItemHide(VALUE_ITEM(usId));
        usId = NEXT_ITEM(usId);
        if ((usId <= usStartItem) || (usId == ITEM_NONE_ID))
        {
            break;
        }
    }
    OSD_LOG_DEBUG("<<<Hide ItemValueList done");
#endif
}

VOID Hv_Mw_OsdItemGetOriPosition(USHORT16 usItemId, USHORT16 *pusX, USHORT16 *pusY)
{
    const OsdItemProp *pstItemProp = OSD_GET_ITEM_PROP(usItemId);
    HV_ASSERT_VALID_PTR_VOID(pstItemProp);

    *pusX = pstItemProp->usX;
    *pusY = pstItemProp->usY;
}

#ifdef OSD_CHECK_DISABLE_HIDE_AND_ITEMLIST_AUTO_ALIGN
static BOOL Hv_Mw_OsdItemIsAtSamePageAlign(USHORT16 usItem1, USHORT16 usItem2)
{
    UCHAR8 ucCurPage1 = 0;
    UCHAR8 ucCurPage2 = 0;
    UCHAR8 ucTotalPage = 0;

    Hv_Mw_OsdItemGetPageAlign(usItem1, &ucCurPage1, &ucTotalPage);
    Hv_Mw_OsdItemGetPageAlign(usItem2, &ucCurPage2, &ucTotalPage);

    return (ucCurPage1 == ucCurPage2);
}

static USHORT16 Hv_Mw_OsdItemGetStartIdInPageAlign(USHORT16 usItemId)
{
    HV_ASSERT_TRUE_RET((usItemId < ITEM_ID_MAX), ITEM_NONE_ID);
    USHORT16 usStartItem = Hv_Mw_OsdItemGetFirstItem(usItemId);
    HV_ASSERT_TRUE_RET((usItemId >= usStartItem), ITEM_NONE_ID);
    USHORT16 usCurId = usStartItem;
    USHORT16 usItemShowCount = 0;

    while (usItemShowCount <= s_stOsdOpsData.usItemNumInPage)
    {
        if (!Hv_App_Cb_ItemCheckDisableHide(usCurId))
        {
            usItemShowCount++;
            if (0 == ((usItemShowCount - 1) % s_stOsdOpsData.usItemNumInPage))
            {
                usStartItem = usCurId;
                usItemShowCount = 1;
            }
        }
        usCurId = NEXT_ITEM(usCurId);

        if ((usCurId > usItemId) || (usCurId <= usStartItem))
        {
            break;
        }
    }
    OSD_LOG_VERBOSE("Start %s align", ITEM_NAME(usStartItem));
    return usStartItem;
}

static USHORT16 Hv_Mw_OsdItemGetEndIdInPageAlign(USHORT16 usItemId)
{
    if (usItemId >= ITEM_ID_MAX)
    {
        return ITEM_NONE_ID;
    }

    USHORT16 usPageStartItem = Hv_Mw_OsdItemGetStartIdInPageAlign(usItemId);
    USHORT16 usEndItem = Hv_Mw_OsdItemGetLastItem(usItemId);
    HV_ASSERT_TRUE_RET((usPageStartItem <= usEndItem), ITEM_NONE_ID);
    USHORT16 usCurId = usPageStartItem;
    USHORT16 usItemShowCount = 0;

    while (usItemShowCount < s_stOsdOpsData.usItemNumInPage)
    {
        if (!Hv_App_Cb_ItemCheckDisableHide(usCurId))
        {
            usItemShowCount++;
            usEndItem = usCurId;
        }
        usCurId = NEXT_ITEM(usCurId);

        if (usCurId <= usPageStartItem)
        {
            break;
        }
    }
    OSD_LOG_VERBOSE("End %s align", ITEM_NAME(usEndItem));
    return usEndItem;
}

VOID Hv_Mw_OsdItemGetPageAlign(USHORT16 usItemId, UCHAR8 *pucCurPage, UCHAR8 *pucTotalPageNum)
{
    if (usItemId >= ITEM_ID_MAX)
    {
        *pucCurPage = 0;
        *pucTotalPageNum = 0;
        return;
    }

    USHORT16 usFirstItem = Hv_Mw_OsdItemGetFirstItem(usItemId);
    USHORT16 usLastItem = Hv_Mw_OsdItemGetLastItem(usItemId);
    HV_ASSERT_TRUE_VOID(usItemId >= usFirstItem);
    HV_ASSERT_TRUE_VOID(usLastItem >= usFirstItem);

    USHORT16 usHideCnt = 0;
    USHORT16 usHideCntTotal = 0;
    for (USHORT16 usCurId = usFirstItem; usCurId < usLastItem; usCurId++)
    {
        if (Hv_App_Cb_ItemCheckDisableHide(usCurId))
        {
            if (usCurId < usItemId)
            {
                usHideCnt++;
            }
            usHideCntTotal++;
        }
    }

    HV_ASSERT_TRUE_VOID(usItemId >= (usFirstItem + usHideCnt));
    *pucCurPage = (UCHAR8)(((usItemId - usFirstItem - usHideCnt) / s_stOsdOpsData.usItemNumInPage) + 1);
    HV_ASSERT_TRUE_VOID(usLastItem >= (usFirstItem + usHideCntTotal));
    *pucTotalPageNum = (UCHAR8)(((usLastItem - usFirstItem - usHideCntTotal) / s_stOsdOpsData.usItemNumInPage) + 1);
    OSD_LOG_DEBUG("%s at page (%d/%d) align", ITEM_NAME(usItemId), *pucCurPage, *pucTotalPageNum);
}

static BOOL CheckHasItemPosition(VOID)
{
    return (!Hv_Comm_LinkedList_IsEmpty(&s_stOsdOpsData.stItemPosList));
}

static BOOL CheckHasItemOtherPosition(VOID)
{
    return (!Hv_Comm_LinkedList_IsEmpty(&s_stOsdOpsData.stItemPosList2));
}

static BOOL Hv_Mw_OsdItemIsDynamicPosition(USHORT16 usItemId)
{
    const OsdItemProp* pstItemProp = OSD_GET_ITEM_PROP(usItemId);
    return (pstItemProp != NULL) ? pstItemProp->ucIsDynamicPos : HV_FALSE;
}

static VOID PushItemPosition(USHORT16 usItemId, USHORT16 usOther)
{
    HV_ASSERT_TRUE_VOID(usItemId < ITEM_ID_MAX);

    OsdPositionNode *pstPosNode = (OsdPositionNode *)HV_MALLOC_OBJECT(OsdPositionNode);
    if (NULL == pstPosNode)
    {
        OSD_LOG_ERROR("Malloc OsdPositionNode fail!");
        return;
    }

    Hv_Comm_LinkedList_Init(&pstPosNode->stNode);
    Hv_Comm_LinkedList_AddTail(&s_stOsdOpsData.stItemPosList, &pstPosNode->stNode);

    Hv_Mw_OsdItemGetOriPosition(usItemId, &pstPosNode->usX, &pstPosNode->usY);
    OSD_LOG_VERBOSE("Push %s's position (%d, %d)", ITEM_NAME(usItemId), pstPosNode->usX, pstPosNode->usY);

    if (TYPE_CHILD == usOther)
    {
        usItemId = CHILD_ITEM(usItemId);
    }
    else if (TYPE_VALUE == usOther)
    {
        usItemId = VALUE_ITEM(usItemId);
    }
    else
    {
        return;
    }

    pstPosNode = (OsdPositionNode *)HV_MALLOC_OBJECT(OsdPositionNode);
    if (NULL == pstPosNode)
    {
        OSD_LOG_ERROR("Malloc OsdPositionNode fail!");
        return;
    }

    Hv_Comm_LinkedList_Init(&pstPosNode->stNode);
    Hv_Comm_LinkedList_AddTail(&s_stOsdOpsData.stItemPosList2, &pstPosNode->stNode);

    Hv_Mw_OsdItemGetOriPosition(usItemId, &pstPosNode->usX, &pstPosNode->usY);
    OSD_LOG_VERBOSE("Push %s's position (%d, %d)", ITEM_NAME(usItemId), pstPosNode->usX, pstPosNode->usY);
}

static VOID PopAndPushItemPosition(USHORT16 usItemId, USHORT16 usOther)
{
    HV_ASSERT_TRUE_VOID(usItemId < ITEM_ID_MAX);

    if (!Hv_Mw_OsdItemIsDynamicPosition(usItemId))
    {
        OSD_LOG_WARN("%s is not dynamic postion, do not move", ITEM_NAME(usItemId));
        return;
    }

    struct LinkedList *pstFirstNode = Hv_Comm_LinkedList_PopHead(&s_stOsdOpsData.stItemPosList);
    HV_ASSERT_VALID_PTR_VOID(pstFirstNode);
    OsdPositionNode *pstPosNode = Hv_Comm_LinkedList_Entry(pstFirstNode, OsdPositionNode, stNode);
    HV_ASSERT_VALID_PTR_VOID(pstPosNode);

    OSD_LOG_VERBOSE("Pop position (%d, %d) to %s", pstPosNode->usX, pstPosNode->usY, ITEM_NAME(usItemId));
    Hv_Mw_OsdItemSetPosition(usItemId, pstPosNode->usX, pstPosNode->usY);

    Hv_Comm_LinkedList_AddTail(&s_stOsdOpsData.stItemPosList, &pstPosNode->stNode);

    Hv_Mw_OsdItemGetOriPosition(usItemId, &pstPosNode->usX, &pstPosNode->usY);
    OSD_LOG_VERBOSE("Push %s's position (%d, %d)", ITEM_NAME(usItemId), pstPosNode->usX, pstPosNode->usY);

    if (TYPE_CHILD == usOther)
    {
        usItemId = CHILD_ITEM(usItemId);
    }
    else if (TYPE_VALUE == usOther)
    {
        usItemId = VALUE_ITEM(usItemId);
    }
    else
    {
        return;
    }

    if (!Hv_Mw_OsdItemIsDynamicPosition(usItemId))
    {
        OSD_LOG_WARN("%s is not dynamic postion, do not move", ITEM_NAME(usItemId));
        return;
    }

    if (!CheckHasItemOtherPosition())
    {
        return;
    }

    pstFirstNode = Hv_Comm_LinkedList_PopHead(&s_stOsdOpsData.stItemPosList2);
    HV_ASSERT_VALID_PTR_VOID(pstFirstNode);
    pstPosNode = Hv_Comm_LinkedList_Entry(pstFirstNode, OsdPositionNode, stNode);
    HV_ASSERT_VALID_PTR_VOID(pstPosNode);

    OSD_LOG_VERBOSE("Pop position (%d, %d) to %s", pstPosNode->usX, pstPosNode->usY, ITEM_NAME(usItemId));
    Hv_Mw_OsdItemSetPosition(usItemId, pstPosNode->usX, pstPosNode->usY);

    Hv_Comm_LinkedList_AddTail(&s_stOsdOpsData.stItemPosList2, &pstPosNode->stNode);

    Hv_Mw_OsdItemGetOriPosition(usItemId, &pstPosNode->usX, &pstPosNode->usY);
    OSD_LOG_VERBOSE("Push %s's position (%d, %d)", ITEM_NAME(usItemId), pstPosNode->usX, pstPosNode->usY);
}

static VOID ClearItemPositionRecord(VOID)
{
    struct LinkedList *pstNode = NULL;
    struct LinkedList *pstNext = NULL;
    OsdPositionNode *pstPosNode = NULL;

    OSD_LOG_VERBOSE("Clear item position list");
    Hv_Comm_LinkedList_ForEachSafe(&s_stOsdOpsData.stItemPosList, pstNext, pstNode)
    {
        pstPosNode = Hv_Comm_LinkedList_Entry(pstNode, OsdPositionNode, stNode);
        HV_ASSERT_VALID_PTR_VOID(pstPosNode);

        Hv_Comm_LinkedList_Del(&s_stOsdOpsData.stItemPosList, &pstPosNode->stNode);
        HV_FREE(pstPosNode);
    }

    Hv_Comm_LinkedList_ForEachSafe(&s_stOsdOpsData.stItemPosList2, pstNext, pstNode)
    {
        pstPosNode = Hv_Comm_LinkedList_Entry(pstNode, OsdPositionNode, stNode);
        HV_ASSERT_VALID_PTR_VOID(pstPosNode);

        Hv_Comm_LinkedList_Del(&s_stOsdOpsData.stItemPosList2, &pstPosNode->stNode);
        HV_FREE(pstPosNode);
    }
}

static VOID Hv_Mw_OsdItemShowListAlignWithOther(USHORT16 usItemId, USHORT16 usOther)
{
    if (usItemId >= ITEM_ID_MAX)
    {
        return;
    }

    USHORT16 usFirstItem = Hv_Mw_OsdItemGetFirstItem(usItemId);
    HV_ASSERT_TRUE_VOID(usFirstItem < ITEM_ID_MAX);
    USHORT16 usStartItem = Hv_Mw_OsdItemGetStartIdInPageAlign(usItemId);
    HV_ASSERT_TRUE_VOID(usStartItem < ITEM_ID_MAX);

    USHORT16 usCurId = usFirstItem;
    USHORT16 usItemShowCount = 0;

    OSD_LOG_DEBUG(">>>Show %s's ItemAlignList from %s(%d)", ITEM_NAME(usItemId), ITEM_NAME(usStartItem), usStartItem);
    while (usItemShowCount < s_stOsdOpsData.usItemNumInPage)
    {
        if (Hv_App_Cb_ItemCheckDisableHide(usCurId))
        {
            PushItemPosition(usCurId, usOther);
        }
        else
        {
            if (CheckHasItemPosition())
            {
                PopAndPushItemPosition(usCurId, usOther);
            }
            if (usCurId >= usStartItem)
            {
                Hv_Mw_OsdItemShow(usCurId);
                if (TYPE_CHILD == usOther)
                {
                    Hv_Mw_OsdItemShow(CHILD_ITEM(usCurId));
                }
                else if (TYPE_VALUE == usOther)
                {
                    Hv_Mw_OsdItemShow(VALUE_ITEM(usCurId));
                }
                usItemShowCount++;
            }
        }
        usCurId = NEXT_ITEM(usCurId);

        if (usCurId <= usFirstItem)
        {
            break;
        }
    }
    ClearItemPositionRecord();
    OSD_LOG_DEBUG("<<<Show ItemAlignList done, num = %d", usItemShowCount);
}

VOID Hv_Mw_OsdItemShowListAlign(USHORT16 usItemId)
{
    Hv_Mw_OsdItemShowListAlignWithOther(usItemId, TYPE_NONE);
}

static VOID Hv_Mw_OsdItemHideListAlignWithOther(USHORT16 usItemId, USHORT16 usOther)
{
    if (usItemId >= ITEM_ID_MAX)
    {
        return;
    }
    USHORT16 usStartItem = Hv_Mw_OsdItemGetStartIdInPageAlign(usItemId);
    HV_ASSERT_TRUE_VOID(usStartItem < ITEM_ID_MAX);

    USHORT16 usCurId = usStartItem;
    USHORT16 usItemShowCount = 0;

    OSD_LOG_DEBUG(">>>Hide %s's ItemAlignList from %s(%d)", ITEM_NAME(usItemId), ITEM_NAME(usStartItem), usStartItem);
    while (usItemShowCount < s_stOsdOpsData.usItemNumInPage)
    {
        if (!Hv_App_Cb_ItemCheckDisableHide(usCurId))
        {
            Hv_Mw_OsdItemHide(usCurId);
            if (TYPE_CHILD == usOther)
            {
                Hv_Mw_OsdItemHide(CHILD_ITEM(usCurId));
            }
            else if (TYPE_VALUE == usOther)
            {
                Hv_Mw_OsdItemHide(VALUE_ITEM(usCurId));
            }
            usItemShowCount++;
        }
        usCurId = NEXT_ITEM(usCurId);

        if (usCurId <= usStartItem)
        {
            break;
        }
    }
    OSD_LOG_DEBUG("<<<Hide ItemAlignList done, num = %d", usItemShowCount);
}

VOID Hv_Mw_OsdItemHideListAlign(USHORT16 usItemId)
{
    Hv_Mw_OsdItemHideListAlignWithOther(usItemId, TYPE_NONE);
}
#endif

#ifndef OSD_CHECK_DISABLE_BY_EACH_ITEM
VOID Hv_Mw_ItemDisableByCondition(BOOL bCondition, const USHORT16 *pusItemArray, USHORT16 usItemNum)
{
    if (bCondition)
    {
        HV_ASSERT_VALID_PTR_VOID(pusItemArray);
        USHORT16 usCount = 0;
        for (USHORT16 usId = 0; usId < usItemNum; usId++)
        {
            usCount = 0;
            if (pusItemArray[usId] == INVALID_PARAM_USHORT16)
            {
                continue;
            }
            while(usCount < s_stOsdOpsData.usCurrentDisableNum)
            {
                if (s_stOsdOpsData.ausItemDisabledArray[usCount] == pusItemArray[usId])
                {
                    OSD_LOG_DEBUG("Found item-%d in ItemDisabledArray[%d]: ", pusItemArray[usId], usCount);
                    break;
                }
                usCount++;
            }
            /* Not found */
            if (usCount == s_stOsdOpsData.usCurrentDisableNum)
            {
                HV_ASSERT_TRUE_VOID(s_stOsdOpsData.usCurrentDisableNum < ARRAY_SIZE(s_stOsdOpsData.ausItemDisabledArray));
                Hv_Mw_OsdItemSetEnable(pusItemArray[usId], HV_FALSE);
                OSD_LOG_DEBUG("New disabled item-%d put in ItemDisabledArray[%d]: ", pusItemArray[usId], s_stOsdOpsData.usCurrentDisableNum);
                s_stOsdOpsData.ausItemDisabledArray[s_stOsdOpsData.usCurrentDisableNum] = pusItemArray[usId];
                s_stOsdOpsData.usCurrentDisableNum++;
            }
        }
    }
}

VOID Hv_Mw_ItemRecoverDisableArray(VOID)
{
    for (USHORT16 usId = 0; usId < s_stOsdOpsData.usCurrentDisableNum; usId++)
    {
        OSD_LOG_DEBUG("Enable ItemDisabledArray[%d]: item-%d", usId, s_stOsdOpsData.ausItemDisabledArray[usId]);
        Hv_Mw_OsdItemSetEnable(s_stOsdOpsData.ausItemDisabledArray[usId], HV_TRUE);
    }

    HV_MEMSET(s_stOsdOpsData.ausItemDisabledArray, INVALID_PARAM_USHORT16, sizeof(s_stOsdOpsData.ausItemDisabledArray));
    s_stOsdOpsData.usCurrentDisableNum = 0;
}
#endif

BOOL Hv_Mw_OsdItemCheckSelect(USHORT16 usItemId, BOOL bCheckState)
{
    if (bCheckState)
    {
        OsdItem* pstItem = OSD_GET_ITEM(usItemId);
        HV_ASSERT_VALID_PTR_RET(pstItem, HV_FALSE);
        return ((pstItem->ucState == OSD_ITEM_SELECT_FOCUS) || (pstItem->ucState == OSD_ITEM_SELECT));
    }
    else
    {
        USHORT16 usUserdataId = OSD_GET_USERDATA(usItemId);
        if (E_USERDATA_NONE != usUserdataId)
        {
            if (Hv_Mw_OsdItemGetIsUserdataValue(usItemId))
            {
                return (OSD_GET_USERDATA_VALUE(usItemId) == Hv_App_GetOsdDataValue(usUserdataId));
            }
        }
        return HV_FALSE;
    }
}

USHORT16 Hv_Mw_OsdItemFindSelected(USHORT16 usItemId, BOOL bCheckState)
{
    if (usItemId >= ITEM_ID_MAX)
    {
        return ITEM_NONE_ID;
    }

    OsdItem* pstItem = OSD_GET_ITEM(usItemId);
    HV_ASSERT_VALID_PTR_RET(pstItem, ITEM_NONE_ID);

    while (!Hv_Mw_OsdItemCheckSelect(pstItem->usId, bCheckState))
    {
        pstItem = OSD_GET_ITEM(NEXT_ITEM(pstItem->usId));
        HV_ASSERT_VALID_PTR_RET(pstItem, ITEM_NONE_ID);
        if (pstItem->usId == usItemId)
        {
            OSD_LOG_DEBUG("No item select");
            return ITEM_NONE_ID;
        }
    }

    return pstItem->usId;
}

VOID Hv_Mw_OsdItemUnSelect(USHORT16 usItemId)
{
    OsdItem* pstItem = OSD_GET_ITEM(usItemId);
    HV_ASSERT_VALID_PTR_VOID(pstItem);

    OSD_LOG_DEBUG("Unselect %s", ITEM_NAME(pstItem->usId));
    /* 还原select item的状态 */
    if (pstItem->ucState == OSD_ITEM_SELECT)
    {
        Hv_Mw_OsdItemUpdateState(pstItem, OSD_ITEM_NORMAL);
        OSD_LOG_DEBUG("Recover %s state = normal", ITEM_NAME(pstItem->usId));
    }
    else if (pstItem->ucState == OSD_ITEM_SELECT_FOCUS)
    {
        Hv_Mw_OsdItemUpdateState(pstItem, OSD_ITEM_FOCUS);
        OSD_LOG_DEBUG("Recover %s state = focus", ITEM_NAME(pstItem->usId));
    }
    else
    {
        OSD_LOG_DEBUG("%s is already unselected", ITEM_NAME(pstItem->usId));
    }
}

SHORT16 Hv_Mw_OsdItemSelect(USHORT16 usItemId)
{
    OsdItem* pstItem = OSD_GET_ITEM(usItemId);
    HV_ASSERT_VALID_PTR_RET(pstItem, 0);
    USHORT16 usSelectedItem = ITEM_NONE_ID;
    SHORT16 sValue = OSD_GET_USERDATA_VALUE(usItemId);

    if ((pstItem->ucState == OSD_ITEM_SELECT) || (pstItem->ucState == OSD_ITEM_SELECT_FOCUS))
    {
        OSD_LOG_DEBUG("%s is already selected, value = %d", ITEM_NAME(pstItem->usId), sValue);
        return sValue;
    }
    OSD_LOG_DEBUG("Select %s, value = %d", ITEM_NAME(pstItem->usId), sValue);

    /* 还原select item的状态 */
    usSelectedItem = Hv_Mw_OsdItemFindSelected(usItemId, HV_TRUE);
    if (usSelectedItem != ITEM_NONE_ID)
    {
        Hv_Mw_OsdItemUnSelect(usSelectedItem);
    }

    /* 更新当前item为select */
    if (pstItem->ucState == OSD_ITEM_FOCUS)
    {
        Hv_Mw_OsdItemUpdateState(pstItem, OSD_ITEM_SELECT_FOCUS);
        OSD_LOG_DEBUG("Update %s state = select&focus", ITEM_NAME(pstItem->usId));
    }
    else
    {
        Hv_Mw_OsdItemUpdateState(pstItem, OSD_ITEM_SELECT);
        OSD_LOG_DEBUG("Update %s state = select", ITEM_NAME(pstItem->usId));
    }

    return sValue;
}

USHORT16 Hv_Mw_OsdItemUpdateSelectList(USHORT16 usItemId)
{
    USHORT16 usUserdataId = OSD_GET_USERDATA(usItemId);
    if (E_USERDATA_NONE == usUserdataId)
    {
        return ITEM_NONE_ID;
    }

    USHORT16 usSelectItemId = ITEM_NONE_ID;
    USHORT16 usCount = 0;
    USHORT16 usStartItem = Hv_Mw_OsdItemGetFirstItem(usItemId);
    USHORT16 usId = usStartItem;

    for (usCount = 0; usCount < Hv_Mw_OsdItemGetListNum(usItemId); usCount++)
    {
        if (Hv_Mw_OsdItemGetIsUserdataValue(usId))
        {
            if (Hv_App_GetOsdDataValue(usUserdataId) == OSD_GET_USERDATA_VALUE(usId))
            {
                usSelectItemId = usId;
                break;
            }
        }
        usId = NEXT_ITEM(usId);
        if (usId == ITEM_NONE_ID)
        {
            break;
        }
    }

    if (usSelectItemId != ITEM_NONE_ID)
    {
        Hv_Mw_OsdItemSelect(usSelectItemId);
    }
    else
    {
        OSD_LOG_DEBUG("No item need select");
    }

    return usSelectItemId;
}

USHORT16 Hv_Mw_OsdItemShowSelectedList(USHORT16 usItem)
{
    if (usItem >= ITEM_ID_MAX)
    {
        return ITEM_NONE_ID;
    }
    USHORT16 usItemSelected = Hv_Mw_OsdItemFindSelected(usItem, HV_FALSE);
    if (ITEM_NONE_ID == usItemSelected)
    {
        usItemSelected = usItem;
    }

    Hv_Mw_OsdItemShowList(usItemSelected);
    return usItemSelected;
}

VOID Hv_Mw_OsdItemHideSelectedList(USHORT16 usItem)
{
    if (usItem >= ITEM_ID_MAX)
    {
        return;
    }
    USHORT16 usItemSelected = Hv_Mw_OsdItemFindSelected(usItem, HV_FALSE);
    if (ITEM_NONE_ID == usItemSelected)
    {
        usItemSelected = usItem;
    }

    Hv_Mw_OsdItemHideList(usItemSelected);
}

USHORT16 Hv_Mw_OsdItemGetFocusing(VOID)
{
    return s_stOsdOpsData.usFocusingItemId;
}

static VOID Hv_Mw_OsdItemChooseFocus(USHORT16 usItemId)
{
    s_stOsdOpsData.usChosenItemId = usItemId;
}

static VOID Hv_Mw_OsdItemFocusUpdate(OsdItem* pstItem)
{
    HV_ASSERT_VALID_PTR_VOID(pstItem);
    OSD_LOG_DEBUG("Focusing %s", ITEM_NAME(pstItem->usId));

    if (pstItem->ucState == OSD_ITEM_SELECT)
    {
        Hv_Mw_OsdItemUpdateState(pstItem, OSD_ITEM_SELECT_FOCUS);
    }
    else
    {
        Hv_Mw_OsdItemUpdateState(pstItem, OSD_ITEM_FOCUS);
    }
}

static VOID Hv_Mw_OsdItemUnFocusUpdate(OsdItem* pstItem, OsdMoveFocusingItem eWhichItem)
{
    HV_ASSERT_VALID_PTR_VOID(pstItem);
    OSD_LOG_DEBUG("Unfocus %s", ITEM_NAME(pstItem->usId));

    if ((pstItem->ucState == OSD_ITEM_FOCUS) || (pstItem->ucState == OSD_ITEM_SELECT_FOCUS))
    {
        if ((eWhichItem == FOCUSING_CHILD) || (eWhichItem == FOCUSING_CHILD_SELECT) || (eWhichItem == FOCUSING_VALUE))
        {
            Hv_Mw_OsdItemUpdateState(pstItem, OSD_ITEM_STAY);
        }
        else
        {
            if (pstItem->ucState == OSD_ITEM_SELECT_FOCUS)
            {
                Hv_Mw_OsdItemUpdateState(pstItem, OSD_ITEM_SELECT);
            }
            else
            {
                Hv_Mw_OsdItemUpdateState(pstItem, OSD_ITEM_NORMAL);
            }
        }
    }
    else
    {
        OSD_LOG_DEBUG("Item-%d state(%s) is not focus / select&focus", pstItem->usId, s_apcItemStateNameTbl[pstItem->ucState]);
    }
}

static OsdItem *Hv_Mw_OsdItemSearchNewFocus(OsdItem* pstCurItem, OsdMoveFocusingItem eWhichItem)
{
    HV_ASSERT_VALID_PTR_RET(pstCurItem, NULL);
    const OsdItemProp *pstItemProp = OSD_GET_ITEM_PROP(pstCurItem->usId);
    HV_ASSERT_VALID_PTR_RET(pstItemProp, NULL);
    USHORT16 usItemId = ITEM_NONE_ID;

    switch (eWhichItem)
    {
        case FOCUSING_NEXT:
        {
            usItemId = pstItemProp->usNextItemId;
            while (!Hv_Mw_OsdItemGetEnable(usItemId))
            {
                OSD_LOG_DEBUG("%s disabled, focusing move to next again.", ITEM_NAME(usItemId));
                usItemId = NEXT_ITEM(usItemId);

                if (usItemId == pstItemProp->usNextItemId)
                {
                    OSD_LOG_DEBUG("All item disabled, no focusing new.");
                    return NULL;
                }
            }
            break;
        }
        case FOCUSING_PREV:
        {
            usItemId = pstItemProp->usPrevItemId;
            while (!Hv_Mw_OsdItemGetEnable(usItemId))
            {
                OSD_LOG_DEBUG("%s disabled, focusing move to prev again.", ITEM_NAME(usItemId));
                usItemId = PREV_ITEM(usItemId);

                if (usItemId == pstItemProp->usPrevItemId)
                {
                    OSD_LOG_DEBUG("All item disabled, no focusing new.");
                    return NULL;
                }
            }
            break;
        }
        case FOCUSING_CHILD:
        {
            usItemId = pstItemProp->usChildItemId;
            while (!Hv_Mw_OsdItemGetEnable(usItemId))
            {
                OSD_LOG_DEBUG("%s disabled, focusing move to next again.", ITEM_NAME(usItemId));
                usItemId = NEXT_ITEM(usItemId);

                if (usItemId == pstItemProp->usChildItemId)
                {
                    OSD_LOG_DEBUG("All item disabled, no focusing new.");
                    return NULL;
                }
            }
            break;
        }
        case FOCUSING_CHILD_SELECT:
        {
            usItemId = Hv_Mw_OsdItemFindSelected(pstItemProp->usChildItemId, HV_FALSE);
            if (ITEM_NONE_ID == usItemId)
            {
                OSD_LOG_DEBUG("No child select, default focusing first item");
                USHORT16 usFirstItemId = Hv_Mw_OsdItemGetFirstItem(pstItemProp->usChildItemId);
                usItemId = usFirstItemId;
                while (!Hv_Mw_OsdItemGetEnable(usItemId))
                {
                    OSD_LOG_DEBUG("%s disabled, focusing move to next again.", ITEM_NAME(usItemId));
                    usItemId = NEXT_ITEM(usItemId);

                    if (usItemId == usFirstItemId)
                    {
                        OSD_LOG_DEBUG("All item disabled, no focusing new.");
                        return NULL;
                    }
                }
            }
            break;
        }
        case FOCUSING_VALUE:
        {
            usItemId = pstItemProp->usValueItemId;
            if (!Hv_Mw_OsdItemGetEnable(usItemId))
            {
                OSD_LOG_DEBUG("Value item disabled, no focusing new.");
                return NULL;
            }
            break;
        }
        case FOCUSING_PARENT:
        {
            usItemId = pstItemProp->usParentItemId;
            break;
        }
        default:
        {
            return NULL;
        }
    }

    return OSD_GET_ITEM(usItemId);
}

OsdItem *Hv_Mw_OsdItemSearchMoveFocus(USHORT16 usCurItemId, OsdMoveFocusingItem eWhichItem)
{
    OSD_LOG_DEBUG("CurFocusingItem: %s, Search Move(%d)", ITEM_NAME(usCurItemId), eWhichItem);
    OsdItem* pstCurFocus = OSD_GET_ITEM(usCurItemId);
    OsdItem* pstNewFocus = NULL;

    if (eWhichItem == FOCUSING_CHOSEN)
    {
        return pstCurFocus;
    }
    else
    {
        pstNewFocus = Hv_Mw_OsdItemSearchNewFocus(pstCurFocus, eWhichItem);
        return pstNewFocus;
    }
}

static USHORT16 Hv_Mw_OsdItemMoveFocus(OsdMoveFocusingItem eWhichItem)
{
    OSD_LOG_DEBUG("CurFocusingItem: %s, Move(%d)", ITEM_NAME(s_stOsdOpsData.usFocusingItemId), eWhichItem);
    OsdItem* pstCurFocus = OSD_GET_ITEM(s_stOsdOpsData.usFocusingItemId);
    OsdItem* pstNewFocus = NULL;

    if (eWhichItem == FOCUSING_CHOSEN)
    {
        pstNewFocus = OSD_GET_ITEM(s_stOsdOpsData.usChosenItemId);
    }
    else
    {
        pstNewFocus = Hv_Mw_OsdItemSearchNewFocus(pstCurFocus, eWhichItem);
    }

    if (NULL != pstNewFocus)
    {
        if (pstNewFocus != pstCurFocus)
        {
            s_stOsdOpsData.usFocusingItemId = pstNewFocus->usId;
            /* 还原当前focus item状态 */
            if (NULL != pstCurFocus)
            {
                Hv_Mw_OsdItemUnFocusUpdate(pstCurFocus, eWhichItem);
            }

            /* focus 新item */
            Hv_Mw_OsdItemFocusUpdate(pstNewFocus);
        }
        else
        {
            OSD_LOG_DEBUG("Focusing same %s, do not move.", ITEM_NAME(pstNewFocus->usId));
        }
    }
    else
    {
        OSD_LOG_DEBUG("No focusing new, do not move.");
    }

    return (NULL != pstNewFocus) ? pstNewFocus->usId : ITEM_NONE_ID;
}

VOID Hv_Mw_OsdItemFocusClear(VOID)
{
    OSD_LOG_DEBUG("Focusing clear.");
    OsdItem* pstCurFocus = OSD_GET_ITEM(s_stOsdOpsData.usFocusingItemId);
    if (NULL != pstCurFocus)
    {
        Hv_Mw_OsdItemUnFocusUpdate(pstCurFocus, FOCUSING_CLEAR);
    }
    s_stOsdOpsData.usFocusingItemId = ITEM_NONE_ID;
}

USHORT16 Hv_Mw_OsdItemFocus(USHORT16 usItemId)
{
    OSD_LOG_DEBUG("Focusing move to %s.", ITEM_NAME(usItemId));
    Hv_Mw_OsdItemChooseFocus(usItemId);
    return Hv_Mw_OsdItemMoveFocus(FOCUSING_CHOSEN);
}

USHORT16 Hv_Mw_OsdItemFocusChild(VOID)
{
    OSD_LOG_DEBUG("Focusing move to child.");
    return Hv_Mw_OsdItemMoveFocus(FOCUSING_CHILD);
}

USHORT16 Hv_Mw_OsdItemFocusChildSelect(VOID)
{
    OSD_LOG_DEBUG("Focusing move to child selected.");
    return Hv_Mw_OsdItemMoveFocus(FOCUSING_CHILD_SELECT);
}

USHORT16 Hv_Mw_OsdItemFocusValue(VOID)
{
    OSD_LOG_DEBUG("Focusing move to value.");
    return Hv_Mw_OsdItemMoveFocus(FOCUSING_VALUE);
}

USHORT16 Hv_Mw_OsdItemFocusParent(VOID)
{
    OSD_LOG_DEBUG("Focusing move to parent.");
    return Hv_Mw_OsdItemMoveFocus(FOCUSING_PARENT);
}

USHORT16 Hv_Mw_OsdItemFocusNext(VOID)
{
    OSD_LOG_DEBUG("Focusing move to next.");
    return Hv_Mw_OsdItemMoveFocus(FOCUSING_NEXT);
}

USHORT16 Hv_Mw_OsdItemFocusPrev(VOID)
{
    OSD_LOG_DEBUG("Focusing move to prev.");
    return Hv_Mw_OsdItemMoveFocus(FOCUSING_PREV);
}

USHORT16 Hv_Mw_OsdItemFocusNextAutoFlip(VOID)
{
    USHORT16 usItem = OSD_GET_FOCUSING();
    USHORT16 usNewFocusItem = Hv_Mw_OsdItemFocusNext();

    if (ITEM_NONE_ID != usNewFocusItem)
    {
        if (!Hv_Mw_OsdItemIsAtSamePage(usItem, usNewFocusItem))
        {
            Hv_Mw_OsdItemHideList(usItem);
            Hv_Mw_OsdItemShowList(usNewFocusItem);
        }
    }

    return usNewFocusItem;
}

USHORT16 Hv_Mw_OsdItemFocusPrevAutoFlip(VOID)
{
    USHORT16 usItem = OSD_GET_FOCUSING();
    USHORT16 usNewFocusItem = Hv_Mw_OsdItemFocusPrev();

    if (ITEM_NONE_ID != usNewFocusItem)
    {
        if (!Hv_Mw_OsdItemIsAtSamePage(usItem, usNewFocusItem))
        {
            Hv_Mw_OsdItemHideList(usItem);
            Hv_Mw_OsdItemShowList(usNewFocusItem);
        }
    }

    return usNewFocusItem;
}

USHORT16 Hv_Mw_OsdItemFocusNextAutoFlipWithSingleChild(VOID)
{
    USHORT16 usItem = OSD_GET_FOCUSING();
    USHORT16 usNewFocusItem = Hv_Mw_OsdItemFocusNext();

    if (ITEM_NONE_ID != usNewFocusItem)
    {
        if (!Hv_Mw_OsdItemIsAtSamePage(usItem, usNewFocusItem))
        {
            Hv_Mw_OsdItemHideListWithSingleChild(usItem);
            Hv_Mw_OsdItemShowListWithSingleChild(usNewFocusItem);
        }
    }

    return usNewFocusItem;
}

USHORT16 Hv_Mw_OsdItemFocusPrevAutoFlipWithSingleChild(VOID)
{
    USHORT16 usItem = OSD_GET_FOCUSING();
    USHORT16 usNewFocusItem = Hv_Mw_OsdItemFocusPrev();

    if (ITEM_NONE_ID != usNewFocusItem)
    {
        if (!Hv_Mw_OsdItemIsAtSamePage(usItem, usNewFocusItem))
        {
            Hv_Mw_OsdItemHideListWithSingleChild(usItem);
            Hv_Mw_OsdItemShowListWithSingleChild(usNewFocusItem);
        }
    }

    return usNewFocusItem;
}

USHORT16 Hv_Mw_OsdItemFocusNextAutoFlipWithSingleValue(VOID)
{
    USHORT16 usItem = OSD_GET_FOCUSING();
    USHORT16 usNewFocusItem = Hv_Mw_OsdItemFocusNext();

    if (ITEM_NONE_ID != usNewFocusItem)
    {
        if (!Hv_Mw_OsdItemIsAtSamePage(usItem, usNewFocusItem))
        {
            Hv_Mw_OsdItemHideListWithSingleValue(usItem);
            Hv_Mw_OsdItemShowListWithSingleValue(usNewFocusItem);
        }
    }

    return usNewFocusItem;
}

USHORT16 Hv_Mw_OsdItemFocusPrevAutoFlipWithSingleValue(VOID)
{
    USHORT16 usItem = OSD_GET_FOCUSING();
    USHORT16 usNewFocusItem = Hv_Mw_OsdItemFocusPrev();

    if (ITEM_NONE_ID != usNewFocusItem)
    {
        if (!Hv_Mw_OsdItemIsAtSamePage(usItem, usNewFocusItem))
        {
            Hv_Mw_OsdItemHideListWithSingleValue(usItem);
            Hv_Mw_OsdItemShowListWithSingleValue(usNewFocusItem);
        }
    }

    return usNewFocusItem;
}

static VOID Hv_Mw_OsdNodeFree(OsdComponentNode* pstNode)
{
    if (NULL == pstNode)
    {
        return;
    }

    if (pstNode->pvDynamicData)
    {
        HV_FREE(pstNode->pvDynamicData);
        pstNode->pvDynamicData = NULL;
#ifdef HV_SCALER_DEBUG_VERSION
        Hv_Mw_OsdDebugDynamicData(pstNode->stCp, HV_FALSE);
#endif
    }

#if (HV_CONFIG_ON == HV_PROJECT_CONFIG_OSD_MARQUEE)
    if (NULL != pstNode->pstMarquee)
    {
        Hv_Drv_OsdFont_RemoveMarqueeNode(pstNode->pstMarquee);
    }
#endif

#if (HV_PROJECT_CONFIG_OSD_CUSTOM_GROUP_SUPPORT == HV_CONFIG_ON)
    if (OSD_CP_TYPE_CUSTOM_GROUP == pstNode->stCp.enType)
    {
        Hv_Drv_OsdFont_RemoveCustomGroup(pstNode->stCp);
    }
#endif

    HV_FREE(pstNode);
}

static VOID Hv_Mw_OsdNodeClearList(OsdNodeHead* pstHead)
{
    HV_ASSERT_VALID_PTR_VOID(pstHead);
    OsdComponentNode *pstNext = NULL;
    OsdComponentNode *pstNode = NULL;

    pstNode = pstHead->pstNode;
    while (NULL != pstNode)
    {
        pstNext = pstNode->pstNext;
        Hv_Mw_OsdNodeFree(pstNode);
        pstNode = pstNext;
    }

    pstHead->pstNode = NULL;
    pstHead->pstLastNode = NULL;
    pstHead->usTotalNum = 0;
    pstHead->usUpdateNum = 0;
}

VOID Hv_Mw_OsdWindowClearShowList(OsdWindow* pstWindow)
{
    HV_ASSERT_VALID_PTR_VOID(pstWindow);

    OSD_LOG_VERBOSE("Clear Window-%d's ShowNodeList.", pstWindow->ucId);
    Hv_Mw_OsdNodeClearList(&pstWindow->stCpShowListHead);
    Hv_Mw_OsdNodeClearList(&pstWindow->stFontShowListHead);
}

VOID Hv_Mw_OsdWindowClearDeleteList(OsdWindow* pstWindow)
{
    HV_ASSERT_VALID_PTR_VOID(pstWindow);

    OSD_LOG_VERBOSE("Clear Window-%d's DeleteNodeList.", pstWindow->ucId);
    Hv_Mw_OsdNodeClearList(&pstWindow->stDeleteListHead);
    Hv_Mw_OsdNodeClearList(&pstWindow->stFontDeleteListHead);
}

VOID Hv_Mw_OsdWindowClearUpdateInfo(OsdWindow* pstWindow)
{
    HV_ASSERT_VALID_PTR_VOID(pstWindow);

    OSD_LOG_DEBUG("Clear Window-%d's update info.", pstWindow->ucId);
    Hv_Mw_OsdWindowClearDeleteList(pstWindow);

    pstWindow->stCpShowListHead.usUpdateNum = 0;
    pstWindow->stFontShowListHead.usUpdateNum = 0;
}

BOOL Hv_Mw_OsdWindowCheckIsEmpty(UCHAR8 ucWindowId)
{
    OsdWindow *pstWindow = OSD_GET_WINDOW(ucWindowId);
    HV_ASSERT_VALID_PTR_RET(pstWindow, HV_FALSE);

    return ((pstWindow->stCpShowListHead.usTotalNum == 0) && (pstWindow->stFontShowListHead.usTotalNum == 0));
}

static VOID Hv_Mw_OsdWindowInit(UCHAR8 ucWindowId)
{
    OsdWindow *pstWindow = OSD_GET_WINDOW(ucWindowId);
    HV_ASSERT_VALID_PTR_VOID(pstWindow);

    pstWindow->bVisable = HV_FALSE;
    pstWindow->bModify = HV_FALSE;
}

static VOID Hv_Mw_OsdWindowShow(UCHAR8 ucWindowId)
{
    OsdWindow *pstWindow = OSD_GET_WINDOW(ucWindowId);
    HV_ASSERT_VALID_PTR_VOID(pstWindow);

    if (!pstWindow->bVisable)
    {
        OSD_LOG_DEBUG("Show Window %d", ucWindowId);
        pstWindow->bVisable = HV_TRUE;
        pstWindow->bModify = HV_TRUE;
        s_stOsdOpsData.bNeedRedraw = HV_TRUE;
    }
}

static VOID Hv_Mw_OsdWindowHide(UCHAR8 ucWindowId)
{
    OsdWindow *pstWindow = OSD_GET_WINDOW(ucWindowId);
    HV_ASSERT_VALID_PTR_VOID(pstWindow);

    if (pstWindow->bVisable)
    {
        OSD_LOG_DEBUG("Hide Window %d", ucWindowId);
        pstWindow->bVisable = HV_FALSE;
        pstWindow->bModify = HV_TRUE;
    }
}

static VOID Hv_Mw_OsdWindowClear(UCHAR8 ucWindowId)
{
    OsdWindow *pstWindow = OSD_GET_WINDOW(ucWindowId);
    HV_ASSERT_VALID_PTR_VOID(pstWindow);

    Hv_Mw_OsdWindowClearShowList(pstWindow);
    Hv_Mw_OsdWindowClearDeleteList(pstWindow);
    pstWindow->bVisable = HV_FALSE;
}

static VOID Hv_Mw_OsdSceneInitItemList(UCHAR8 ucSceneId)
{
    const OsdSceneProp *pstSceneProp = OSD_GET_SCENE_PROP(ucSceneId);
    HV_ASSERT_VALID_PTR_VOID(pstSceneProp);
    OsdItem* pstItemList = NULL;

    OSD_LOG_DEBUG("Reset %s all item Visable and State", SCENE_NAME(ucSceneId));
    pstItemList = OSD_GET_ITEM(pstSceneProp->usItemStartId);
    if (pstItemList)
    {
        for (USHORT16 usItemId = 0; usItemId < pstSceneProp->usItemNum; usItemId++)
        {
            pstItemList[usItemId].bVisable = HV_FALSE;
            pstItemList[usItemId].ucState = OSD_ITEM_NORMAL;
        }
    }
}

VOID Hv_Mw_OsdSceneInit(UCHAR8 ucSceneId)
{
    OsdScene *pstScene = OSD_GET_SCENE(ucSceneId);
    HV_ASSERT_VALID_PTR_VOID(pstScene);
    const OsdSceneProp *pstSceneProp = OSD_GET_SCENE_PROP(ucSceneId);
    HV_ASSERT_VALID_PTR_VOID(pstSceneProp);
    UCHAR8 ucWindowStartId = 0;
    UCHAR8 ucWindowEndId = 0;

    OSD_LOG_DEBUG("Init scene %s", SCENE_NAME(ucSceneId));

    CLEAR_SCENE_VISABLE_FLAG(ucSceneId);

    for (UCHAR8 ucPartId = 0; ucPartId < pstSceneProp->ucFramePartNum; ucPartId++)
    {
        /* init window */
        ucWindowStartId = pstSceneProp->astFramePart[ucPartId].ucWindowStartId;
        ucWindowEndId = ucWindowStartId + pstSceneProp->astFramePart[ucPartId].ucWindowNum;
        for (UCHAR8 ucWinId = ucWindowStartId; ucWinId < ucWindowEndId; ucWinId++)
        {
            Hv_Mw_OsdWindowInit(ucWinId);
        }
    }

    /* clear所有component showlist */
    Hv_Mw_OsdSceneClear(ucSceneId);

    Hv_Mw_OsdSceneInitItemList(ucSceneId);

    pstScene->bVisable = HV_FALSE;
    pstScene->bModify = HV_FALSE;
}

VOID Hv_Mw_OsdSceneShow(UCHAR8 ucSceneId)
{
    OsdScene *pstScene = OSD_GET_SCENE(ucSceneId);
    HV_ASSERT_VALID_PTR_VOID(pstScene);
    const OsdSceneProp *pstSceneProp = OSD_GET_SCENE_PROP(ucSceneId);
    HV_ASSERT_VALID_PTR_VOID(pstSceneProp);
    UCHAR8 ucWindowStartId = 0;
    UCHAR8 ucWindowEndId = 0;
    UCHAR8 ucWinCount = 0;
    const OsdComponent* pstCpList = NULL;

    OSD_LOG_DEBUG("Show scene %s", SCENE_NAME(ucSceneId));
    pstScene->bVisable = HV_TRUE;

    for (UCHAR8 ucPartId = 0; ucPartId < pstSceneProp->ucFramePartNum; ucPartId++)
    {
        /* show window */
        ucWindowStartId = pstSceneProp->astFramePart[ucPartId].ucWindowStartId;
        ucWindowEndId = ucWindowStartId + pstSceneProp->astFramePart[ucPartId].ucWindowNum;
        for (UCHAR8 ucWinId = ucWindowStartId; ucWinId < ucWindowEndId; ucWinId++)
        {
            if (GET_SCENE_VISABLE_FLAG(ucSceneId, ucWinCount))
            {
                Hv_Mw_OsdWindowShow(ucWinId);
            }
#if (HV_PROJECT_CONFIG_OSD_FORCE_REDRAW_ALL == HV_CONFIG_OFF)
            if (OSD_GET_WINDOW(ucWinId)->ucPhyId <= PHY_WIN_3)
            {
                Hv_Mw_OsdUpdateFontResList(OSD_GET_WINDOW(ucWinId));
            }
#endif
            ucWinCount++;
        }
    }

    /* 显示所有静态component */
    OSD_LOG_DEBUG("Show %s All static Component List.", SCENE_NAME(ucSceneId));
    for (UCHAR8 ucPartId = 0; ucPartId < pstSceneProp->ucFramePartNum; ucPartId++)
    {
        pstCpList = pstSceneProp->astFramePart[ucPartId].pstCpList;
        if (pstCpList)
        {
            for (USHORT16 usCpCnt = 0; pstCpList[usCpCnt].enType != OSD_CP_TYPE_NONE; usCpCnt++)
            {
                Hv_Mw_OsdComponentShow(pstCpList[usCpCnt]);
#if (HV_CONFIG_ON == HV_PROJECT_CONFIG_OSD_MIRROR)
                if (bRTLShow)
                {
                    Hv_Mw_OsdDynamicComponentMirrorUpdate(pstCpList[usCpCnt], 0, 0);
                }
#endif
            }
        }
    }

    if (pstSceneProp->ucFramePartNum > 1)
    {
        s_stOsdOpsData.bNeedMultiFrameDraw = HV_TRUE;
    }

    pstScene->bModify = HV_TRUE;
}

VOID Hv_Mw_OsdSceneHide(UCHAR8 ucSceneId)
{
    OsdScene *pstScene = OSD_GET_SCENE(ucSceneId);
    HV_ASSERT_VALID_PTR_VOID(pstScene);
    const OsdSceneProp *pstSceneProp = OSD_GET_SCENE_PROP(ucSceneId);
    HV_ASSERT_VALID_PTR_VOID(pstSceneProp);
    UCHAR8 ucWindowStartId = 0;
    UCHAR8 ucWindowEndId = 0;
    UCHAR8 ucWinCount = 0;

    OSD_LOG_DEBUG("Hide scene %s", SCENE_NAME(ucSceneId));
    CLEAR_SCENE_VISABLE_FLAG(ucSceneId);
    for (UCHAR8 ucPartId = 0; ucPartId < pstSceneProp->ucFramePartNum; ucPartId++)
    {
        /* hide window */
        ucWindowStartId = pstSceneProp->astFramePart[ucPartId].ucWindowStartId;
        ucWindowEndId = ucWindowStartId + pstSceneProp->astFramePart[ucPartId].ucWindowNum;
        for (UCHAR8 ucWinId = ucWindowStartId; ucWinId < ucWindowEndId; ucWinId++)
        {
            if (OSD_GET_WINDOW(ucWinId)->bVisable)
            {
                SET_SCENE_VISABLE_FLAG(ucSceneId, ucWinCount);
                Hv_Mw_OsdWindowHide(ucWinId);
            }
#if (HV_PROJECT_CONFIG_OSD_FORCE_REDRAW_ALL == HV_CONFIG_OFF)
            if (OSD_GET_WINDOW(ucWinId)->ucPhyId <= PHY_WIN_3)
            {
                Hv_Mw_OsdClearFontResList(OSD_GET_WINDOW(ucWinId));
            }
#endif
            ucWinCount++;
        }
    }
    pstScene->bVisable = HV_FALSE;
    pstScene->bModify = HV_TRUE;
#if (HV_PROJECT_CONFIG_OSD_DDRPATH == HV_CONFIG_ON)
    /* Hide one scene when multi scene on screen, must redraw */
    Hv_Mw_OsdOpsForceRedraw(HV_TRUE);
#endif
}

VOID Hv_Mw_OsdSceneRefresh(UCHAR8 ucSceneId)
{
    const OsdSceneProp *pstSceneProp = OSD_GET_SCENE_PROP(ucSceneId);
    HV_ASSERT_VALID_PTR_VOID(pstSceneProp);
    const OsdScene *pstScene = OSD_GET_SCENE(ucSceneId);

    if (!pstScene->bVisable)
    {
        OSD_LOG_VERBOSE("%s is not visable, do not refresh", SCENE_NAME(ucSceneId));
        return;
    }

    USHORT16 usItemEndId = pstSceneProp->usItemStartId + pstSceneProp->usItemNum;
    OSD_LOG_DEBUG("Refresh %s's all visable items", SCENE_NAME(ucSceneId));
    for (USHORT16 usItemId = pstSceneProp->usItemStartId; usItemId < usItemEndId; usItemId++)
    {
        Hv_Mw_OsdItemRefresh(usItemId);
    }
}

VOID Hv_Mw_OsdRefreshAll(VOID)
{
    OSD_LOG_DEBUG("Refresh all screen");

    for (UCHAR8 ucSceneId = 0; ucSceneId < SCENE_ID_MAX; ucSceneId++)
    {
        Hv_Mw_OsdSceneRefresh(ucSceneId);
    }
}

VOID Hv_Mw_OsdSceneClear(UCHAR8 ucSceneId)
{
    OsdScene *pstScene = OSD_GET_SCENE(ucSceneId);
    HV_ASSERT_VALID_PTR_VOID(pstScene);
    const OsdSceneProp *pstSceneProp = OSD_GET_SCENE_PROP(ucSceneId);
    HV_ASSERT_VALID_PTR_VOID(pstSceneProp);
    OsdWindow *pstWindow = NULL;
    UCHAR8 ucWindowStartId = 0;
    UCHAR8 ucWindowEndId = 0;

    OSD_LOG_DEBUG("Clear %s", SCENE_NAME(ucSceneId));
    for (UCHAR8 ucPartId = 0; ucPartId < pstSceneProp->ucFramePartNum; ucPartId++)
    {
        ucWindowStartId = pstSceneProp->astFramePart[ucPartId].ucWindowStartId;
        ucWindowEndId = ucWindowStartId + pstSceneProp->astFramePart[ucPartId].ucWindowNum;
        for (UCHAR8 ucWinId = ucWindowStartId; ucWinId < ucWindowEndId; ucWinId++)
        {
            Hv_Mw_OsdWindowClear(ucWinId);
        }
    }
    pstScene->bVisable = HV_FALSE;
}

VOID Hv_Mw_OsdSceneClearAll(VOID)
{
    OsdScene *pstScene = NULL;
    OSD_LOG_DEBUG("Clear All Scene.");

    Hv_Mw_OsdItemFocusClear();

    for (UCHAR8 ucId = 0; ucId < SCENE_ID_MAX; ucId++)
    {
        Hv_Mw_OsdSceneClear(ucId);
        pstScene = OSD_GET_SCENE(ucId);
        if (pstScene)
        {
            pstScene->bModify = HV_FALSE;
        }
    }

    s_stOsdOpsData.bNeedRedraw = HV_TRUE;
}

BOOL Hv_Mw_OsdSceneIsShowing(UCHAR8 ucSceneId)
{
    OsdScene *pstScene = OSD_GET_SCENE(ucSceneId);
    return (NULL != pstScene) ? pstScene->bVisable : HV_FALSE;
}

UCHAR8 Hv_Mw_OsdSceneGetShowing(UCHAR8 ucSceneIdStart)
{
    for (UCHAR8 ucId = ucSceneIdStart; ucId < SCENE_ID_MAX; ucId++)
    {
        if (Hv_Mw_OsdSceneIsShowing(ucId))
        {
            return ucId;
        }
    }
    return SCENE_ID_MAX;
}

BOOL Hv_Mw_OsdIsShowing(VOID)
{
    return (SCENE_ID_MAX != Hv_Mw_OsdSceneGetShowing(0));
}
