/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.

// Description : Cross platform DXGL helper types


#ifndef __GLCROSSPLATFORM__
#define __GLCROSSPLATFORM__

#include <algorithm>
#include <cfloat>

using namespace std;

#if defined(_MSC_VER)
#define ILINE __forceinline
#elif defined(__GNUC__) || defined(__clang__)
#define ILINE __attribute__((always_inline))
#else
#define ILINE inline
#endif

#if !defined(_MSC_VER)

inline int sprintf_s(char* buffer, size_t size, const char* format, ...)
{
#if defined(LINUX) || defined(APPLE)
    va_list args;
    va_start(args, format);
    int err = vsnprintf(buffer, size, format, args);
    va_end(args);
    return err;
#else
#error "Not implemented on this platform"
#endif
}

template <size_t size>
int sprintf_s(char (&buffer)[size], const char* format, ...)
{
#if defined(LINUX) || defined(APPLE)
    va_list args;
    va_start(args, format);
    int err = vsnprintf(buffer, size, format, args);
    va_end(args);
    return err;
#else
#error "Not implemented on this platform"
#endif
}

inline int strcpy_s(char* dst, size_t size, const char* src)
{
#if defined(LINUX) || defined(APPLE)
    strncpy(dst, src, size);
    dst[size - 1] = 0;
    return 0;
#else
#error "Not implemented on this platform"
#endif
}

template <size_t SIZE>
int strcpy_s(char (&dst)[SIZE], const char* src)
{
    return strcpy_s(dst, SIZE, src);
}

inline void ZeroMemory(void* pPtr, int nSize)
{
    memset(pPtr, 0, nSize);
}

#endif //!defined(_MSC_VER)

namespace NCryOpenGL
{
    inline void* Malloc(size_t size)
    {
        return malloc(size)
    }

    inline void* Calloc(size_t num, size_t size)
    {
        return calloc(num, size)
    }

    inline void* Realloc(void* memblock, size_t size)
    {
        return realloc(memblock, size)
    }

    inline void Free(void* memblock)
    {
        free(memblock)
    }

    void* CreateTLS();
    void SetTLSValue(void* pTLSHandle, void* pValue);
    void* GetTLSValue(void* pTLSHandle);
    void DestroyTLS(void* pTLSHandle);
    bool MakeDir(const char* szDirName);

    namespace NCrossPlatformImpl
    {
        struct SAutoLog
        {
            FILE* m_pFile;

            SAutoLog(const char* szFileName)
            {
                m_pFile = fopen("DXGL.log", "w");
            }

            ~SAutoLog()
            {
                if (m_pFile != NULL)
                {
                    fclose(m_pFile);
                }
            }
        };

        struct SAutoTLSSlot
        {
            void* m_pTLSHandle;

            SAutoTLSSlot()
            {
                m_pTLSHandle = CreateTLS();
            }

            ~SAutoTLSSlot()
            {
                DestroyTLS(m_pTLSHandle);
            }
        };

        inline uint32 CRC32Reflect(unsigned int ref, char ch)
        {
            unsigned int value = 0;

            // Swap bit 0 for bit 7
            // bit 1 for bit 6, etc.
            for (int i = 1; i < (ch + 1); i++)
            {
                if (ref & 1)
                {
                    value |= 1 << (ch - i);
                }
                ref >>= 1;
            }
            return value;
        }

        extern SAutoLog g_kLog;
        extern SAutoTLSSlot g_kCRCTable;
    }

#define PRINTF_PARAMS(a, b)

    void LogMessage(ELogSeverity eSeverity, const char* szFormat, ...) PRINTF_PARAMS(2, 3);
    inline void LogMessage(ELogSeverity eSeverity, const char* szFormat, ...)
    {
        if (NCrossPlatformImpl::g_kLog.m_pFile != NULL)
        {
            va_list kArgs;
            va_start(kArgs, szFormat);
            vfprintf(NCrossPlatformImpl::g_kLog.m_pFile, szFormat, kArgs);
            va_end(kArgs);
            fputc('\n', NCrossPlatformImpl::g_kLog.m_pFile);
            fflush(NCrossPlatformImpl::g_kLog.m_pFile);
        }
    }

