/* Copyright (c) <2003-2019> <Julio Jerez, Newton Game Dynamics>
* 
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* 
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 
* 3. This notice may not be removed or altered from any source distribution.
*/

#ifndef __DGTYPES_H__
#define __DGTYPES_H__

#ifdef _MSC_VER 
	#ifdef _M_ARM
		#ifndef _ARM_VER
			#define _ARM_VER
		#endif
	#elif defined (_M_X64)
		#ifndef _WIN_64_VER
			#define _WIN_64_VER
		#endif
	#elif defined (_M_IX86)
		#ifndef _WIN_32_VER
			#define _WIN_32_VER
		#endif
	#else 
		#error target platform not defined
	#endif

	#if _MSC_VER >= 1400
		#define HAVE_STRUCT_TIMESPEC
	#endif
#endif

#if (defined (_WIN_32_VER) || defined (_WIN_64_VER) || (defined (_MSC_VER ) && defined (_ARM_VER)) )
	#pragma warning (disable: 4100) //unreferenced formal parameter
	#pragma warning (disable: 4201) //nonstandard extension used : nameless struct/union
	#pragma warning (disable: 4324) //structure was padded due to __declspec(align())
	#pragma warning (disable: 4514) //unreferenced inline function has been removed
	#pragma warning (disable: 4530) //C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc
	#pragma warning (disable: 4625) //copy constructor could not be generated because a base class copy constructor is inaccessible or deleted
	#pragma warning (disable: 4626) //assignment operator could not be generated because a base class assignment operator is inaccessible or deleted
	#pragma warning (disable: 4640) //construction of local static object is not thread-safe
	#pragma warning (disable: 4820) //bytes padding added after data member
	#pragma warning (disable: 4005) //'__useHeader': macro redefinition
	//#pragma warning (disable: 4577) // 'noexcept' used with no exception handling mode specified; termination on exception is not guaranteed. Specify /EHsc
    
	#include <io.h> 
	#include <direct.h> 
	#include <malloc.h>
	#include <float.h>
	#include <stdarg.h>
	#include <process.h>

	#ifdef _DEBUG
		#pragma warning (disable: 4127)	//conditional expression is constant
	#endif

	#pragma warning (push, 3) 
		#include <windows.h>
		#include <crtdbg.h>
		//#ifndef _DURANGO
		//	#include <tlhelp32.h>
		//#endif
	#pragma warning (pop) 
#endif

#include <new>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <time.h>
#include <math.h>
#include <float.h>
#include <ctype.h>

#if (defined (__MINGW32__) || defined (__MINGW64__))
	#include <io.h> 
	#include <direct.h> 
	#include <malloc.h>
	#include <float.h>
	#include <windows.h>
	#include <process.h>
#endif

#if (defined (_WIN_32_VER) || defined (_WIN_64_VER))
	#include <intrin.h>
	#include <emmintrin.h> 
	#include <pmmintrin.h>
#endif

#if (defined (_POSIX_VER) || defined (_POSIX_VER_64) || defined (__MINGW32__) || defined (__MINGW64__))
  /* CMake defines NDEBUG for _not_ debug builds. Therefore, set
     Newton's _DEBUG flag only when NDEBUG is not defined.
  */
  #ifndef NDEBUG
    #define _DEBUG 1
  #endif

	#include <unistd.h>
	#include <assert.h>
	#if (!defined(__arm__) && !defined(__aarch64__)) // it was __ARMCC_VERSION before, it should be __ARM__ or aarch64, otherwise cross compiling in gcc fails.
		extern "C" 
		{ 
			// for SSE3 and up
			#include <pmmintrin.h> 
			#include <emmintrin.h> 
			#include <mmintrin.h> 
		} 
	#endif
#endif

#ifdef _MACOSX_VER
	#include <unistd.h>
	#include <sys/sysctl.h>
    #include <assert.h> 
    #if (defined __i386__ || defined __x86_64__)
		#include <fenv.h>
		#include <pmmintrin.h> 
		#include <emmintrin.h>  //sse3
        #include <mmintrin.h> 
    #endif
#endif

// uncomment out D_PROFILER to enable profiler frame capture profiler traces
// alternatively the end application can use a command line option to enable this define
//#define D_PROFILER

// uncomment this for Scalar floating point 
// alternatively the end application can use a command line option to enable this define
//#define DG_SCALAR_VECTOR_CLASS

// uncomment this for Scalar floating point 
// alternatively the end application can use a command line option to enable this define
//#define __ANDROID__

