#ifndef XPLATFORM_INCLUDED
#define XPLATFORM_INCLUDED
// HANDLE
// File mapping
// UnMapView
// CreateFileMapping
// WorkingSetInfo

#ifdef _WIN32
#define ALIGN( declaration , bytes ) __declspec(align(bytes)) declaration
const char FileSeparator = '\\';
#else // !_WIN32
#define ALIGN( declaration , bytes ) declaration __attribute__((aligned(bytes)))
const char FileSeparator = '/';
#endif // _WIN32

#undef USE_BOOST
#define USE_BOOST


#define USE_NEW_FILE_HANDLES

#ifdef USE_BOOST
#define SHOW_BOOST_MESSAGES	// Indicates that boost-related warning messages should be output on compilation
#define USE_BOOST_SIGNALS	// Indicates that boost signals should be used for synchronization
#define USE_BOOST_THREADS	// Indicates that boost threads should be used for multi-threading
#define USE_BOOST_CRITICAL	// Indicates that boost locks/mutexes should be used for critical sections
#define USE_BOOST_SOCKETS	// Indicates that boost sockets should be used for communication

#undef SHOW_BOOST_MESSAGES

#define DEBUG_SIGNALS

#define THREAD_FUNCTION_IN void*
#define THREAD_FUNCTION_OUT int
#ifdef _WIN32
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501
#endif // !_WIN32_WINNT
#endif // _WIN32
#else // !USE_BOOST
#pragma comment( lib , "Ws2_32.lib" )
#define THREAD_FUNCTION_IN LPVOID
#define THREAD_FUNCTION_OUT DWORD WINAPI
#include <winsock2.h>
#include <ws2tcpip.h>
#include <atlstr.h>
#include <windows.h>
#endif// USE_BOOST

#include <Util/Array.h>

#ifdef USE_BOOST
#include <stdint.h>
typedef uint8_t byte;
#endif // USE_BOOST



// locking
#ifdef USE_BOOST_CRITICAL
#include <boost/thread/recursive_mutex.hpp>
typedef boost::recursive_mutex CriticalSectionLock;
inline void InitializeCriticalSection( CriticalSectionLock* ){ ; }
inline void     DeleteCriticalSection( CriticalSectionLock* ){ ; }
inline void      EnterCriticalSection( CriticalSectionLock* l ){ l->lock(); }
inline void      LeaveCriticalSection( CriticalSectionLock* l ){ l->unlock(); }
#else // !USE_BOOST_CRITICAL
typedef CRITICAL_SECTION CriticalSectionLock;
#endif // USE_BOOST_CRITICAL

#ifdef USE_BOOST_SOCKETS
#include <boost/asio.hpp>
#endif // USE_BOOST_SOCKETS
class IOServer
{
	static int _count;
#ifndef USE_BOOST_SOCKETS
	static WSADATA _wsaData;
#endif // !USE_BOOST_SOCKETS
	static CriticalSectionLock _stdoutLock , _stderrLock , _systemLock;
public:
#ifdef USE_BOOST_SOCKETS
	static boost::asio::io_service io_service;
#endif // USE_BOOST_SOCKETS
	static void Load( void );
	static void UnLoad( void );
	class StdoutLock
	{
	public:
		StdoutLock ( void );
		~StdoutLock( void );
	};
	class StderrLock
	{
	public:
		StderrLock ( void );
		~StderrLock( void );
	};
	class SystemLock
	{
	public:
		SystemLock ( void );
		~SystemLock( void );
	};
	static void  printfID(            const char* format , ... );
	static void fprintfID( FILE* fp , const char* format , ... );
};

