/******************************************************************************
 * biso_util.c
 *
 * Copyright (c) 2020, longpanda <admin@ventoy.net>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 *
 */
 

#include "biso.h"
#include "biso_list.h"
#include "biso_util.h"
#include "biso_9660.h"

VOID *zalloc(size_t size)
{
    void *p = malloc(size);
    if (NULL != p)
    {
        memset(p, 0, size);
    }
    return p;
}


#if (1 == MEMORY_DEBUG)
STATIC UINT g_uiBISOTotMalloc = 0;
STATIC UINT g_uiBISOPeekMalloc = 0;
STATIC UINT g_uiBISOMallocTime = 0;
STATIC UINT g_uiBISOFreeTime = 0;

VOID *g_apstBISOMalloc[7000];
VOID *g_apstBISOFree[7000];

VOID * BISO_UTIL_Malloc(IN size_t ulSize)
{
    VOID *pData = malloc(ulSize + 4);
    
    #if (1 == MEMORY_DEBUG_DUMP)
    printf("ID %u Malloc %p %lu\n", g_uiBISOMallocTime, (UCHAR *)pData + 4, ulSize);
    g_apstBISOMalloc[g_uiBISOMallocTime] = (UCHAR *)pData + 4;
    #endif

    *(UINT32 *)pData = (UINT32)ulSize;
    g_uiBISOMallocTime++;
    g_uiBISOTotMalloc += (UINT32)ulSize;
    if (g_uiBISOTotMalloc > g_uiBISOPeekMalloc)
    {
        g_uiBISOPeekMalloc = g_uiBISOTotMalloc;
    }
   
    return (UCHAR *)pData + 4;
}

VOID *BISO_UTIL_Zalloc(IN size_t ulSize)
{
    VOID *pData = zalloc(ulSize + 4);

    #if (1 == MEMORY_DEBUG_DUMP)
    printf("ID %u Zalloc %p %lu\n", g_uiBISOMallocTime, (UCHAR *)pData + 4, ulSize);
    g_apstBISOMalloc[g_uiBISOMallocTime] = (UCHAR *)pData + 4;
    #endif

    *(UINT32 *)pData = (UINT32)ulSize;
    g_uiBISOMallocTime++;
    g_uiBISOTotMalloc += (UINT32)ulSize;
    if (g_uiBISOTotMalloc > g_uiBISOPeekMalloc)
    {
        g_uiBISOPeekMalloc = g_uiBISOTotMalloc;
    }
   
    return (UCHAR *)pData + 4;
}

VOID BISO_UTIL_Free(IN VOID *pData)
{    
    #if (1 == MEMORY_DEBUG_DUMP)
    printf("ID %u Free %p %u\n", g_uiBISOFreeTime, pData, *(UINT32 *)((UCHAR *)pData - 4));
    g_apstBISOFree[g_uiBISOFreeTime] = pData;
    #endif

    g_uiBISOFreeTime++;
    g_uiBISOTotMalloc -= *(UINT32 *)((UCHAR *)pData - 4);
    if (g_uiBISOTotMalloc > g_uiBISOPeekMalloc)
    {
        g_uiBISOPeekMalloc = g_uiBISOTotMalloc;
    }
    
    free((UCHAR *)pData - 4);
}

VOID BISO_UTIL_DumpMemOp(VOID)
{
    BISO_DUMP("\n Memory Operation: Malloc(%u) Free(%u) \nTotal current use %u, Peek memory use %u.\n", 
              g_uiBISOMallocTime, g_uiBISOFreeTime, g_uiBISOTotMalloc, g_uiBISOPeekMalloc);

#if (1 == MEMORY_DEBUG_DUMP)
{
    UINT i, j;
    for (i = 0; i < g_uiBISOMallocTime; i++)
    {
        for (j = 0; j < g_uiBISOFreeTime; j++)
        {
            if (g_apstBISOMalloc[i] == g_apstBISOFree[j])
            {
                break;
            }
        }

        if (j >= g_uiBISOFreeTime)
        {
            printf("ID %u ptr %p is not freed.\n", i, g_apstBISOMalloc[i]);
        }
    }
}
#endif

}
#endif

