/**
 * @file hv_drv_OsdIndex.c
 * @brief Osd Driver index management c file.
 * @details This file provides the following functions: \n
 *          (1) index initialization & reset functions \n
 *          (2) calculate row's column number functions \n
 *          (3) row & column alloc functions \n
 *          (4) blank row insert functions \n
 *          (5) index transfer functions \n
 *
 * @author HiView SoC Software Team
 * @version 1.0.0
 * @date 2022-08-31
 * @copyright Copyright(c),2022-8, Hiview Software. All rights reserved.
 * @par History:
 * <table>
 * <tr><th>Author         <th>Date        <th>Change Description
 * <tr><td>OSD Group      <td>2022-08-31   <td>create
 * </table>
*/

#include "hv_drv_Osd.h"
#include "hv_cal_Osd.h"

#ifdef HV_SCALER_DEBUG_VERSION
USHORT16 g_usMaxIndexSize = 0;
#endif
static USHORT16 s_usCurrentIndexOffset = 0;
static OsdSRAMChange s_stIndexChange;

VOID Hv_Drv_OsdIndex_ResetOffset(VOID)
{
    OSD_DRV_LOG_DEBUG("Index offset reset to 0");

    s_usCurrentIndexOffset = 0;
}

VOID Hv_Drv_OsdIndex_AllocRegion(USHORT16 usSize)
{
    HV_ASSERT_TRUE_VOID(s_usCurrentIndexOffset + usSize <= OSD_INDEX_BUFFER_SIZE);

    usSize = ALIGN(usSize, 16);

    OSD_DRV_LOG_DEBUG("Allocate index from 0x%x, size: 0x%x", s_usCurrentIndexOffset, usSize);
    s_usCurrentIndexOffset += usSize;
#ifdef HV_SCALER_DEBUG_VERSION
    if (g_usMaxIndexSize < s_usCurrentIndexOffset)
    {
        g_usMaxIndexSize = s_usCurrentIndexOffset;
    }
#endif
}

UCHAR8 *Hv_Drv_OsdIndex_GetCurrentAddress(VOID)
{
    return (UCHAR8*)(OSD_INDEX_DDR_START + s_usCurrentIndexOffset);
}

OsdIndexInfo *Hv_Drv_OsdIndex_GetCurrentIndexInfo(VOID)
{
    OsdPhyFontInfo *pstFontInfo = NULL;

    pstFontInfo = Hv_Drv_OsdFont_GetCurrentFontInfo();

    return &pstFontInfo->stIndexInfo;
}

static VOID Hv_Drv_OsdIndex_IncreaseDelta(OsdIndexColumn *pstColumn, USHORT16 usDelta)
{
    BOOL bIsIncrease = HV_FALSE;
    UCHAR8 ucCurDelta = 0;
    OsdColumnType enColType = 0;

    enColType = Hv_Cal_OsdIndex_GetColType(pstColumn);
    if (enColType == OSD_COLUMN_BMP)
    {
        OSD_DRV_LOG_ERROR("OSD forbids component too closely following BMP: %d", usDelta);
        return;
    }

    bIsIncrease = Hv_Cal_OsdIndex_DeltaIsIncrease(pstColumn);
    ucCurDelta = Hv_Cal_OsdIndex_GetColDelta(pstColumn);

    if (bIsIncrease)
    {
        ucCurDelta += usDelta;
    }
    else
    {
        if (ucCurDelta < usDelta)
        {
            ucCurDelta = usDelta - ucCurDelta;
            bIsIncrease = 1;
        }
        else
        {
            ucCurDelta -= usDelta;
        }
    }

    Hv_Cal_OsdIndex_SetColDelta(pstColumn, bIsIncrease, ucCurDelta);
}