    inline uint32 GetCRC32(const char* pData, size_t uSize, uint32 uCRC)
    {
        uint32* pCRCTable(static_cast<uint32*>(GetTLSValue(NCrossPlatformImpl::g_kCRCTable.m_pTLSHandle)));
        if (pCRCTable == NULL)
        {
            pCRCTable = new uint32[256];
            SetTLSValue(NCrossPlatformImpl::g_kCRCTable.m_pTLSHandle, pCRCTable);

            // This is the official polynomial used by CRC-32
            // in PKZip, WinZip and Ethernet.
            unsigned int ulPolynomial = 0x04c11db7;

            // 256 values representing ASCII character codes.
            for (int i = 0; i <= 0xFF; i++)
            {
                pCRCTable[i] = NCrossPlatformImpl::CRC32Reflect(i, 8) << 24;
                for (int j = 0; j < 8; j++)
                {
                    pCRCTable[i] = (pCRCTable[i] << 1) ^ (pCRCTable[i] & (1U << 31) ? ulPolynomial : 0);
                }
                pCRCTable[i] = NCrossPlatformImpl::CRC32Reflect(pCRCTable[i], 32);
            }
        }

        int len;
        unsigned char* buffer;

        // Get the length.
        len = uSize;

        // Save the text in the buffer.
        buffer = (unsigned char*)pData;
        // Perform the algorithm on each character in the string, using the lookup table values.

        while (len--)
        {
            uCRC = (uCRC >> 8) ^ pCRCTable[(uCRC & 0xFF) ^ *buffer++];
        }
        // Exclusive OR the result with the beginning value.
        return uCRC ^ 0xffffffff;
    }

    using std::string;

    struct STraceFile
    {
        STraceFile()
            : m_pFile(NULL)
        {
        }

        ~STraceFile()
        {
            if (m_pFile != NULL)
            {
                fclose(m_pFile);
            }
        }

        bool Open(const char* szFileName, bool bBinary)
        {
            if (m_pFile != NULL)
            {
                return false;
            }

            const char* szDirName = "DXGLTrace";
            do
            {
                char acFullPath[256];
                sprintf_s(acFullPath, "%s/%s", szDirName, szFileName);
                const char* szMode(bBinary ? "wb" : "w");
                m_pFile = fopen(acFullPath, szMode);
                if (m_pFile != NULL)
                {
                    return true;
                }
            } while (MakeDir(szDirName));

            return false;
        }

        void Write(const void* pvData, uint32 uSize)
        {
            fwrite(pvData, (size_t)uSize, 1, m_pFile);
        }

        void Printf(const char* szFormat, ...)
        {
            va_list kArgs;
            va_start(kArgs, szFormat);
            vfprintf(m_pFile, szFormat, kArgs);
            va_end(kArgs);
        }

        FILE* m_pFile;
    };

    inline void RegisterConfigVariable(const char*, int* piVariable, int iValue)
    {
        *piVariable = iValue;
    }

#define IL2VAL(mask, shift)         \
    c |= ((x & mask) != 0) * shift; \
    x >>= ((x & mask) != 0) * shift

    inline uint32 IntegerLog2(uint32 x)
    {
        uint32 c = 0;
        IL2VAL(0xffff0000u, 16);
        IL2VAL(0xff00, 8);
        IL2VAL(0xf0, 4);
        IL2VAL(0xc, 2);
        IL2VAL(0x2, 1);
        return c;
    }

#undef IL2VAL

    template <typename T>
    inline size_t countTrailingZeroes(T v)
    {
        size_t n = 0;

        v = ~v & (v - 1);
        while (v)
        {
            ++n;
            v >>= 1;
        }

        return n;
    }

    template<typename DestinationType, typename SourceType>
    inline DestinationType alias_cast(SourceType pPtr)
    {
        union
        {
            SourceType pSrc;
            DestinationType pDst;
        } conv_union;
        conv_union.pSrc = pPtr;
        return conv_union.pDst;
    }

    inline void* cryMemcpy(void* pDst, const void* pSrc, size_t uLength)
    {
        return memcpy(pDst, pSrc, uLength);
    }

    inline void PushProfileLabel(const char* szName)
    {
        DXGLProfileLabelPush(szName);
    }

    inline void PopProfileLabel(const char* szName)
    {
        DXGLProfileLabelPop(szName);
    }
}

#endif //__GLCROSSPLATFORM__