/***************************************************************************
*                                                                          *
*  Copyright (C) Golaem S.A.  All Rights Reserved.                         *
*                                                                          *
****************************************************************************/

#pragma once

//NOTE: all predefine C/C++ compiler macros: http://sourceforge.net/p/predef/wiki/Home/

#define GLM_COMPILER_MSVC 1
#define GLM_COMPILER_GCC 2

#if defined(_MSC_VER)
#define GLM_COMPILER GLM_COMPILER_MSVC
#define GLM_COMPILER_VERSION _MSC_VER
#define GLM_IS_COMPILER_MSVC 1
#define GLM_IS_COMPILER_GCC 0

#define GLM_DO_PRAGMA(x) __pragma(#x)
#define GLM_PRAGMA_MESSAGE(x) __pragma(message(#x))
//GLM_PRAGMA_MESSAGE("Platform Compiler is Microsoft Visual C++.")
#elif defined(__GNUC__)
#define GLM_COMPILER GLM_COMPILER_GCC
#define GLM_COMPILER_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100)
#define GLM_IS_COMPILER_MSVC 0
#define GLM_IS_COMPILER_GCC 1

#define GLM_DO_PRAGMA(x) _Pragma(#x)
#define GLM_PRAGMA_MESSAGE(x) GLM_DO_PRAGMA(message(#x))
//GLM_PRAGMA_MESSAGE("Platform Compiler is GCC.")
#else
#error "This compiler is not supported."
#endif

#define GLM_PLATFORM_WINDOWS 1
#define GLM_PLATFORM_LINUX 2
#define GLM_PLATFORM_MACOSX 3

#if defined(__WIN32__) || defined(_WIN32) || defined(__WIN64__) || defined(_WIN64) || defined(WIN32)
#define GLM_IS_PLATFORM_WINDOWS 1
#define GLM_IS_PLATFORM_LINUX 0
#define GLM_IS_PLATFORM_MACOSX 0
#define GLM_PLATFORM GLM_PLATFORM_WINDOWS
//GLM_PRAGMA_MESSAGE("Platform OS is Windows.")
#elif defined(__linux__) || defined(LINUX)
#define GLM_IS_PLATFORM_WINDOWS 0
#define GLM_IS_PLATFORM_LINUX 1
#define GLM_IS_PLATFORM_MACOSX 0
#define GLM_PLATFORM GLM_PLATFORM_LINUX
//GLM_PRAGMA_MESSAGE("Platform OS is Linux.")
#elif (defined(__APPLE__) && defined(__MACH__)) || defined(MACOSX)
#define GLM_IS_PLATFORM_WINDOWS 0
#define GLM_IS_PLATFORM_LINUX 0
#define GLM_IS_PLATFORM_MACOSX 1
#define GLM_PLATFORM GLM_PLATFORM_MACOSX
//GLM_PRAGMA_MESSAGE("Platform OS is MacOSX.")
#else
#error "This platform is not supported."
#endif

#define GLM_MEMORY_ADDRESS_SPACE_32BIT 1
#define GLM_MEMORY_ADDRESS_SPACE_64BIT 2

#if defined(__x86_64__) || defined(_M_X64) || defined(__ia64__) || defined(_M_IA64) || defined(__powerpc64__)
#define GLM_IS_64BIT 1
#define GLM_IS_32BIT 0
#define GLM_MEMORY_ADDRESS_SPACE GLM_MEMORY_ADDRESS_SPACE_64BIT
//GLM_PRAGMA_MESSAGE("Using 64bit memory address space.")
#else
#define GLM_IS_64BIT 0
#define GLM_IS_32BIT 1
#define GLM_MEMORY_ADDRESS_SPACE GLM_MEMORY_ADDRESS_SPACE_32BIT
//GLM_PRAGMA_MESSAGE("Using 32bit memory address space.")
#endif

#if GLM_IS_COMPILER_MSVC
#ifdef __cplusplus
#define GLM_FULLY_SPECIALIZED_TEMPLATE_INLINE inline
#define GLM_C_INLINE inline
#else
#define GLM_FULLY_SPECIALIZED_TEMPLATE_INLINE __inline
#define GLM_C_INLINE __inline
#endif
#define GLM_FORCE_INLINE __forceinline
#define GLM_NO_INLINE __declspec(noinline)

#define GLM_TLS __declspec(thread)

#define GLM_ALIGNOF __alignof
#define GLM_ALIGN(n) __declspec(align(n))

//NOTE: apparently 16 bytes alignment has been disabled on 32bit platforms for performance reason
#if GLM_IS_64BIT
#define GLM_ALIGN_16 __declspec(align(16))
#elif GLM_IS_32BIT
#define GLM_ALIGN_16
#endif

