/*
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License version 2 
    as published by the Free Software Foundation.

    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, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA


    Copyright (C) 2006  Thierry Berger-Perrin <tbptbp@gmail.com>
*/
#ifndef SPECIFICS_H
#define SPECIFICS_H

// Macro tricks
#define PP_STRINGIZE(X)        PP_DO_STRINGIZE(X)
#define PP_DO_STRINGIZE(X)    #X

#define PP_UNICODIZE(X)        PP_DO_UNICODIZE(X)
#define PP_DO_UNICODIZE(X)    PP_JOIN(L,#X)

#define PP_JOIN( X, Y )        PP_DO_JOIN( X, Y )
#define PP_DO_JOIN( X, Y )    PP_DO_JOIN2(X,Y)
#define PP_DO_JOIN2( X, Y )    X##Y

//#define PP_JOIN3( X, Y, Z )        PP_DO_3JOIN( X, Y, Z )
//#define PP_DO_3JOIN( X, Y,  Z )    PP_DO_3JOIN3(X,Y,Z)
//#define PP_DO_3JOIN3( X, Y, Z )    X##Y##Z



/*
    Compiler detection.


    First detect ICC as it will pretend to be either msvc or gcc.
    gcc:    __GCC__
    msvc:    __MSVC__
    icc:    __ICC__ & (__ICC_GCC__ || __ICC_MSVC__)
*/
#if defined(__INTEL_COMPILER)
    #define __ICC__
    #if defined(_MSC_VER)
        #define __ICC_MSVC__
    #elif defined(__GNUC__)
        #define __ICC_GCC__
    #else
        #error couldn't grok which compiler ICC is trying to emulate.
    #endif
#elif defined(__GNUC__)
    #define __GCC__
#elif defined(_MSC_VER)
    #define __MSVC__
#else
    #error unsupported compiler.
#endif

/*
    Platform & wideness detection
    Either LINUX or WINDOWS
    Either X86_32 or X86_64

    _WIN32 is defined by icc
*/
#if defined WIN32 || defined _WIN32 || defined __CYGWIN32__ || defined __MINGW32__
    #ifndef WIN32    // should really fix makefiles, eh.
        #define WIN32
    #endif
    #define WINDOWS
    #define X86_32
#elif defined WIN64 || defined _WIN62
    #error terra incognita.
    #define WINDOWS
    #define X86_64
#elif defined __linux__
    //#if defined(__amd64__) || defined(__linux__)
    //#elif !defined(WIN32) && (defined(__CYGWIN32__) || defined(__MINGW32__) || defined(_MSC_VER))
    #define LINUX
    #define X86_64    // i have no 32bit linux (with SSE) atm.
#else
    #error couldn't recognize a platform to build for.
#endif

/*
    Assume a non debug build

*/
#if !(defined DEBUG || defined _DEBUG)
    #undef NDEBUG
    #define NDEBUG
#else
    #undef DEBUG
    #define DEBUG
#endif



/*
    Setup some compiler specific stuff
*/
#if defined __GCC__ || defined __ICC_GCC__
    // i can't use 'restrict' because it collides with m$ fucking headers '__declspec(restrict)' ...
    // and 'RESTRICT' is ugly
    #define __restrict                __restrict__

    #define FINLINE                    inline __attribute__((always_inline))
    #define NOINLINE                __attribute__((noinline))

    #define MM_ALIGN16                __attribute__ ((aligned (16)))

    #define EXPECT_TAKEN(a)            __builtin_expect(!!(a), true)
    #define EXPECT_NOT_TAKEN(a)        __builtin_expect(!!(a), false)

    #define UNUSED                    __attribute__((__unused__))
    #define NORETURN                __attribute__((noreturn))
    #define NOVTABLE

    #define BREAKPOINT()            do { __asm__("int3"); } while(0)

    #define SYM_EXPORT                __attribute((dllexport)) // __declspec(dllexport)
    #define SYM_IMPORT                __attribute((dllimport)) // __declspec(dllexport)
    #ifndef DO_EXPORT
        #define DLL_SYM                SYM_IMPORT
    #else
        #define DLL_SYM                SYM_EXPORT
    #endif

    //#define THISFUNC __PRETTY_FUNCTION__
    /*
        #ifdef __MINGW32__
            #define __COMPILER_ENV__    "MinGW"
        #elif defined __CYGWIN32__
            #define __COMPILER_ENV__    "CygWin"
        #elif defined(AMD64)
            #define __COMPILER_ENV__    "AMD64"
        #else
            #define __COMPILER_ENV__    "something else"
        #endif
    */