// sockets
#ifdef USE_BOOST_SOCKETS
#include <boost/asio.hpp>
typedef boost::asio::ip::tcp::socket* Socket;
typedef boost::asio::ip::tcp::acceptor* AcceptorSocket;
typedef boost::asio::ip::address EndpointAddress;
const Socket _INVALID_SOCKET_ = (Socket)NULL;
const AcceptorSocket _INVALID_ACCEPTOR_SOCKET_ = (AcceptorSocket)NULL;
static boost::asio::io_service io_service;
#ifdef SHOW_BOOST_MESSAGES
#pragma message( "[WARNING] Boost sockets ignoring flags" )
#endif // !SHOW_BOOST_MESSAGES
template< class C > int socket_receive( Socket& s , C* destination , size_t len , int flags )
{
	boost::system::error_code ec;
	int ret = (int)( boost::asio::read( *s , boost::asio::buffer( destination , len ) , ec ) );
	if( ec ) return  -1;
	else     return ret;
}
template< class C > int socket_send( Socket& s , const C* source , size_t len , int flags )
{
	boost::system::error_code ec;
	int ret = (int)( boost::asio::write( *s , boost::asio::buffer( source , len ) , ec ) );
	if( ec ) return  -1;
	else     return ret;
}
inline bool AddressesEqual( const EndpointAddress& a1 ,  const EndpointAddress& a2 ){ return a1.to_string()==a2.to_string(); }
inline const char *LastSocketError( void ){ return ""; }
void PrintHostAddresses( FILE* fp=stdout );
#else // !USE_BOOST_SOCKETS
typedef SOCKET Socket;
typedef SOCKET AcceptorSocket;
typedef in_addr EndpointAddress;
static const Socket _INVALID_SOCKET_ = INVALID_SOCKET;
static const AcceptorSocket _INVALID_ACCEPTOR_SOCKET_ = INVALID_SOCKET;
template< class C > int socket_receive( Socket& s , C* destination , size_t len , int flags ){ return recv( s , destination , len , flags ); }
template< class C > int socket_send( Socket& s , const C* source , size_t len , int flags ){ return send( s , source , len , flags ); }
inline bool AddressesEqual( const EndpointAddress& a1 ,  const EndpointAddress& a2 ){ return a1.s_addr==a2.s_addr; }
const char *wstrerror( int err );
int inet_aton( const char *cp , struct in_addr *inp );
inline const char* LastSocketError( void ){ return wstrerror( WSAGetLastError() ); }
void PrintHostAddresses( FILE* fp=stdout );
#endif // USE_BOOST_SOCKETS

// signals
#ifdef USE_BOOST_SIGNALS
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition_variable.hpp>
struct Signal
{
#ifdef DEBUG_SIGNALS
	int count;
#endif // DEBUG_SIGNALS
	boost::mutex mutex;
	bool state;
	boost::condition_variable condition;
#ifdef DEBUG_SIGNALS
	Signal( void ){ state=true , count=0; }
#else // !DEBUG_SIGNALS
	Signal( void ){ state=true; }
#endif // DEBUG_SIGNALS
	Signal( const Signal& s )
	{
		state = s.state;
		fprintf( stderr , "[WARNING] Could not copy signal mutex/condition" );
	}
};
// Used to ping-pong between two clients.
// The client whose state is asigned "true" goes first.
struct Signaller
{
	Signal& signal;
	bool state;
	static void Start( Signal& signal , bool state , long long ms=100 , bool verbose=false )
	{
		boost::unique_lock< boost::mutex > lock( signal.mutex );
#if 1
		int count = 0;
		while( signal.state!=state )
		{
			signal.condition.timed_wait( lock , boost::posix_time::milliseconds(ms) );
			if( count && verbose ) IOServer::printfID( "waiting on signal (%d)\n" , count );
			count++;
		}
#else
		while( signal.state!=state ) signal.condition.wait( lock );
#endif
#ifdef DEBUG_SIGNALS
		if( state ) signal.count++;
		else        signal.count--;
		if(  state && signal.count!=1 ) fprintf( stderr , "[ERROR] Primary count should be one: %d\n" , signal.count ) , exit( 0 );
		if( !state && signal.count!=0 ) fprintf( stderr , "[ERROR] Secondary count should be zero: %d\n" , signal.count ) , exit( 0 );
#endif // DEBUG_SIGNALS
	}
	static void Stop( Signal& signal , bool state )
	{
		{
			boost::unique_lock< boost::mutex > lock( signal.mutex );
			if( signal.state!=state ) fprintf( stderr , "[ERROR] Trying to release other signal\n" ) , exit( 0 );
			signal.state = !state;
		}
		signal.condition.notify_one();
	}
	Signaller( Signal& si , bool st ) : signal( si ) , state( st ) { Start( signal , state ); }
	~Signaller( void ){ Stop( signal , state ); }
};
#else // !USE_BOOST_SIGNALS
struct Signal
{
	HANDLE handles[2];
	Signal( void ){ handles[0] = CreateEvent( NULL , false , true , NULL ) , handles[1] = CreateEvent( NULL , false , false , NULL ); }
	~Signal( void ){ CloseHandle( handles[0] ) , CloseHandle( handles[1] ); }
};
struct Signaller
{
	Signal& signal;
	bool state;
	static void Start( Signal& signal , bool state ){ WaitForSingleObject( signal.handles[state?0:1] , INFINITE ); }
	static void Stop ( Signal& signal , bool state ){ if( !SetEvent( signal.handles[state?1:0] ) ) fprintf( stderr , "[ERROR] SetEvent failed\n" ) , exit( 0 ); }
	Signaller( Signal& si , bool st ) : signal( si ) , state( st ) { Start( signal , state ); }
	~Signaller( void ){ Stop( signal , state ); }
};
#endif // USE_BOOST_SIGNALS