//NOTE: using same default alignment as malloc implementation in MSVC https://msdn.microsoft.com/en-us/library/ycsb6wwf.aspx
#if GLM_IS_64BIT
#define GLM_ALIGN_DEFAULT 16
#elif GLM_IS_32BIT
#define GLM_ALIGN_DEFAULT 8
#endif

#elif GLM_IS_COMPILER_GCC
#define GLM_FULLY_SPECIALIZED_TEMPLATE_INLINE inline
#define GLM_C_INLINE static inline
#define GLM_FORCE_INLINE inline __attribute__((always_inline))
#define GLM_NO_INLINE __attribute__((noinline))

#define GLM_TLS __thread

#define GLM_ALIGNOF __alignof__
#define GLM_ALIGN(n) __attribute__((aligned(n)))
#define GLM_ALIGN_16 __attribute__((aligned(16)))

//NOTE: using same default alignment as malloc implementation in GCC http://www.gnu.org/software/libc/manual/html_node/Aligned-Memory-Blocks.html
#if GLM_IS_64BIT
#define GLM_ALIGN_DEFAULT 16
#elif GLM_IS_32BIT
#define GLM_ALIGN_DEFAULT 8
#endif
#else
#error "This platform is not supported."
#endif

//HACK: cache line size depends on the CPU architecture
// since x86 architecture CPUs use 64 bytes cache lines, for now assuming 64 bytes
#if (GLM_PLATFORM_WINDOWS || GLM_PLATFORM_LINUX || GLM_PLATFORM_MACOSX)
#define GLM_MEMORY_CACHE_LINE_SIZE 64
#else
#error "This platform is not supported."
#endif

#if GLM_IS_COMPILER_MSVC
#define GLM_DEPRECATED_MESSAGE(message) __declspec(deprecated(message))
#define GLM_DEPRECATED __declspec(deprecated)
#else
// obfuscate Linux warnings
//#define GLM_DEPRECATED_MESSAGE(message) __attribute__ ((deprecated))
//#define GLM_DEPRECATED __attribute__ ((deprecated))
#define GLM_DEPRECATED_MESSAGE(message)
#define GLM_DEPRECATED
#endif

//NOTE: introducing macros to help declaring a multi line macro (See http://cnicholson.net/2009/03/stupid-c-tricks-dowhile0-and-c4127/)
#if GLM_IS_COMPILER_MSVC
#define GLM_MULTI_LINE_MACRO_BEGIN \
    do                             \
    {
#define GLM_MULTI_LINE_MACRO_END          \
    __pragma(warning(push))               \
        __pragma(warning(disable : 4127)) \
    }                                     \
    while (0)                             \
    __pragma(warning(pop))
#else
#define GLM_MULTI_LINE_MACRO_BEGIN \
    do                             \
    {
#define GLM_MULTI_LINE_MACRO_END \
    ;                            \
    }                            \
    while (0)
#endif

#define GLM_UNUSED(x)                \
    GLM_MULTI_LINE_MACRO_BEGIN(void) \
    sizeof(x);                       \
    GLM_MULTI_LINE_MACRO_END

//NOTE: introducing another macro to help getting rid of
//warning C4100: unreferenced formal parameter
//warning C4189: local variable is initialized but not referenced
#define GLM_UNREFERENCED(x)          \
    GLM_MULTI_LINE_MACRO_BEGIN(void) \
    (x);                             \
    GLM_MULTI_LINE_MACRO_END
//NOTE: some other implementation suggestions I found on the web:
//( *(volatile typeof(x) *)&(x) = (x); )
//for GCC: UNUSED_ ## x __attribute__((unused))

#define GLM_DISALLOW_COPY_AND_ASSIGN(TypeName) \
    TypeName(const TypeName&);                 \
    TypeName& operator=(const TypeName&);

#define GLM_MAX_PATH 1024

#if !defined(GLM_THREAD_STACK_SIZE)
#define GLM_THREAD_STACK_SIZE 1 * 1024 * 1024
#endif

//NOTE: implementation copied from http://blogs.msdn.com/b/the1/archive/2004/05/07/128242.aspx
template <typename T, int N>
char (&ArrayCountHelper(T (&array)[N]))[N];

#define GLM_ARRAY_COUNT(array) (sizeof(ArrayCountHelper(array)))

#define GLM_CAST_POINTER_TO_ARRAY(var, type, count) (*reinterpret_cast<type(*)[count]>(var))
#define GLM_CAST_ARRAY_TO_POINTER(var, type) (reinterpret_cast<type*>(&var[0]))