INT BISO_UTIL_GetTimeZone(VOID)
{
    INT iTimeZone;
    INT iLocalHour;
    INT iGMTHour;
    time_t ulTime;
    struct tm *pstLocalTM = NULL;
    struct tm *pstGMTM = NULL;
    
    time(&ulTime);
    pstGMTM = gmtime(&ulTime); 
    iGMTHour = pstGMTM->tm_hour;

    pstLocalTM = localtime(&ulTime);
    iLocalHour = pstLocalTM->tm_hour;

    iTimeZone = iLocalHour - iGMTHour;     
    if (iTimeZone < -12) 
    {  
        iTimeZone += 24;
    } 
    else if (iTimeZone > 12) 
    {
        iTimeZone -= 24;
    }

    return iTimeZone;
}

ULONG BISO_UTIL_ReadFile
(
    IN  CONST CHAR *pcFileName, 
    IN  UINT64 ui64Seek, 
    IN  UINT   uiDataLen,
    OUT VOID  *pDataBuf
)
{
    UINT uiReadLen = 0;
    BISO_FILE_S *pstFile = NULL;
    
    if ((NULL == pcFileName) || (NULL == pDataBuf))
    {
        return BISO_ERR_NULL_PTR;
    }

    pstFile = BISO_PLAT_OpenExistFile(pcFileName);
    if (NULL == pstFile)
    {
        return BISO_ERR_OPEN_FILE;
    }

    BISO_PLAT_SeekFile(pstFile, ui64Seek, SEEK_SET);
    uiReadLen = (UINT)BISO_PLAT_ReadFile(pstFile, 1, uiDataLen, pDataBuf);
    if (uiReadLen != uiDataLen)
    {
        BISO_DIAG("Read Len %u, data len %u.", uiReadLen, uiDataLen);
        BISO_PLAT_CloseFile(pstFile);
        return BISO_ERR_READ_FILE;
    }

    BISO_PLAT_CloseFile(pstFile);
    return BISO_SUCCESS;
}


CHAR * BISO_UTIL_CopyStr
(
    IN  CONST CHAR *szSrc, 
    IN  UINT        uiSrcSize, 
    OUT CHAR       *szDest
)
{
    UINT i;
    UINT uiAllSpace = 1;

    for (i = uiSrcSize; i > 0; i--)
    {
        if ((0 != szSrc[i - 1]) && (' ' != szSrc[i - 1]))
        {
            uiAllSpace = 0;
            break;
        }

        if (' ' != szSrc[i - 1])
        {
            uiAllSpace = 0;
        }
    }

    if (i > 0)
    {
        memcpy(szDest, szSrc, i);
    }
    szDest[i] = 0;

    if (uiAllSpace == 1)
    {
        scnprintf(szDest, uiSrcSize, "*All Space*"); /* no safe */
    }

    if (szDest[0] == 0)
    {
        scnprintf(szDest, uiSrcSize, "*Empty*"); /* no safe */
    }

    return szDest;    
}

CHAR * BISO_UTIL_CopyUCS2Str
(
    IN  CONST CHAR *szSrc, 
    IN  UINT        uiSrcSize, 
    OUT CHAR       *szDest
)
{
    UINT i;
    
    memcpy(szDest, szSrc, uiSrcSize);

    for (i = 0; (i * 2 + 1) < uiSrcSize; i++)
    {
        szDest[i] = szDest[i * 2 + 1];
    }
    szDest[i] = 0;

    return szDest;    
}