// by default newton run on a separate thread and 
// optionally concurrent with the calling thread,
// it also uses a thread job pool for multi core systems.
// define DG_USE_THREAD_EMULATION on the command line for 
// platform that do not support hardware multi threading or 
// if the and application want to control threading at the application level 
//#define DG_USE_THREAD_EMULATION

#if (defined (_WIN_32_VER) || defined (_WIN_64_VER))
	#if _MSC_VER < 1700
		#ifndef DG_USE_THREAD_EMULATION
			#define DG_USE_THREAD_EMULATION
		#endif
	#endif
#endif

#ifndef DG_USE_THREAD_EMULATION
	#include <mutex>
	#include <thread>
	#include <condition_variable>
#endif


//************************************************************
#ifdef DG_DISABLE_ASSERT
	#define dgAssert(x)
#else 
	#if defined (_WIN_32_VER) || defined (_WIN_64_VER) 
		#define dgAssert(x) _ASSERTE(x)
	#elif defined (_MSC_VER ) && defined (_ARM_VER) 
		#define dgAssert(x) _ASSERTE(x)
	#else
		#ifdef _DEBUG
			#define dgAssert(x) assert(x)
		#else 
			#define dgAssert(x)
		#endif
	#endif
#endif


#define	DG_MAX_THREADS_HIVE_COUNT		16

#ifdef _DEBUG
//#define __ENABLE_DG_CONTAINERS_SANITY_CHECK 
#endif


#ifdef DLL_DECLSPEC
#undef DLL_DECLSPEC
#endif

#ifdef _DEBUG
	#define DG_INLINE inline
#else
	#if defined(_MSC_VER)
			#define DG_INLINE __forceinline 
	#else 
		#define DG_INLINE	inline
		//#define DG_INLINE	 __attribute__((always_inline))
	#endif
#endif


#define DG_VECTOR_SIMD_SIZE		16
#define DG_VECTOR_AVX2_SIZE		32

#if defined(_MSC_VER)
	#define	DG_GCC_VECTOR_ALIGNMENT
	#define	DG_MSC_VECTOR_ALIGNMENT    __declspec(align(DG_VECTOR_SIMD_SIZE))
#else
	#define	DG_GCC_VECTOR_ALIGNMENT    __attribute__ ((aligned (DG_VECTOR_SIMD_SIZE)))
	#define	DG_MSC_VECTOR_ALIGNMENT
#endif

#if defined(_MSC_VER)
	#define	DG_GCC_AVX_ALIGNMENT
	#define	DG_MSC_AVX_ALIGNMENT       __declspec(align(DG_VECTOR_AVX2_SIZE))
#else
	#define	DG_GCC_AVX_ALIGNMENT       __attribute__ ((aligned (DG_VECTOR_AVX2_SIZE)))
	#define	DG_MSC_AVX_ALIGNMENT
#endif

#if defined(_MSC_VER)
	#define DG_LIBRARY_EXPORT __declspec(dllexport)
	#define DG_LIBRARY_IMPORT __declspec(dllimport)
	#define DG_LIBRARY_STATIC
#else
	#define DG_LIBRARY_EXPORT __attribute__((visibility("default")))
	#define DG_LIBRARY_IMPORT __attribute__((visibility("default")))
	#define DG_LIBRARY_STATIC
#endif


#if ((defined (_WIN_32_VER) || defined (_WIN_64_VER)) && (_MSC_VER  >= 1600))
	#include <stdint.h>
	typedef int8_t dgInt8;
	typedef uint8_t dgUnsigned8;

	typedef int16_t dgInt16;
	typedef uint16_t dgUnsigned16;

	typedef int32_t dgInt32;
	typedef uint32_t dgUnsigned32;

	typedef int64_t dgInt64;
	typedef uint64_t dgUnsigned64;
#else
	typedef char dgInt8;
	typedef unsigned char dgUnsigned8;

	typedef short dgInt16;
	typedef unsigned short dgUnsigned16;

	typedef int dgInt32;
	typedef unsigned dgUnsigned32;
	typedef unsigned int dgUnsigned32;

	typedef long long dgInt64;
	typedef unsigned long long dgUnsigned64;
	typedef double dgFloat64;
#endif


typedef double dgFloat64;

#ifdef _NEWTON_USE_DOUBLE
	typedef double dgFloat32;
#else
	typedef float dgFloat32;
#endif


class dgTriplex
{
	public:
	dgFloat32 m_x;
	dgFloat32 m_y;
	dgFloat32 m_z;
};