#elif defined __MSVC__ || defined __ICC_MSVC__
    // collision with __declspec(restrict)
    //#define __restrict                kkkkkkkkk(_restrict)

    #define FINLINE                    __forceinline
    #define NOINLINE                __declspec(noinline)
    #define MM_ALIGN16                __declspec(align(16))
    #define BREAKPOINT()            do { _asm { int 3 } } while (0)
    #define EXPECT_TAKEN(a)            (a)
    #define EXPECT_NOT_TAKEN(a)        (a)
    #define UNUSED                    __attribute__ ((__unused__))
    #define NOVTABLE                __declspec(novtable)
    #define NORETURN                __declspec(noreturn)
    #define THISFUNC        __FUNCTION__
    #define SYM_EXPORT    __declspec(dllexport)
    #define SYM_IMPORT    __declspec(dllimport)
    #ifndef DO_EXPORT
        #define DLL_SYM    SYM_IMPORT
    #else
        #define DLL_SYM    SYM_EXPORT
    #endif
#endif

#if defined __ICC__
    // mute icc a bit, it's really verbose at warning lvl 4
    #pragma warning(disable : 981) // operands are evaluated in unspecified order
    #pragma warning(disable : 1572) // remark #1572: floating-point equality and inequality comparisons are unreliable
    #pragma warning(disable : 1419) // external declaration in primary source file
    #pragma warning(disable : 1418) // external function definition with no prior declaration
#endif

/*
    Before we try to fix some issues, we need to pull in those horrible windows headers.
*/
#if defined WINDOWS && !defined SKIP_WINDOW_H
    #ifndef UNICODE
        #define UNICODE
    #endif

    // msvc8 fixage for uterly silly pseudo safe libc overloads.
#ifndef __STDC_WANT_SECURE_LIB__
    #define __STDC_WANT_SECURE_LIB__        0
#endif
    #define _CRT_SECURE_NO_DEPRECATE
    #undef _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES


    #define STRICT            1
    // don't include this stuff (some of these are from AFXV_W32.H and may be deprecated - marked with '?')
    #define VC_EXTRALEAN
    #define WIN32_LEAN_AND_MEAN
    #define WIN32_EXTRA_LEAN

    #define NOSERVICE            // - All Service Controller routines, SERVICE_ equates, etc.
    #define NOMCX                // - Modem Configuration Extensions
    #define NOIME                // <imm.h>
    #define NOSOUND                // - Sound driver routines
    #define NOCOMM                // - COMM driver routines
    #define NOKANJI                // - Kanji support stuff.
    #define NORPC                // ?
    #define NOPROXYSTUB            // ?
    #define NOIMAGE                // ?
    #define NOTAPE                // ?
    #define NOMETAFILE            // - typedef METAFILEPICT
    #define NOMINMAX            // - Macros min(a,b) and max(a,b) I define myself
    #define NOCRYPT                // - crypto API
    #define NOHELP                // - Help engine interface.
    #define NOPROFILER            // - Profiler interface.
    #define NODEFERWINDOWPOS    // - DeferWindowPos routines
    #define NORASTEROPS            // - Binary and Tertiary raster ops
    //#define NOSHOWWINDOW        // - SW_*
    #define OEMRESOURCE            // - OEM Resource values
    #define NOATOM                // - Atom Manager routines
    #define NOCLIPBOARD            // - Clipboard routines
    #define NOCOLOR                // - Screen colors
    #define NOCTLMGR            // - Control and Dialog routines
    #define NODRAWTEXT            // - DrawText() and DT_*
    //#define NOGDI                // - All GDI defines and routines

    #define NOMINMAX            // weee

#ifdef _WIN32_WINNT
	#undef _WIN32_WINNT
#endif
    #define _WIN32_WINNT    0x0501        // XP, 0x0500 -> Windows 2000

    #ifdef __MSVC__
        #pragma warning ( push, 1 )        // ignore any warnings or changes that win32 has
    #endif
        // ok we're ready, finally include The Devil
#ifdef  _XBOX
	#include <xtl.h>
#else
	#include <windows.h>
#endif
	#ifdef __MSVC__
        #pragma warning ( pop )            // back to normal
    #endif

    // while we're include headers, let's get proper ptrdiff_t support for msvc
    #ifdef __MSVC__
        #include <crtdefs.h>
    #endif
#endif

/*

    Standard types.

    int_t/uint_t aliases the fast version for the platform

*/
#if defined X86_64
    #ifndef LINUX
        #error win64 isn't supported.
    #endif

    //#include <sys/types.h>
    #include <stdint.h>
    typedef signed char            char_t;
    typedef unsigned char        uchar_t;

    typedef int64_t                int_t;
    typedef uint64_t            uint_t;

    typedef uint_t                bool_t;
    typedef void *                handle_t;

    typedef float                float32_t;
    typedef double                float64_t;
#else
    // 32bit
    typedef signed char            char_t;
    typedef unsigned char        uchar_t;

    typedef char_t                int8_t;
    typedef uchar_t                uint8_t;
    typedef short                int16_t;
    typedef unsigned short        uint16_t;
    typedef long                int32_t;
    typedef unsigned long        uint32_t;

    #if defined __MSVC__ || defined __ICC__
        typedef __int64                int64_t;
        typedef unsigned __int64    uint64_t;
    #else
        typedef long long            int64_t;
        typedef unsigned long long    uint64_t;
    #endif

    typedef int                    int_t;
    typedef unsigned int        uint_t;

    typedef uint_t                bool_t;
    typedef void *                handle_t;

    typedef float                float32_t;
    typedef double                float64_t;