VOID BISO_UTIL_PathProc(INOUT CHAR *pcPath, INOUT UINT *puiLen)
{
    UINT i;
    
    if ((NULL == pcPath) || (NULL == puiLen) || (0 == *puiLen))
    {
        return;
    }

    /* 把所有的\替换为/ */
    for (i = 0; i < *puiLen; i++)
    {
        if ('\\' == pcPath[i])
        {
            pcPath[i] = '/';
        }
    }

    /* 确保最后有1个/ */
    if ('/' != pcPath[*puiLen - 1])
    {
        pcPath[(*puiLen)++] = '/';
        pcPath[*puiLen] = 0;
    }
}

ULONG BISO_UTIL_PathSplit
(
    IN CONST CHAR *pcFullPath, 
    OUT UINT *puiDirNum, 
    OUT UINT *puiDirPos
)
{
    USHORT usPos = 0;
    USHORT usLen = 0;
    UINT uiDirNum = 0;
    CONST CHAR *pcLastPos = pcFullPath;
    CONST CHAR *pcCurPos = pcFullPath;
    
    DBGASSERT(NULL != pcFullPath);
    DBGASSERT(NULL != puiDirNum);
    DBGASSERT(NULL != puiDirPos);

    while (*pcCurPos)
    {
        if (('/' == *pcCurPos) || ('\\' == *pcCurPos))
        {
            usPos = pcLastPos - pcFullPath;
            usLen = pcCurPos - pcLastPos;
            if (usLen <= 0)
            {
                return BISO_ERR_FAILED;
            }
            
            puiDirPos[uiDirNum] = (UINT)((UINT)usPos << 16) | usLen;

            uiDirNum++;
            pcLastPos = pcCurPos + 1;
        }
        
        pcCurPos++;
    }

    usPos = pcLastPos - pcFullPath;
    usLen = pcCurPos - pcLastPos;
    if (usLen <= 0)
    {
        return BISO_ERR_FAILED;
    }    
    puiDirPos[uiDirNum++] = (UINT)((UINT)usPos << 16) | usLen;
    
    *puiDirNum = uiDirNum;
    return BISO_SUCCESS;
}

BISO_DIR_TREE_S * BISO_UTIL_FindLinkTgt(IN BISO_DIR_TREE_S *pstCurNode)
{
    UINT i = 0;
    UINT uiDirNum = 0;
    UINT auiDirPos[32];
    CHAR szDirName[1024];        
    USHORT usPos = 0;
    USHORT usLen = 0;
    CHAR *pcLink = NULL;
    BISO_DIR_TREE_S *pstFileList = NULL;
    BISO_DIR_TREE_S *pstRootDir = NULL;
    
    DBGASSERT(NULL != pstCurNode);

    /* 如果不是符号链接则返回自己 */
    if (BOOL_TRUE != BISO_DIR_TREE_IS_SYMLINK(pstCurNode))
    {
        return pstCurNode;
    }

    pcLink = pstCurNode->pstPosixInfo->pcLinkSrc;
    
    if ('/' == pcLink[0])
    {
        return NULL;
    }

    /* 把链接分割开 */
    if (BISO_SUCCESS != BISO_UTIL_PathSplit(pcLink, &uiDirNum, auiDirPos))
    {
        return NULL;
    }

    pstRootDir = pstCurNode->pstParent;

    /* 依次查找每一部分目录 */
    for (i = 0; (i < uiDirNum) && (NULL != pstCurNode)&& (NULL != pstRootDir); i++)
    {
        usPos = auiDirPos[i] >> 16;
        usLen = auiDirPos[i] & 0xFF;

        memcpy(szDirName, pcLink + usPos, usLen);
        szDirName[usLen] = 0;

        if (0 == BISO_PATH_STRCMP(szDirName, "."))
        {
            pstCurNode = pstCurNode->pstParent;
        }
        else if (0 == BISO_PATH_STRCMP(szDirName, ".."))
        {
            if (NULL == pstCurNode->pstParent)
            {
                return NULL;
            }
            pstCurNode = pstCurNode->pstParent->pstParent;
            pstRootDir = pstCurNode;
        }
        else
        {
            pstCurNode = pstRootDir->pstChild;
            pstFileList = pstRootDir->pstFileList;

            /* 先找当前所在目录下的文件夹 */
            while (pstCurNode)
            {
                if (0 == BISO_PATH_STRCMP(pstCurNode->szName, szDirName))
                {
                    pstRootDir = pstCurNode;
                    break;
                }
                pstCurNode = pstCurNode->pstNext;
            }

            /* 文件夹找不到就找文件 */
            if (NULL == pstCurNode)
            {
                pstCurNode = pstFileList;
                while (pstCurNode)
                {
                    if (0 == BISO_PATH_STRCMP(pstCurNode->szName, szDirName))
                    {
                        pstRootDir = NULL;
                        break;
                    }
                    pstCurNode = pstCurNode->pstNext;
                }
            }
        }
    }

    return pstCurNode;
}