// threads
#ifdef USE_BOOST_THREADS
#include <boost/thread.hpp>
typedef boost::thread* ThreadHandle;
inline void SetThisThreadID( char* id )
{
	std::stringstream stream;
	stream << boost::this_thread::get_id();
	sprintf( id , "%s" , stream.str().c_str() );
}
inline void CloseThreadHandle( ThreadHandle& t ){ t->join() ; delete t ; t=NULL; }
#ifdef SHOW_BOOST_MESSAGES
#pragma message( "[WARNING] Not testing thread handles" )
#endif // SHOW_BOOST_MESSAGES
inline bool TestThreadHandle( ThreadHandle& t ) { return t!=NULL; }
#ifdef SHOW_BOOST_MESSAGES
#pragma message( "[WARNING] Not setting thread priority" )
#endif // SHOW_BOOST_MESSAGES
inline ThreadHandle RunThread( THREAD_FUNCTION_OUT ThreadFunction( THREAD_FUNCTION_IN ) , void* params , int threadPriority=0 ){ return new boost::thread( ThreadFunction , params ); }
inline bool InterruptThread( ThreadHandle& t ){ t->interrupt() ; CloseThreadHandle( t ) ; return true; }
inline bool WaitOnThread( ThreadHandle& t , int ms=-1 , const char* message=NULL )
{
	if( !t->joinable() ) return true;
	if( ms<0 ) { CloseThreadHandle( t ) ; return true; }
	else
		if( !t->timed_join( boost::posix_time::milliseconds(ms) ) )
		{
			if( message ) fprintf( stderr , "[WARNING] Failed to wait on thread: %s\n" , message );
			else          fprintf( stderr , "[WARNING] Failed to wait on thread\n" );
			return false;
		}
		else return true;
}
inline bool WaitOnThreads( ThreadHandle* t , int tCount , int ms=-1 , const char* message=NULL )
{
	for( int i=0 ; i<tCount ; i++ )
	{
		if( t[i]->joinable() )
		{
			if( ms<0 ) CloseThreadHandle( t[i] );
			while( t[i]->joinable() && !t[i]->timed_join( boost::posix_time::milliseconds(ms) ) )
			{
				if( message ) fprintf( stderr , "[WARNING] Failing to wait on thread[%d/%d]: %s\n" , i , tCount , message );
				else          fprintf( stderr , "[WARNING] Failing to wait on thread[%d/%d]\n" , i  , tCount );
			}
		}
	}
	return true;
}
inline bool IsActiveThread( ThreadHandle& t ) { return t->joinable(); }
inline void SleepThisThread( int ms ){ if(ms<0) boost::this_thread::yield() ; else boost::this_thread::sleep_for( boost::chrono::milliseconds(ms) ); }
#else // !USE_BOOST_THREADS
typedef HANDLE ThreadHandle;	
inline int SetThisThreadID( char* id ) { sprintf( id , "%d" , GetCurrentThreadId() ); }
inline void CloseThreadHandle( ThreadHandle& t ){ CloseHandle( t ); }
inline bool TestThreadHandle( ThreadHandle& t ) { return t!=0; }
inline ThreadHandle RunThread( THREAD_FUNCTION_OUT ThreadFunction( THREAD_FUNCTION_IN ) , void* params , int threadPriority=0 )
{
	ThreadHandle thread = NULL;
	DWORD threadID;
	thread = CreateThread
		( 
		NULL,				// default security attributes
		0,					// use default stack size  
		ThreadFunction ,	// thread function 
		params,				// argument to thread function 
		0,					// use default creation flags 
		&threadID	// returns the thread identifier
		);
	if( !thread ) fprintf( stderr , "CreateThread failed: " ) , exit( 0 );
	if( threadPriority ) SetThreadPriority( thread , threadPriority );
	return thread;
}
inline bool InterruptThread( ThreadHandle& t ){ return TerminateThread( t , 0 )==TRUE; }
inline bool WaitOnThread( ThreadHandle& t , int ms=-1 ){ return WaitForSingleObject( t , ms<0 ? INFINITE : ms )==WAIT_OBJECT_0; }
inline bool WaitOnThreads( ThreadHandle* t , int tCount , int ms=-1 , const char* message=NULL ){ return WaitForMultipleObjects( tCount , t , TRUE , ms<0 ? INFINITE : ms )==WAIT_OBJECT_0; }
inline bool IsActiveThread( ThreadHandle& t ) { return t!=0; }
inline void SleepThisThread( int ms ){ Sleep(ms); }
#endif // USE_BOOST_THREADS