VOID Hv_Drv_OsdIndex_DecreaseDelta(OsdIndexColumn *pstColumn, USHORT16 usDelta)
{
    BOOL bIsIncrease = HV_FALSE;
    UCHAR8 ucCurDelta = 0;
    OsdColumnType enColType = 0;

    enColType = Hv_Cal_OsdIndex_GetColType(pstColumn);
    if (enColType == OSD_COLUMN_BMP)
    {
        OSD_DRV_LOG_ERROR("OSD forbids component too closely following BMP: %d", usDelta);
        return;
    }

    bIsIncrease = Hv_Cal_OsdIndex_DeltaIsIncrease(pstColumn);
    ucCurDelta = Hv_Cal_OsdIndex_GetColDelta(pstColumn);

    if (!bIsIncrease)
    {
        ucCurDelta += usDelta;
    }
    else
    {
        if (ucCurDelta < usDelta)
        {
            ucCurDelta = usDelta - ucCurDelta;
            bIsIncrease = 0;
        }
        else
        {
            ucCurDelta -= usDelta;
        }
    }

    Hv_Cal_OsdIndex_SetColDelta(pstColumn, bIsIncrease, ucCurDelta);
}

OsdIndexColumn *Hv_Drv_OsdIndex_FillColumnBlanks(OsdIndexColumn *pstColumn,
                                                USHORT16 usDiff, UCHAR8 ucWidth)
{
    OsdIndexColumn *pstPrevColumn = NULL;

    if (0 == usDiff)
    {
        return pstColumn;
    }
    else if (usDiff <= 4)
    {
        pstPrevColumn = pstColumn - 1;
        Hv_Drv_OsdIndex_IncreaseDelta(pstPrevColumn, usDiff);

        return pstColumn;
    }

    while (usDiff > 0)
    {
        Hv_Cal_OsdIndex_SetColType(pstColumn, OSD_COLUMN_BLANK);
        if (usDiff >= 2 * (USHORT16)ucWidth)
        {
            Hv_Cal_OsdIndex_SetColBlankWidth(pstColumn, ucWidth);
            usDiff -= ucWidth;
        }
        else
        {
            Hv_Cal_OsdIndex_SetColBlankWidth(pstColumn, usDiff);
            usDiff = 0;
        }
        pstColumn++;
    }

    return pstColumn;
}

OsdIndexRow* Hv_Drv_OsdIndex_NewRow(OsdIndexRow *pstPrevRow)
{
    OsdIndexInfo *pstIndexInfo = Hv_Drv_OsdIndex_GetCurrentIndexInfo();
    OsdIndexRow *pstRow = NULL;

    OSD_DRV_LOG_DEBUG("Row %u set as normal Row", pstIndexInfo->ucRowCount);

    pstIndexInfo->ucRowCount++;

    if (NULL != pstPrevRow)
    {
        pstRow = pstPrevRow + 1;
    }
    else
    {
        pstRow = (OsdIndexRow*)pstIndexInfo->pucIndexDDRAddr;
    }

    HV_MEMSET(pstRow, 0, sizeof(*pstRow));

    Hv_Cal_OsdIndex_SetRowBlinkEnable(pstRow, 0);
    Hv_Cal_OsdIndex_SetRowHSpace(pstRow, 0);
    Hv_Cal_OsdIndex_SetRowAttr(pstRow, 1);
    Hv_Cal_OsdIndex_SetRowEnable(pstRow, 1);

    return pstRow;
}

static OsdIndexRow* Hv_Drv_OsdIndex_NewBlankRow(OsdIndexRow *pstCurRow, UCHAR8 ucSpace)
{
    OsdIndexInfo *pIndexInfo = Hv_Drv_OsdIndex_GetCurrentIndexInfo();

    OSD_DRV_LOG_DEBUG("Row %u set as blank Row vspace %u", pIndexInfo->ucRowCount, ucSpace);

    pIndexInfo->ucRowCount++;
    pstCurRow++;

    HV_MEMSET(pstCurRow, 0, sizeof(*pstCurRow));

    Hv_Cal_OsdIndex_SetRowAttr(pstCurRow, 1);
    Hv_Cal_OsdIndex_SetRowEnable(pstCurRow, 0);
    Hv_Cal_OsdIndex_SetRowVSpace(pstCurRow, ucSpace);
    Hv_Cal_OsdIndex_SetRowLen(pstCurRow, 0);

    return pstCurRow;
}

VOID Hv_Drv_OsdIndex_NewEndRow(VOID)
{
    OsdIndexInfo *pIndexInfo = Hv_Drv_OsdIndex_GetCurrentIndexInfo();
    OsdIndexRow *pstRow = Hv_Drv_OsdIndex_GetRow(pIndexInfo->ucRowCount++);

    HV_MEMSET(pstRow, 0, sizeof(*pstRow));
    Hv_Cal_OsdIndex_SetRowAttr(pstRow, 0);
}

