#include "FileUtil.h"

#define NAME_MAX 255

int FileMove(const char *pDestinationDir, const char *pSourceDir, const char *pFilename)
{
    char szSourceFilename[MAX_STRING_SIZE + 1];
    char szDestinationFilename[MAX_STRING_SIZE + 1];

#ifdef _WIN32
    snprintf(szSourceFilename, sizeof(szSourceFilename), "%s\\%s", pSourceDir, pFilename);
    snprintf(szDestinationFilename, sizeof(szDestinationFilename), "%s\\%s", pDestinationDir, pFilename);
    if (MoveFile(szSourceFilename, szDestinationFilename) == 0)
    {
        // use FormatMessage & GetLastError
        TCHAR szErrorMsg[MAX_STRING_SIZE + 1];
        FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                       NULL,
                       GetLastError(),
                       MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                       szErrorMsg,
                       sizeof(szErrorMsg),
                       NULL);
        WriteTraceLog(LogType_ErrorOut, LogLevel_Debug, __FILE__, __LINE__, __FUNCTION__, "MoveFile error[%s]", szErrorMsg);
        return FILE_ERR_COPY;
    }
    else
    {
        return FILE_ERR_SUCCESS;
    }

#else
    int nInputFD;
    int nOutputFD;
    int nResult;
    off_t nBytesCopied;
    struct stat sFileState;

    nResult = rename(pDestinationDir, pFilename);

    if (nResult != 0)
    {
        nResult = errno;
        if (nResult == EXDEV)
        {
            snprintf(szSourceFilename, sizeof(szSourceFilename), "%s%c%s", pSourceDir, PATH_SEPERATOR, pFilename);
            if ((nInputFD = open(szSourceFilename, O_RDONLY)) == -1)
            {
                return FILE_ERR_SOURCEFILE_OPEN;
            }
            snprintf(szDestinationFilename, sizeof(szDestinationFilename), "%s%c%s", pDestinationDir, PATH_SEPERATOR, pFilename);
            if ((nOutputFD = creat(szDestinationFilename, 0660)) == -1)
            {
                close(nInputFD);
                return FILE_ERR_DESTINATIONFILE_OPEN;
            }
            nBytesCopied = 0;
            SafeMemSet(&sFileState, sizeof(sFileState), 0, sizeof(sFileState));
            fstat(nInputFD, &sFileState);
            nResult = sendfile(nOutputFD, nInputFD, &nBytesCopied, sFileState.st_size);
            if (nResult == -1)
            {
                WriteTraceLog(LogType_ErrorOut, LogLevel_Debug, __FILE__, __LINE__, __FUNCTION__, "sendfile error[%s]", strerror(errno));
                close(nInputFD);
                close(nOutputFD);
                return FILE_ERR_COPY;
            }
            close(nInputFD);
            close(nOutputFD);
        }
        else
        {
            WriteTraceLog(LogType_ErrorOut, LogLevel_Error, __FILE__, __LINE__, __FUNCTION__, "File Move Error[%s]", strerror(nResult));
        }
    }

    return FILE_ERR_SUCCESS;
#endif
}

int MWGetFileSize(const char *pFilename, int nFilenameSize, int *pnFileSize)
{
    struct stat sFileState;
    int nResult;
    int nErrorNo;

    if (pnFileSize == NULL)
    {
        WriteTraceLog(LogType_ErrorOut, LogLevel_Trace, __FILE__, __LINE__, __FUNCTION__, "Filesize parameter is null");
        return FILE_ERR_NULL_POINTER;
    }

    if (pFilename == NULL)
    {
        WriteTraceLog(LogType_ErrorOut, LogLevel_Trace, __FILE__, __LINE__, __FUNCTION__, "Filename parameter is null");
        return FILE_ERR_NULL_POINTER;
    }

    nResult = CheckNULL(pFilename, nFilenameSize);
    if (nResult == STR_ERR_NOT_EXIST_NULL)
    {
        WriteTraceLog(LogType_ErrorOut, LogLevel_Trace, __FILE__, __LINE__, __FUNCTION__, "Filename is not null terminated string");
        return FILE_ERR_FILENAME;
    }

    nResult = stat(pFilename, &sFileState);

    if (nResult == -1)
    {
        nErrorNo = errno;
        switch (nErrorNo)
        {
        case ENOENT:
        case EFAULT:
        case ENAMETOOLONG:
            WriteTraceLog(LogType_ErrorOut, LogLevel_Trace, __FILE__, __LINE__, __FUNCTION__, "Path error[Msg:%s, PATH_MAX:%d,NAME_MAX:%d]", strerror(nErrorNo), PATH_MAX, NAME_MAX);
            return FILE_ERR_FILENAME;
        case EACCES:
        case ELOOP:
        case ENOTDIR:
            WriteTraceLog(LogType_ErrorOut, LogLevel_Trace, __FILE__, __LINE__, __FUNCTION__, "Filename error[%s]", strerror(nErrorNo));
            return FILE_ERR_SOURCEFILE_OPEN;
        case ENOMEM:
            WriteTraceLog(LogType_ErrorOut, LogLevel_Trace, __FILE__, __LINE__, __FUNCTION__, "Memory error[%s]", strerror(nErrorNo));
            return FILE_ERR_MEM;
        case EOVERFLOW:
            WriteTraceLog(LogType_ErrorOut, LogLevel_Trace, __FILE__, __LINE__, __FUNCTION__, "Application compiled on a 32-bit platform. To solve the problem compile with -D_FILE_OFFSET_BITS=64[%s]", strerror(nErrorNo));
            return FILE_ERR_PLATFORM;
        }
    }

    if (!S_ISREG(sFileState.st_mode))
    {
        return FILE_ERR_FILENAME_IS_NOT_FILE;
    }
    *pnFileSize = sFileState.st_size;
    return FILE_ERR_SUCCESS;

}

int FileOpenforContinuousTransfer(FILE **ppFile, const char *pszFilename)
{
    int nResult;
    char szErrStringBuffer[MAX_STRING_SIZE + 1];
#ifdef __SECURE_CODING
    nResult = fopen_s(ppFile, pszFilename, "r+");
    if (nResult != EOK)
    {
        nResult = fopen_s(ppFile, pszFilename, "w+");
        if (nResult != EOK)
        {
            strerror_s(szErrStringBuffer, sizeof(szErrStringBuffer), nResult);
            WriteTraceLog(LogType_ErrorOut, LogLevel_Trace, __FILE__, __LINE__, __FUNCTION__, "Data file open error[File:%s, Msg:%s]", pszFilename, szErrStringBuffer);
            return FILE_ERR_DESTINATIONFILE_OPEN;
        }
    }
#else
    *ppFile = fopen(pszFilename, "r+");
    if (*ppFile == NULL)
    {
        *ppFile = fopen(pszFilename, "w+");
        if (*ppFile == NULL)
        {
            nResult = errno;
            WriteTraceLog(LogType_ErrorOut, LogLevel_Trace, __FILE__, __LINE__, __FUNCTION__, "Data file open error[File:%s, Msg:%s]", pszFilename, strerror(nResult));
            return FILE_ERR_DESTINATIONFILE_OPEN;
        }
    }
#endif

    return FILE_ERR_SUCCESS;
}