#ifdef _WIN32
inline int GetThisProcessID( void ){ return GetCurrentProcessId(); }
#ifndef strcasecmp
#define strcasecmp _stricmp
#endif // strcasecmp
#else // !_WIN32
inline int GetThisProcessID( void ){ return getpid(); }
#endif // _WIN32


#ifdef USE_NEW_FILE_HANDLES
#ifdef _WIN32
#include <io.h>
#endif // _WIN32
struct _FileHandle
{
	_FileHandle( void ) : _fp( NULL ) , _deleteOnClose( false ){ _fileName[0]=0; }
	~_FileHandle( void )
	{
		if( _fp )
		{
			fclose( _fp );
			if( _fp && _deleteOnClose && strlen( _fileName ) ) remove( _fileName );
		}
		_fp = NULL;
		_fileName[0] = 0;
	}
	const char* fileName( void ) const { return _fileName; }
	bool deleteOnClose( void ) const { return _deleteOnClose; }
	_FileHandle( const char* fileName )
	{
		strcpy( _fileName , fileName );
#ifdef _WIN32
		fopen_s( &_fp , _fileName , "w+b" );
#else // !_WIN32
		_fp = fopen( fileName , "w+b" );
#endif // _WIN32
		if( !_fp ) fprintf( stderr , "[ERROR] Failed to open file: %s\n" , _fileName ) , exit( 0 );
		_deleteOnClose = false;
		setbuf( _fp , NULL );
	}
	_FileHandle( const char* dir , const char* prefix , bool deleteOnClose )
	{
		const char scratch[] = "scratch";
		const char current[] = ".";
		if( !prefix ) prefix = scratch;
		{
			IOServer::SystemLock lock;
			if( !dir || !strlen(dir) ) dir = getenv( "TMP" );	// Try to get the scratch directory from the environment variable
			if( !dir || !strlen(dir) ) dir = current;			// Set it to the current directory
			if( dir[strlen(dir)-1]!=FileSeparator ) sprintf( _fileName , "%s%c%s_%d_XXXXXX" , dir , FileSeparator , prefix , GetThisProcessID() );
			else                                    sprintf( _fileName ,   "%s%s_%d_XXXXXX" , dir ,                 prefix , GetThisProcessID() );
		}
#ifdef _WIN32
		_mktemp( _fileName );
		fopen_s( &_fp , _fileName , "w+b" );
#else // !_WIN32
		mktemp( _fileName );
		_fp = fopen( _fileName , "w+b" );
#endif // _WIN32
		if( !_fp ) fprintf( stderr , "[ERROR] Failed to open file: %s\n" , _fileName ) , exit( 0 );
		_deleteOnClose = deleteOnClose;
		setbuf( _fp , NULL );
	}
	bool seek( long long offset )
	{
#ifdef _WIN32
		return _fseeki64( _fp , offset , SEEK_SET )==0;
#else // !_WIN32
		return  fseek   ( _fp , offset , SEEK_SET )==0;
#endif // _WIN32
	}
	long long read ( Pointer( byte ) buffer , long long bytesToRead  ){ return fread ( buffer , sizeof(byte) , bytesToRead  , _fp ); }
	long long write( Pointer( byte ) buffer , long long bytesToWrite ){ return fwrite( buffer , sizeof(byte) , bytesToWrite , _fp ); }
private:
	FILE* _fp;
	bool _deleteOnClose;
	char _fileName[1024];
};
typedef _FileHandle* FileHandle;
inline FileHandle CreateFileHandle( const char* dir , const char* prefix , bool deleteOnClose ){ return new _FileHandle( dir , prefix , deleteOnClose ); }
inline FileHandle CreateFileHandle( const char* fileName ){ return new _FileHandle( fileName ); }
inline void CloseFileHandle( FileHandle& hFile ){ if( hFile ) delete hFile ; hFile = NULL; }
inline bool SeekFileHandle( FileHandle hFile , long long  distanceToMove ){ return hFile->seek( distanceToMove ); }
inline bool SetEndOfFileHandle( FileHandle hFile ){ return true; }
inline long long ReadFileHandle( FileHandle hFile , Pointer( byte ) buffer , long long bytesToRead ){ return hFile->read( buffer , bytesToRead ); }
inline long long WriteFileHandle( FileHandle hFile , Pointer( byte ) buffer , long long bytesToWrite ){ return hFile->write( buffer , bytesToWrite ); }
#else // !USE_NEW_FILE_HANDLES
#include <atlstr.h>
typedef HANDLE FileHandle;
inline FileHandle CreateFileHandle( const char* dir , const char* prefix , bool deleteOnClose )
{
	char fullPrefix[512];
	if( prefix ) sprintf( fullPrefix , "%s_%lld",prefix , (unsigned long long)( GetThisProcessID() ) );
	else         sprintf( fullPrefix , "scratch_%lld"   , (unsigned long long)( GetThisProcessID() ) );
	CString s( _tempnam( dir , fullPrefix ) );

	if( deleteOnClose )	return CreateFile( s , FILE_READ_DATA | FILE_WRITE_DATA , 0 , NULL , CREATE_ALWAYS , FILE_FLAG_NO_BUFFERING | FILE_FLAG_DELETE_ON_CLOSE , NULL );
	else				return CreateFile( s , FILE_READ_DATA | FILE_WRITE_DATA , 0 , NULL , CREATE_ALWAYS , FILE_FLAG_NO_BUFFERING                             , NULL );
}
inline FileHandle CreateFilehandle( const char* fileName )
{
	return CreateFile( CString( fileName ) , FILE_READ_DATA | FILE_WRITE_DATA , 0 , NULL , OPEN_ALWAYS , FILE_FLAG_NO_BUFFERING , NULL );
}
inline bool SeekFileHandle( FileHandle hFile , long long  distanceToMove )
{
	LARGE_INTEGER li;
	li.QuadPart=distanceToMove;
	DWORD ret=SetFilePointer( hFile , li.LowPart , &li.HighPart , FILE_BEGIN );
	return ret!=INVALID_SET_FILE_POINTER;
}
inline bool SetEndOfFileHandle( FileHandle hFile ){ return SetEndOfFile( hFile )==TRUE; }
inline int ReadFileHandle( FileHandle hFile , Pointer( byte ) buffer , int bytesToRead )
{
	int bytesRead;
	if( !ReadFile( hFile , buffer , bytesToRead , bytesRead , NULL ) ) fprintf( stderr , "[ERROR] Failed to read from file\n" ) , exit( 0 );
	return bytesRead;
}
inline int WriteFileHandle( FileHandle hFile , Pointer( byte ) buffer , int bytesToWrite )
{
	int bytesWritten;
	if( !WriteFile( hFile , buffer , bytesToWrite , bytesWritten , NULL ) ) fprintf( stderr , "[ERROR] Failed to write to file\n" ) , exit( 0 );
	return bytesWritten;
}
inline void CloseFileHandle( FileHandle& hFile ){ if( hFile ) CloseHandle( hFile ) ; hFile = 0; }
#endif // USE_NEW_FILE_HANDLES