static USHORT16 Hv_Drv_OsdIndex_GetRowIndex(OsdIndexRow *pstRow)
{
    OsdIndexInfo *pIndexInfo = Hv_Drv_OsdIndex_GetCurrentIndexInfo();

    HV_ASSERT_TRUE_RET(pstRow != NULL, 0);

    return ((UINT32)pstRow - (UINT32)pIndexInfo->pucIndexDDRAddr) >> 2;
}

OsdIndexRow* Hv_Drv_OsdIndex_FillBlankLines(OsdIndexRow *pstCurRow, USHORT16 usPixels)
{
    UCHAR8 ucQuo = 0;
    UCHAR8 ucRmd = 0;
    UCHAR8 ucCount = 0;

    ucQuo = usPixels / 31;
    ucRmd = usPixels % 31;
    if (ucRmd < 16)
    {
        OSD_DRV_LOG_DEBUG("Row %u vspace %u", Hv_Drv_OsdIndex_GetRowIndex(pstCurRow), ucRmd);
        Hv_Cal_OsdIndex_SetRowVSpace(pstCurRow, ucRmd);
    }
    else
    {
        OSD_DRV_LOG_DEBUG("Row %u vspace %u", Hv_Drv_OsdIndex_GetRowIndex(pstCurRow), 0);
        Hv_Cal_OsdIndex_SetRowVSpace(pstCurRow, 0);
        pstCurRow = Hv_Drv_OsdIndex_NewBlankRow(pstCurRow, ucRmd - 16);
    }

    for (; ucCount < ucQuo; ++ucCount)
    {
        pstCurRow = Hv_Drv_OsdIndex_NewBlankRow(pstCurRow, 15);
    }

    return pstCurRow;
}

OsdIndexRow* Hv_Drv_OsdIndex_GetRow(UCHAR8 ucRowIndex)
{
    OsdIndexInfo *pIndexInfo = Hv_Drv_OsdIndex_GetCurrentIndexInfo();

    return (OsdIndexRow*)(pIndexInfo->pucIndexDDRAddr + ucRowIndex * sizeof(OsdIndexRow));
}

VOID Hv_Drv_OsdIndex_SetChangeRange(OsdSRAMChangeType enType, UINT32 uiChangeStart, UINT32 uiChangeEnd)
{
    Hv_Drv_OsdMemory_SetSRAMChange(&s_stIndexChange, enType, uiChangeStart, uiChangeEnd);

    OSD_DRV_LOG_DEBUG("Updated Osd Index change, type: %s range: 0x%x - 0x%x",
            Hv_Drv_OsdCommon_GetSRAMChangeTypeStr(enType),
            s_stIndexChange.uiChangeStart, s_stIndexChange.uiChangeEnd);
}

VOID Hv_Drv_OsdIndex_TransferIndex(VOID)
{
    UINT32 uiAddr = 0;
    UINT32 uiLength = 0;
    OsdSRAMChange *pstChange = &s_stIndexChange;

    if (pstChange->enType == OSD_SRAM_CHANGE_NONE)
    {
        return;
    }
    else if (pstChange->enType == OSD_SRAM_CHANGE_FULL)
    {
        Hv_Drv_OsdDMA_Pend(OSD_INDEX_DDR_START, OSD_INDEX_BUFFER_BASE_ADDR,
                s_usCurrentIndexOffset);
    }
    else
    {
        uiAddr = OSD_INDEX_BUFFER_BASE_ADDR + pstChange->uiChangeStart - OSD_INDEX_DDR_START;
        uiLength = pstChange->uiChangeEnd - pstChange->uiChangeStart;

        Hv_Drv_OsdDMA_Pend(pstChange->uiChangeStart, uiAddr, uiLength);
    }
    OSD_DRV_LOG_DEBUG("Index total size = 0x%x", s_usCurrentIndexOffset);

    Hv_Drv_OsdIndex_SetChangeRange(OSD_SRAM_CHANGE_NONE, 0, 0);
}

VOID Hv_Drv_OsdIndex_Init(VOID)
{
    HV_MEMSET(&s_stIndexChange, 0, sizeof(s_stIndexChange));
    Hv_Drv_OsdIndex_ResetOffset();
}