#define dgPi		 	dgFloat32 (3.141592f)
#define dgPI2		 	dgFloat32 (dgPi * 2.0f)
#define dgEXP		 	dgFloat32 (2.71828f)
#define dgEpsilon	  	dgFloat32 (1.0e-5f)
#define dgDegreeToRad  	dgFloat32 (dgPi / 180.0f)
#define dgRadToDegree  	dgFloat32 (180.0f / dgPi)

class dgBigVector;
#ifndef _NEWTON_USE_DOUBLE
class dgVector;
#endif 


#if (defined (_WIN_32_VER) || defined (_WIN_64_VER))
	#define dgApi __cdecl 	
	#define dgStdApi __stdcall 	
#else
	#define dgApi 	
	#define dgStdApi
#endif



#if (defined (_WIN_32_VER) || defined (_WIN_64_VER))
	#define dgCheckFloat(x) (_finite(x) && !_isnan(x))
//	#define dgCheckFloat(x) 1
#else
	#define dgCheckFloat(x) (isfinite(x) && !isnan(x))
//		#define dgCheckFloat(x) 1
#endif

typedef void (dgApi *dgDeserialize) (void* const userData, void* buffer, dgInt32 size);
typedef void (dgApi *dgSerialize) (void* const userData, const void* const buffer, dgInt32 size);
typedef bool (dgApi *dgReportProgress) (dgFloat32 progressNormalzedPercent, void* const userData);

// assume this function returns memory aligned to 16 bytes
#define dgAlloca(type, count) (type*) alloca (sizeof (type) * (count))

DG_INLINE dgInt32 dgExp2 (dgInt32 x)
{
	dgInt32 exp;
	for (exp = -1; x; x >>= 1) {
		exp ++;
	}
	return exp;
}

DG_INLINE dgInt32 dgBitReversal(dgInt32 v, dgInt32 base)
{
	dgInt32 x = 0;
	dgInt32 power = dgExp2 (base) - 1;
	do {
		x += (v & 1) << power;
		v >>= 1;
		power--;
	} while (v);
	dgAssert(x < base);
	return x;
}


template <class T> 
DG_INLINE T dgMin(T A, T B)
{
	return (A < B) ? A : B; 
}

template <class T> 
DG_INLINE T dgMax(T A, T B)
{
	return (A > B) ? A : B; 
}

template <class T>
DG_INLINE T dgMin(T A, T B, T C)
{
	return dgMin(dgMin (A, B), C);
}

template <class T>
DG_INLINE T dgMax(T A, T B, T C)
{
	return dgMax(dgMax (A, B), C);
}

template <class T>
DG_INLINE T dgClamp(T val, T min, T max)
{
	return dgMax (min, dgMin (max, val));
}

template <class T> 
DG_INLINE void dgSwap(T& A, T& B)
{
	T tmp (A);
	A = B;
	B = tmp;
}	

template <class T>
DG_INLINE T dgAbs(T A)
{
	// according to Intel this is better because is does not read after write
	return (A >= T(0)) ? A : -A;
}

template <class T>
DG_INLINE T dgSign(T A)
{
	return (A >= T(0)) ? T(1) : T(-1);
}

template <class T> 
DG_INLINE bool dgAreEqual(T A, T B, T tol)
{
	if ((dgAbs(A) < tol) && (dgAbs(B) < tol)) {
		return true;
	}
/*
	dgInt32 exp0;
	dgFloat64 mantissa0 = frexp(dgFloat64 (A), &exp0);

	dgInt32 exp1;
	dgFloat64 mantissa1 = frexp(dgFloat64(B), &exp1);

	if ((exp0 < -12) && (exp1 < -12)) {
		return true;
	}

	if (exp0 != exp1) {
		return false;
	}
	return dgAbs(mantissa0 - mantissa1) < tol;
*/	
	T den = dgMax(dgAbs(A), dgAbs(B)) + tol;
	A /= den;
	B /= den;
	return dgAbs(A - B) < tol;
}

#ifdef _NEWTON_USE_DOUBLE
	union dgFloatSign
	{
		struct {
			dgInt32 m_dommy;
			dgInt32 m_iVal;
		} m_integer;
		dgFloat64 m_fVal;
	};
#else
	union dgFloatSign
	{
		struct {
			dgInt32 m_iVal;
		} m_integer;
		dgFloat32 m_fVal;
	};
#endif

union dgDoubleInt
{
	struct {
		dgInt32 m_intL;
		dgInt32 m_intH;
	};
	void* m_ptr;
	dgInt64 m_int;
	dgFloat64 m_float;
};