ULONG BISO_MBUF_Append
(
    IN BISO_MBUF_S *pstMBuf, 
    IN UINT uiDataSize, 
    IN VOID *pData
)
{
    if ((NULL == pstMBuf) || (pstMBuf->uiCurBufNum >= BISO_MBUF_MAX_BLK))
    {
        return BISO_ERR_INVALID_PARAM;
    }

    pstMBuf->apucDataBuf[pstMBuf->uiCurBufNum] = (UCHAR *)BISO_MALLOC(uiDataSize);
    if (NULL == pstMBuf->apucDataBuf[pstMBuf->uiCurBufNum])
    {
        return BISO_ERR_ALLOC_MEM;
    }

    if (NULL == pData)
    {
        memset(pstMBuf->apucDataBuf[pstMBuf->uiCurBufNum], 0, uiDataSize);        
    }
    else
    {
        memcpy(pstMBuf->apucDataBuf[pstMBuf->uiCurBufNum], pData, uiDataSize);        
    }
    
    pstMBuf->auiBufSize[pstMBuf->uiCurBufNum] = uiDataSize;
    pstMBuf->uiTotDataSize += uiDataSize;
    pstMBuf->uiCurBufNum++;
    
    return BISO_SUCCESS;
}

VOID BISO_MBUF_Free(IN BISO_MBUF_S *pstMBuf)
{
    UINT i;
    if (NULL != pstMBuf)
    {
        for (i = 0; i < pstMBuf->uiCurBufNum; i++)
        {
            BISO_FREE(pstMBuf->apucDataBuf[i]);
        }
        memset(pstMBuf, 0, sizeof(BISO_MBUF_S));
    }
}

VOID BISO_MBUF_CopyToBuf(IN CONST BISO_MBUF_S *pstMBuf, OUT VOID *pDataBuf)
{
    UINT i;
    UCHAR *pucDataBuf = (UCHAR *)pDataBuf;
    
    if ((NULL != pstMBuf) && (NULL != pucDataBuf))
    {
        for (i = 0; i < pstMBuf->uiCurBufNum; i++)
        {
            if (NULL != pstMBuf->apucDataBuf[i])
            {
                memcpy(pucDataBuf, pstMBuf->apucDataBuf[i], pstMBuf->auiBufSize[i]);
                pucDataBuf += pstMBuf->auiBufSize[i];
            }
        }
    }
}

VOID BISO_MBUF_PULLUP(INOUT BISO_MBUF_S *pstMBuf)
{
    UINT uiSize = 0;
    VOID *pData = NULL;
    
    DBGASSERT(NULL != pstMBuf);

    if (pstMBuf->uiCurBufNum <= 1)
    {
        return;
    }

    uiSize = pstMBuf->uiTotDataSize;
    pData = BISO_MALLOC(uiSize);
    if (NULL == pData)
    {
        return;
    }

    BISO_MBUF_CopyToBuf(pstMBuf, pData);
    BISO_MBUF_Free(pstMBuf);

    memset(pstMBuf, 0, sizeof(BISO_MBUF_S));
    pstMBuf->apucDataBuf[0] = (UCHAR *)pData;
    pstMBuf->auiBufSize[0]  = uiSize;
    pstMBuf->uiTotDataSize  = uiSize;
    pstMBuf->uiCurBufNum    = 1;

    return;
}