#endif


/*
    One last fixage pass.
*/

// wrong, but i don't wanna know why those aren't defined sometimes
#ifdef WINDOWS
    extern "C" {
        #if defined __GNUC__
            SYM_IMPORT void        __stdcall OutputDebugStringA(const char*);
            SYM_IMPORT void        __stdcall OutputDebugStringW(const wchar_t*);
            // hmm. bad signature :)
            SYM_IMPORT uint32_t    __stdcall GetThreadId(handle_t);
        #endif
        // somehow msvc also misses it somewhere
#ifndef  _XBOX
		SYM_IMPORT void        __stdcall Sleep(unsigned long);
#endif
    }
#endif

// fix for msvc intrinsics not being intrinsics.
#if defined __MSVC__ && !defined __ICC__
    // MSVC7 kludging
    #if _MSC_VER >= 1310 && !defined(SKIP_WINDOWS_H)
        extern "C" {
            LONG _InterlockedIncrement(LONG volatile *);
            LONG _InterlockedDecrement(LONG volatile *);
        }
        #pragma intrinsic(_InterlockedIncrement, _InterlockedDecrement)
    #endif

    // MSVC8 kludging
    #if _MSC_VER >= 1400
        #define __MSVC8__
        // we kludge around sqrtf not being inlined in "math.h"
        // we'll only address other intrinsics here.
        // here's the other "true intrinsic" as m$ call them: atan exp log10 sqrt atan2 log sin tan cos
        // check if they really get inlined.
        #include <memory.h>    // argh. need to declare them first.
        #include <string.h>
        #include <math.h>
        #pragma intrinsic(memset, memcmp, memcpy, strlen, strcmp, strcpy, _strset,strcat)
        #pragma intrinsic(fabs, abs)        // hmm.
    #elif _MSC_VER >= 1310
        #define __MSVC7__
    #endif

    // #pragma warning (disable : 4514)    // unreferenced inline function has been removed

#endif // SPECIFICS_HPP

// some libc fixin'
#if defined __MSVC__
    #define snprintf                    _snprintf
    //#define vsnprintf                    _vsnprintf
    #define vsnwprintf                    _vsnwprintf
#endif


/*
    Bake a string representing compiler/platform/whatever combo.
*/
#if defined __ICC__
    #define COMPILER_PRETTY_STAMP    "icc v"PP_STRINGIZE(__INTEL_COMPILER)
#elif defined __MSVC__
    //#define    COMPILER_PRETTY_STAMP    "msvc v"PP_STRINGIZE(_MSC_VER)" ["__COMPILER_TARGET__"/"PP_STRINGIZE(_M_IX86)"]"
    #if _MSC_VER >= 1400
        #define    COMPILER_PRETTY_STAMP    "msvc8"
    #elif _MSC_VER >= 1310
        #define    COMPILER_PRETTY_STAMP    "msvc7"
    #else
        #define    COMPILER_PRETTY_STAMP    "msvc"
    #endif
#else
    // GCC
    #define COMPILER_PRETTY_STAMP "gcc v"PP_STRINGIZE(__GNUC__)"."PP_STRINGIZE(__GNUC_MINOR__)"."PP_STRINGIZE(__GNUC_PATCHLEVEL__)
#endif


/*
    It's rather annoying that none of those freaking compilers could agree about these freaking headers
    Therefore we fix it all right now, right here
*/
#ifndef _XBOX
#include <mmintrin.h>
#include <xmmintrin.h>
#include <emmintrin.h>    // includes <xmmintrin.h>

#if !defined __MSVC__
    #include <pmmintrin.h>    // SSE3 & stuff
#endif

// Gcc long standing reported bug, those definitions are keyed on __SSE3__ and that's plain wrong.
#ifndef _MM_DENORMALS_ZERO_MASK
    #define _MM_DENORMALS_ZERO_MASK   0x0040
    #define _MM_DENORMALS_ZERO_ON     0x0040
    #define _MM_DENORMALS_ZERO_OFF    0x0000
#endif

#ifndef _MM_SET_DENORMALS_ZERO_MODE
    #define _MM_SET_DENORMALS_ZERO_MODE(mode) _mm_setcsr((_mm_getcsr() & ~_MM_DENORMALS_ZERO_MASK) | (mode))
#endif
#ifndef _MM_GET_DENORMALS_ZERO_MODE
    #define _MM_GET_DENORMALS_ZERO_MODE() (_mm_getcsr() & _MM_DENORMALS_ZERO_MASK)
#endif
#endif

/*
#ifdef __ICC__
    #define _USE_MATH_DEFINES
    #include <mathimf.h> // - Intel math library definitions
    #include <math.h>
#else
    //probleme sur amd64
    //#define _USE_MATH_DEFINES
    #include "math.h"
#endif
*/

extern NOINLINE NORETURN void fatal(const char * const reason);
extern NOINLINE NORETURN void bail(const char * const reason, const int exitcode);
#endif