void dgGetMinMax (dgBigVector &Min, dgBigVector &Max, const dgFloat64* const vArray, dgInt32 vCount, dgInt32 strideInBytes);
dgInt32 dgVertexListToIndexList (dgFloat64* const vertexList, dgInt32 strideInBytes, dgInt32 compareCount,     dgInt32 vertexCount,         dgInt32* const indexListOut, dgFloat64 tolerance = dgEpsilon);
dgInt32 dgVertexListToIndexList (dgFloat32* const vertexList, dgInt32 strideInBytes, dgInt32 floatSizeInBytes, dgInt32 unsignedSizeInBytes, dgInt32 vertexCount, dgInt32* const indexListOut, dgFloat32 tolerance = dgEpsilon);

#define PointerToInt(x) ((size_t)x)
#define IntToPointer(x) ((void*)(size_t(x)))


#ifndef _MSC_VER 
	#define _stricmp(x,y) strcasecmp(x,y)
#endif

#define dgSqrt(x)			dgFloat32 (sqrt(x))	
#define dgSin(x)			dgFloat32 (sin(x))
#define dgCos(x)			dgFloat32 (cos(x))
#define dgAsin(x)			dgFloat32 (asin(x))
#define dgAcos(x)			dgFloat32 (acos(x))
#define dgLog(x)			dgFloat32 (log(x))
#define dgCeil(x)			dgFloat32 (ceil(x))
#define dgFloor(x)			dgFloat32 (floor(x))	
#define dgPow(x,y)			dgFloat32 (pow(x,y))
#define dgFmod(x,y)			dgFloat32 (fmod(x,y))
#define dgAtan2(x,y)		dgFloat32 (atan2(x,y))
#define dgRsqrt(x)			(dgFloat32 (1.0f) / dgSqrt(x))
#define dgClearFP()			_clearfp() 
#define dgControlFP(x,y)	_controlfp(x,y)

enum dgSerializeRevisionNumber
{
	m_firstRevision = 100,
	// add new serialization revision number here
	m_currentRevision 
};

dgUnsigned64 dgGetTimeInMicrosenconds();
dgFloat64 dgRoundToFloat(dgFloat64 val);
void dgSerializeMarker(dgSerialize serializeCallback, void* const userData);
dgInt32 dgDeserializeMarker(dgDeserialize serializeCallback, void* const userData);

class dgFloatExceptions
{
	public:
	#ifdef _MSC_VER
		#define DG_FLOAT_EXECTIONS_MASK	(EM_INVALID | EM_DENORMAL | EM_ZERODIVIDE)
	#else 
		#define DG_FLOAT_EXECTIONS_MASK	0
	#endif

	dgFloatExceptions(dgUnsigned32 mask = DG_FLOAT_EXECTIONS_MASK);
	~dgFloatExceptions();

	private:
	#if (defined (_MSC_VER) && defined (_WIN_32_VER))
	dgUnsigned32 m_mask;
#endif
};

class dgSetPrecisionDouble 
{
	public:
	dgSetPrecisionDouble();
	~dgSetPrecisionDouble();
	#if (defined (_MSC_VER) && defined (_WIN_32_VER))
	dgInt32 m_mask; 
	#endif
};

DG_INLINE dgInt32 dgAtomicExchangeAndAdd (dgInt32* const addend, dgInt32 amount)
{
	#if (defined (_WIN_32_VER) || defined (_WIN_64_VER))
		return _InterlockedExchangeAdd((long*)addend, long(amount));
	#elif defined (_MSC_VER ) && defined (_ARM_VER) 
		return _InterlockedExchangeAdd((long*)addend, long(amount));
	#elif (defined (__MINGW32__) || defined (__MINGW64__))
		return InterlockedExchangeAdd((long*)addend, long(amount));
	#elif (defined (_POSIX_VER) || defined (_POSIX_VER_64) ||defined (_MACOSX_VER)|| defined ANDROID)
		return __sync_fetch_and_add((int32_t*)addend, amount);
	#else
		#error "dgAtomicExchangeAndAdd implementation required"
	#endif
}

DG_INLINE dgInt32 dgInterlockedExchange(dgInt32* const ptr, dgInt32 value)
{
	#if (defined (_WIN_32_VER) || defined (_WIN_64_VER))
		return _InterlockedExchange((long*) ptr, value);
	#elif defined (_MSC_VER ) && defined (_ARM_VER) 
		return _InterlockedExchange((long*)ptr, value);
	#elif (defined (__MINGW32__) || defined (__MINGW64__))
		return InterlockedExchange((long*) ptr, value);
	#elif (defined (_POSIX_VER) || defined (_POSIX_VER_64) ||defined (_MACOSX_VER))
		return __sync_lock_test_and_set((int32_t*)ptr, value);
	#else
		#error "dgInterlockedExchange implementation required"
	#endif
}