BISO_QUEUE_S * BISO_QUEUE_Create(VOID)
{
    BISO_DLL_S *pstSLL = (BISO_DLL_S *)BISO_ZALLOC(sizeof(BISO_DLL_S));
    if (NULL != pstSLL)
    {
        BISO_DLL_Init(pstSLL);
    }
    return (BISO_QUEUE_S *)pstSLL;
}

VOID BISO_QUEUE_Destroy(IN BISO_QUEUE_S *pstQueue)
{
    BISO_DLL_Free(pstQueue);
    BISO_FREE(pstQueue);    
}

VOID BISO_QUEUE_Push(IN BISO_QUEUE_S *pstQueue, IN VOID *pData)
{
    BISO_QUEUE_NODE_S *pstNode = NULL;
    
    pstNode = (BISO_QUEUE_NODE_S *)BISO_DLL_Last(pstQueue);

    /* 当前节点已满需要扩展新内存节点 */
    if ((NULL == pstNode) || (BISO_QUEUE_PTR_NUM == pstNode->usLast))
    {
        pstNode = (BISO_QUEUE_NODE_S *)BISO_ZALLOC(sizeof(BISO_QUEUE_NODE_S));
        if (NULL == pstNode)
        {
            return;
        }
        BISO_DLL_AddTail(pstQueue, (BISO_DLL_NODE_S *)pstNode);
    }

    /* Last往前走一步 */
    pstNode = (BISO_QUEUE_NODE_S *)BISO_DLL_Last(pstQueue);
    pstNode->apList[pstNode->usLast++] = pData;
}

VOID * BISO_QUEUE_PopHead(IN BISO_QUEUE_S *pstQueue)
{
    VOID *pData = NULL;
    BISO_QUEUE_NODE_S *pstNode = NULL;
    
    pstNode = (BISO_QUEUE_NODE_S *)BISO_DLL_First(pstQueue);
    if (NULL == pstNode)
    {
        return NULL;
    }

    /* First往前走一步 */
    pData = pstNode->apList[pstNode->usFirst++];

    /* 该节点已空,则摘除节点,释放内存 */
    if (pstNode->usFirst == pstNode->usLast)
    {
        BISO_DLL_DelHead(pstQueue);
        BISO_FREE(pstNode);
    }

    return pData;
}

VOID * BISO_QUEUE_PopTail(IN BISO_QUEUE_S *pstQueue)
{
    VOID *pData = NULL;
    BISO_QUEUE_NODE_S *pstNode = NULL;
    
    pstNode = (BISO_QUEUE_NODE_S *)BISO_DLL_Last(pstQueue);
    if ((NULL == pstNode) || (0 == pstNode->usLast))
    {
        return NULL;
    }

    /* Last往后退一步 */
    pstNode->usLast--;
    pData = pstNode->apList[pstNode->usLast];

    /* 该节点已空,则摘除节点,释放内存 */
    if (pstNode->usFirst == pstNode->usLast)
    {
        BISO_DLL_DelTail(pstQueue);
        BISO_FREE(pstNode);
    }

    return pData;
}

UINT64 BISO_UTIL_WholeFile2Buf(IN CONST CHAR *szFileName, OUT UCHAR *pucBuf)
{
    UINT uiFileSize;
    UINT uiReadSize;
    BISO_FILE_S *pstFile = BISO_PLAT_OpenExistFile(szFileName);

    uiFileSize = BISO_PLAT_GetFileSize(szFileName);
    uiReadSize = BISO_PLAT_ReadFile(pstFile, 1, uiFileSize, pucBuf);

    BISO_PLAT_CloseFile(pstFile);

    return uiReadSize;
}