#if DEBUG_ARRAY_ACCESS
template< class C >
int socket_receive( Socket& s , Array< C > destination , size_t len , int flags )
{
	if( len>destination.maximum()*sizeof( C ) )
	{
		fprintf( stderr , "Size of socket_receive exceeds destination maximum: %zd > %zd\n" , len , destination.maximum()*sizeof( C ) );
		ASSERT( 0 );
		exit( 0 );
	}
	return socket_receive( s , (char*)&destination[0] , len , flags );
}
template< class C >
int socket_send( Socket s , Array< C > source , size_t len , int flags )
{
	if( len>source.maximum()*sizeof( C ) )
	{
		fprintf( stderr , "Size of socket_send exceeds source maximum: %zd > %zd\n" , len , source.maximum()*sizeof( C ) );
		ASSERT( 0 );
		exit( 0 );
	}
	return socket_send( s , (char*)&source[0] , len , flags );
}
template< class C >
int socket_send( Socket s , ConstArray< C > source , size_t len , int flags )
{
	if( len>source.maximum()*sizeof( C ) )
	{
		fprintf( stderr , "Size of socket_send exceeds source maximum: %zd > %zd\n" , len , source.maximum()*sizeof( C ) );
		ASSERT( 0 );
		exit( 0 );
	}
	return socket_send( s , (char*)&source[0] , len , flags );
}
#endif // DEBUG_ARRAY_ACCESS
#endif // XPLATFORM_INCLUDED