/*
DG_INLINE void* dgInterlockedExchange(void** const ptr, void* value)
{
	#if defined(_WIN32)
		#ifdef _M_X64 
			return (void*)_InterlockedExchange64((dgInt64*)ptr, dgInt64 (value));
		#else
			return (void*)_InterlockedExchange((long*) ptr, dgInt32(value));
		#endif
	#elif defined(__linux__) || defined(_MACOSX_VER)
		#if defined(__ANDROID__)
			return __sync_lock_test_and_set(ptr, value);
		#else
			#ifdef __x86_64__
				return (void*)__sync_lock_test_and_set((dgInt64*) ptr, (dgInt64)value);
			#else
				return (void*)__sync_lock_test_and_set((dgInt32*) ptr, (dgInt32) value);
			#endif
		#endif
	#else
		#error "dgInterlockedExchange implementation required"
	#endif
}
*/

DG_INLINE dgInt32 dgInterlockedTest(dgInt32* const ptr, dgInt32 value)
{
	#if (defined (_WIN_32_VER) || defined (_WIN_64_VER))
		return _InterlockedCompareExchange((long*)ptr, value, value);
	#elif defined (_MSC_VER ) && defined (_ARM_VER) 
		return _InterlockedCompareExchange((long*)ptr, value, value);
	#elif (defined (__MINGW32__) || defined (__MINGW64__))
		return InterlockedCompareExchange((long*)ptr, value, value);
	#elif (defined (_POSIX_VER) || defined (_POSIX_VER_64) ||defined (_MACOSX_VER))
		return __sync_lock_test_and_set((int32_t*)ptr, value);
	#else
		#error "dgInterlockedTest implementation required"
	#endif
}

DG_INLINE void dgThreadYield()
{
#ifndef DG_USE_THREAD_EMULATION
	std::this_thread::yield();
#endif
}

DG_INLINE void dgThreadPause()
{
#ifndef DG_USE_THREAD_EMULATION
	//#if defined (_WIN_32_VER) || defined (_WIN_64_VER) || defined (WIN32) || defined (i386_) || defined (x86_64_)
	#if defined (_WIN_32_VER) || defined (_WIN_64_VER)
		_mm_pause();
	#else 
		std::this_thread::yield();
	#endif
#endif
}

DG_INLINE void dgSpinLock(dgInt32* const ptr)
{
#ifndef DG_USE_THREAD_EMULATION 
	while (dgInterlockedExchange(ptr, 1)) {
		dgThreadYield();
	}
#endif
}


DG_INLINE void dgSpinUnlock (dgInt32* const ptr)
{
	#ifndef DG_USE_THREAD_EMULATION 
		dgInterlockedExchange(ptr, 0);
	#else
		*ptr = 0;
	#endif
}


class dgScopeSpinLock
{
	public:
	DG_INLINE dgScopeSpinLock(dgInt32* const lock)
		:m_atomicLock(lock)
	{
		dgSpinLock(m_atomicLock);
	}

	DG_INLINE ~dgScopeSpinLock()
	{
		dgSpinUnlock(m_atomicLock);
	}

	dgInt32* m_atomicLock;
};

class dgScopeSpinPause
{
	public:
	DG_INLINE dgScopeSpinPause(dgInt32* const lock)
		:m_atomicLock(lock)
	{
		while (dgInterlockedExchange(m_atomicLock, 1)) {
			dgThreadPause();
		}
	}

	DG_INLINE ~dgScopeSpinPause()
	{
		dgSpinUnlock(m_atomicLock);
	}

	dgInt32* m_atomicLock;
};



#ifdef _MACOSX_VER
#include <sys/time.h>
#ifndef CLOCK_REALTIME
	#define CLOCK_REALTIME 0
#endif
#ifndef CLOCK_MONOTONIC
	#define CLOCK_MONOTONIC 0
#endif
//clock_gettime is not implemented on OSX
DG_INLINE int clock_gettime(int /*clk_id*/, struct timespec* t) {
    struct timeval now;
    int rv = gettimeofday(&now, NULL);
    if (rv) return rv;
    t->tv_sec  = now.tv_sec;
    t->tv_nsec = now.tv_usec * 1000;
    return 0;
}
#endif

#endif

