// Copyright 2014 PDFium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
 
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com

#include "../../include/fxcrt/fx_basic.h"
#if _FXM_PLATFORM_ != _FXM_PLATFORM_WINDOWS_
#include <sys/types.h>
#include <dirent.h>
#else
#include <direct.h>
#endif
CFX_PrivateData::~CFX_PrivateData()
{
    ClearAll();
}
void FX_PRIVATEDATA::FreeData()
{
    if (m_pData == NULL) {
        return;
    }
    if (m_bSelfDestruct) {
        delete (CFX_DestructObject*)m_pData;
    } else if (m_pCallback) {
        m_pCallback(m_pData);
    }
}
void CFX_PrivateData::AddData(FX_LPVOID pModuleId, FX_LPVOID pData, PD_CALLBACK_FREEDATA callback, FX_BOOL bSelfDestruct)
{
    if (pModuleId == NULL) {
        return;
    }
    FX_PRIVATEDATA* pList = m_DataList.GetData();
    int count = m_DataList.GetSize();
    for (int i = 0; i < count; i ++) {
        if (pList[i].m_pModuleId == pModuleId) {
            pList[i].FreeData();
            pList[i].m_pData = pData;
            pList[i].m_pCallback = callback;
            return;
        }
    }
    FX_PRIVATEDATA data = {pModuleId, pData, callback, bSelfDestruct};
    m_DataList.Add(data);
}
void CFX_PrivateData::SetPrivateData(FX_LPVOID pModuleId, FX_LPVOID pData, PD_CALLBACK_FREEDATA callback)
{
    AddData(pModuleId, pData, callback, FALSE);
}
void CFX_PrivateData::SetPrivateObj(FX_LPVOID pModuleId, CFX_DestructObject* pObj)
{
    AddData(pModuleId, pObj, NULL, TRUE);
}
FX_BOOL CFX_PrivateData::RemovePrivateData(FX_LPVOID pModuleId)
{
    if (pModuleId == NULL) {
        return FALSE;
    }
    FX_PRIVATEDATA* pList = m_DataList.GetData();
    int count = m_DataList.GetSize();
    for (int i = 0; i < count; i ++) {
        if (pList[i].m_pModuleId == pModuleId) {
            m_DataList.RemoveAt(i);
            return TRUE;
        }
    }
    return FALSE;
}
FX_LPVOID CFX_PrivateData::GetPrivateData(FX_LPVOID pModuleId)
{
    if (pModuleId == NULL) {
        return NULL;
    }
    FX_PRIVATEDATA* pList = m_DataList.GetData();
    int count = m_DataList.GetSize();
    for (int i = 0; i < count; i ++) {
        if (pList[i].m_pModuleId == pModuleId) {
            return pList[i].m_pData;
        }
    }
    return NULL;
}
void CFX_PrivateData::ClearAll()
{
    FX_PRIVATEDATA* pList = m_DataList.GetData();
    int count = m_DataList.GetSize();
    for (int i = 0; i < count; i ++) {
        pList[i].FreeData();
    }
    m_DataList.RemoveAll();
}
void FX_atonum(FX_BSTR strc, FX_BOOL& bInteger, void* pData)
{
    if (FXSYS_memchr(strc.GetPtr(), '.', strc.GetLength()) == NULL) {
        bInteger = TRUE;
        int cc = 0, integer = 0;
        FX_LPCSTR str = strc.GetCStr();
        int len = strc.GetLength();
        FX_BOOL bNegative = FALSE;
        if (str[0] == '+') {
            cc++;
        } else if (str[0] == '-') {
            bNegative = TRUE;
            cc++;
        }
        while (cc < len) {
            if (str[cc] < '0' || str[cc] > '9') {
                break;
            }
            integer = integer * 10 + str[cc] - '0';
            if (integer < 0) {
                break;
            }
            cc ++;
        }
        if (bNegative) {
            integer = -integer;
        }
        *(int*)pData = integer;
    } else {
        bInteger = FALSE;
        *(FX_FLOAT*)pData = FX_atof(strc);
    }
}
FX_FLOAT FX_atof(FX_BSTR strc)
{
    if (strc.GetLength() == 0) {
        return 0.0;
    }
    int cc = 0;
    FX_BOOL bNegative = FALSE;
    FX_LPCSTR str = strc.GetCStr();
    int len = strc.GetLength();
    if (str[0] == '+') {
        cc++;
    } else if (str[0] == '-') {
        bNegative = TRUE;
        cc++;
    }
    while (cc < len) {
        if (str[cc] != '+' && str[cc] != '-') {
            break;
        }
        cc ++;
    }
    FX_FLOAT value = 0;
    while (cc < len) {
        if (str[cc] == '.') {
            break;
        }
        value = value * 10 + str[cc] - '0';
        cc ++;
    }
    static const FX_FLOAT fraction_scales[] = {0.1f, 0.01f, 0.001f, 0.0001f, 0.00001f, 0.000001f,
                                               0.0000001f, 0.00000001f, 0.000000001f, 0.0000000001f, 0.00000000001f
                                              };
    int scale = 0;
    if (cc < len && str[cc] == '.') {
        cc ++;
        while (cc < len) {
            value += fraction_scales[scale] * (str[cc] - '0');
            scale ++;
            if (scale == sizeof fraction_scales / sizeof(FX_FLOAT)) {
                break;
            }
            cc ++;
        }
    }
    return bNegative ? -value : value;
}
static FX_BOOL FX_IsDigit(FX_BYTE ch)
{
    return (ch >= '0' && ch <= '9') ? TRUE : FALSE;
}
static FX_BOOL FX_IsXDigit(FX_BYTE ch)
{
    return (FX_IsDigit(ch) || (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f')) ? TRUE : FALSE;
}
static FX_BYTE FX_MakeUpper(FX_BYTE ch)
{
    if (ch < 'a' || ch > 'z') {
        return ch;
    }
    return ch - 32;
}
static int FX_HexToI(FX_BYTE ch)
{
    ch = FX_MakeUpper(ch);
    return FX_IsDigit(ch) ? (ch - '0') : (ch - 55);
}
static const unsigned char url_encodeTable[128] = {
    1,  1,  1,  1,		1,  1,  1,  1,
    1,  1,  1,  1,		1,  1,  1,  1,
    1,  1,  1,  1,		1,  1,  1,  1,
    1,  1,  1,  1,		1,  1,  1,  1,
    1,  0,  1,  1,		0,  1,  0,  0,
    0,  0,  0,  0,		0,  0,  0,  0,
    0,  0,  0,  0,		0,  0,  0,  0,
    0,  0,  0,  0,		1,  0,  1,  0,
    0,  0,  0,  0,		0,  0,  0,  0,
    0,  0,  0,  0,		0,  0,  0,  0,
    0,  0,  0,  0,		0,  0,  0,  0,
    0,  0,  0,  1,		1,  1,  1,  0,
    1,  0,  0,  0,		0,  0,  0,  0,
    0,  0,  0,  0,		0,  0,  0,  0,
    0,  0,  0,  0,		0,  0,  0,  0,
    0,  0,  0,  1,		1,  1,  1,  1,
};
CFX_ByteString FX_UrlEncode(const CFX_WideString& wsUrl)
{
    const char arDigits[] = "0123456789ABCDEF";
    CFX_ByteString rUrl;
    int nLength = wsUrl.GetLength();
    for (int i = 0; i < nLength; i++) {
        FX_DWORD word = wsUrl.GetAt(i);
        if (word > 0x7F || url_encodeTable[word] == 1) {
            CFX_ByteString bsUri = CFX_ByteString::FromUnicode((FX_WORD)word);
            int nByte = bsUri.GetLength();
            for (int j = 0; j < nByte; j++) {
                rUrl += '%';
                FX_BYTE code = bsUri.GetAt(j);
                rUrl += arDigits[code >> 4];
                rUrl += arDigits[code & 0x0F];
            }
        } else {
            rUrl += CFX_ByteString::FromUnicode((FX_WORD)word);
        }
    }
    return rUrl;
}
CFX_WideString FX_UrlDecode(const CFX_ByteString& bsUrl)
{
    CFX_ByteString rUrl;
    int nLength = bsUrl.GetLength();
    for (int i = 0; i < nLength; i++) {
        if (i < nLength - 2 && bsUrl[i] == '%' && FX_IsXDigit(bsUrl[i + 1]) && FX_IsXDigit(bsUrl[i + 2])) {
            rUrl += (FX_HexToI(bsUrl[i + 1]) << 4 | FX_HexToI(bsUrl[i + 2]));
            i += 2;
        } else {
            rUrl += bsUrl[i];
        }
    }
    return CFX_WideString::FromLocal(rUrl);
}
CFX_ByteString FX_EncodeURI(const CFX_WideString& wsURI)
{
    const char arDigits[] = "0123456789ABCDEF";
    CFX_ByteString rURI;
    CFX_ByteString bsUri = wsURI.UTF8Encode();
    int nLength = bsUri.GetLength();
    for (int i = 0; i < nLength; i++) {
        FX_BYTE code = bsUri.GetAt(i);
        if (code > 0x7F || url_encodeTable[code] == 1) {
            rURI += '%';
            rURI += arDigits[code >> 4];
            rURI += arDigits[code & 0x0F];
        } else {
            rURI += code;
        }
    }
    return rURI;
}
CFX_WideString FX_DecodeURI(const CFX_ByteString& bsURI)
{
    CFX_ByteString rURI;
    int nLength = bsURI.GetLength();
    for (int i = 0; i < nLength; i++) {
        if (i < nLength - 2 && bsURI[i] == '%' && FX_IsXDigit(bsURI[i + 1]) && FX_IsXDigit(bsURI[i + 2])) {
            rURI += (FX_HexToI(bsURI[i + 1]) << 4 | FX_HexToI(bsURI[i + 2]));
            i += 2;
        } else {
            rURI += bsURI[i];
        }
    }
    return CFX_WideString::FromUTF8(rURI);
}
#if _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
class CFindFileData : public CFX_Object
{
public:
    virtual ~CFindFileData() {}
    HANDLE				m_Handle;
    FX_BOOL				m_bEnd;
};
class CFindFileDataA : public CFindFileData
{
public:
    virtual ~CFindFileDataA() {}
    WIN32_FIND_DATAA	m_FindData;
};
class CFindFileDataW : public CFindFileData
{
public:
    virtual ~CFindFileDataW() {}
    WIN32_FIND_DATAW	m_FindData;
};
#endif
void* FX_OpenFolder(FX_LPCSTR path)
{
#if _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
#ifndef _WIN32_WCE
    CFindFileDataA* pData = FX_NEW CFindFileDataA;
    if (!pData) {
        return NULL;
    }
#ifdef _FX_WINAPI_PARTITION_DESKTOP_
    pData->m_Handle = FindFirstFileA(CFX_ByteString(path) + "/*.*", &pData->m_FindData);
#else
    pData->m_Handle = FindFirstFileExA(CFX_ByteString(path) + "/*.*", FindExInfoStandard, &pData->m_FindData, FindExSearchNameMatch, NULL, 0);
#endif
#else
    CFindFileDataW* pData = FX_NEW CFindFileDataW;
    if (!pData) {
        return NULL;
    }
    pData->m_Handle = FindFirstFileW(CFX_WideString::FromLocal(path) + L"/*.*", &pData->m_FindData);
#endif
    if (pData->m_Handle == INVALID_HANDLE_VALUE) {
        delete pData;
        return NULL;
    }
    pData->m_bEnd = FALSE;
    return pData;
#else
    DIR* dir = opendir(path);
    return dir;
#endif
}
void* FX_OpenFolder(FX_LPCWSTR path)
{
#if _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
    CFindFileDataW* pData = FX_NEW CFindFileDataW;
    if (!pData) {
        return NULL;
    }
#ifdef _FX_WINAPI_PARTITION_DESKTOP_
    pData->m_Handle = FindFirstFileW(CFX_WideString(path) + L"/*.*", &pData->m_FindData);
#else
    pData->m_Handle = FindFirstFileExW(CFX_WideString(path) + L"/*.*", FindExInfoStandard, &pData->m_FindData, FindExSearchNameMatch, NULL, 0);
#endif
    if (pData->m_Handle == INVALID_HANDLE_VALUE) {
        delete pData;
        return NULL;
    }
    pData->m_bEnd = FALSE;
    return pData;
#else
    DIR* dir = opendir(CFX_ByteString::FromUnicode(path));
    return dir;
#endif
}
FX_BOOL FX_GetNextFile(void* handle, CFX_ByteString& filename, FX_BOOL& bFolder)
{
    if (handle == NULL) {
        return FALSE;
    }
#if _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
#ifndef _WIN32_WCE
    CFindFileDataA* pData = (CFindFileDataA*)handle;
    if (pData->m_bEnd) {
        return FALSE;
    }
    filename = pData->m_FindData.cFileName;
    bFolder = pData->m_FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
    if (!FindNextFileA(pData->m_Handle, &pData->m_FindData)) {
        pData->m_bEnd = TRUE;
    }
    return TRUE;
#else
    CFindFileDataW* pData = (CFindFileDataW*)handle;
    if (pData->m_bEnd) {
        return FALSE;
    }
    filename = CFX_ByteString::FromUnicode(pData->m_FindData.cFileName);
    bFolder = pData->m_FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
    if (!FindNextFileW(pData->m_Handle, &pData->m_FindData)) {
        pData->m_bEnd = TRUE;
    }
    return TRUE;
#endif
#elif defined(__native_client__)
    abort();
    return FALSE;
#else
    struct dirent *de = readdir((DIR*)handle);
    if (de == NULL) {
        return FALSE;
    }
    filename = de->d_name;
    bFolder = de->d_type == DT_DIR;
    return TRUE;
#endif
}
FX_BOOL FX_GetNextFile(void* handle, CFX_WideString& filename, FX_BOOL& bFolder)
{
    if (handle == NULL) {
        return FALSE;
    }
#if _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
    CFindFileDataW* pData = (CFindFileDataW*)handle;
    if (pData->m_bEnd) {
        return FALSE;
    }
    filename = pData->m_FindData.cFileName;
    bFolder = pData->m_FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
    if (!FindNextFileW(pData->m_Handle, &pData->m_FindData)) {
        pData->m_bEnd = TRUE;
    }
    return TRUE;
#elif defined(__native_client__)
    abort();
    return FALSE;
#else
    struct dirent *de = readdir((DIR*)handle);
    if (de == NULL) {
        return FALSE;
    }
    filename = CFX_WideString::FromLocal(de->d_name);
    bFolder = de->d_type == DT_DIR;
    return TRUE;
#endif
}
void FX_CloseFolder(void* handle)
{
    if (handle == NULL) {
        return;
    }
#if _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
    CFindFileData* pData = (CFindFileData*)handle;
    FindClose(pData->m_Handle);
    delete pData;
#else
    closedir((DIR*)handle);
#endif
}
FX_WCHAR FX_GetFolderSeparator()
{
#if _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
    return '\\';
#else
    return '/';
#endif
}