#include <stdio.h>
#include <stdarg.h>
#include "XPlatform.h"

int IOServer::_count = 0;
CriticalSectionLock IOServer::_stdoutLock , IOServer::_stderrLock , IOServer::_systemLock;

#ifdef USE_BOOST_CRITICAL
IOServer::StdoutLock::StdoutLock ( void ){ EnterCriticalSection( &_stdoutLock ); }
IOServer::StdoutLock::~StdoutLock( void ){ LeaveCriticalSection( &_stdoutLock ); }
IOServer::StderrLock::StderrLock ( void ){ EnterCriticalSection( &_stderrLock ); }
IOServer::StderrLock::~StderrLock( void ){ LeaveCriticalSection( &_stderrLock ); }
IOServer::SystemLock::SystemLock ( void ){ EnterCriticalSection( &_systemLock ); }
IOServer::SystemLock::~SystemLock( void ){ LeaveCriticalSection( &_systemLock ); }
#else // !USE_BOOST_CRITICAL
IOServer::StdoutLock::StdoutLock ( void ){ if( _count ) EnterCriticalSection( &_stdoutLock ) ; else fprintf( stderr , "[WARNING] Lock not initialized\n" ); }
IOServer::StdoutLock::~StdoutLock( void ){ if( _count ) LeaveCriticalSection( &_stdoutLock ) ; else fprintf( stderr , "[WARNING] Lock not initialized\n" ); }
IOServer::StderrLock::StderrLock ( void ){ if( _count ) EnterCriticalSection( &_stderrLock ) ; else fprintf( stderr , "[WARNING] Lock not initialized\n" ); }
IOServer::StderrLock::~StderrLock( void ){ if( _count ) LeaveCriticalSection( &_stderrLock ) ; else fprintf( stderr , "[WARNING] Lock not initialized\n" ); }
IOServer::SystemLock::SystemLock ( void ){ if( _count ) EnterCriticalSection( &_systemLock ) ; else fprintf( stderr , "[WARNING] Lock not initialized\n" ); }
IOServer::SystemLock::~SystemLock( void ){ if( _count ) LeaveCriticalSection( &_systemLock ) ; else fprintf( stderr , "[WARNING] Lock not initialized\n" ); }
#endif // USE_BOOST_CRITICAL

#ifdef USE_BOOST_SOCKETS
boost::asio::io_service IOServer::io_service;
#else // !USE_BOOST_SOCKETS
WSADATA IOServer::_wsaData;
#endif // USE_BOOST_SOCKETS
void IOServer::Load( void )
{
	if( !_count )
	{
		InitializeCriticalSection( &_stdoutLock );
		InitializeCriticalSection( &_stderrLock );
		InitializeCriticalSection( &_systemLock );
#ifndef USE_BOOST_SOCKETS
		if( WSAStartup( MAKEWORD(2,2), &_wsaData ) ) fprintf( stderr , "WSAStartup failed: %s\n", LastSocketError() ) , exit(0);
#endif // !USE_BOOST_SOCKETS
	}
	_count++;
}
void IOServer::UnLoad( void )
{
	_count--;
	if( !_count )
	{
#ifndef USE_BOOST_SOCKETS
		WSACleanup();
#endif // !USE_BOOST_SOCKETS
		DeleteCriticalSection( &_stdoutLock );
		DeleteCriticalSection( &_stderrLock );
		DeleteCriticalSection( &_systemLock );
	}
}

void IOServer::fprintfID( FILE* fp , const char* format , ... )
{
	StdoutLock lock;
	va_list args;
	va_start( args , format );
	char id[512] ; SetThisThreadID( id );
	fprintf( fp , "%s] " , id );
	vfprintf( fp , format , args );
	va_end( args );
	fflush( fp );
}

void IOServer::printfID( const char* format , ... )
{
	StdoutLock lock;
	va_list args;
	va_start( args , format );
	char id[512] ; SetThisThreadID( id );
	printf( "%s] " , id );
	vprintf( format , args );
	va_end( args );
	fflush( stdout );
}


void PrintHostAddresses( FILE* fp )
{
#ifdef USE_BOOST_SOCKETS
	boost::asio::ip::tcp::resolver resolver( io_service );
	boost::asio::ip::tcp::resolver::query query( boost::asio::ip::host_name() , std::string( "" ) , boost::asio::ip::resolver_query_base::numeric_service );
	boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve( query ) , end;
	for( int count=0 ; iterator!=end ; )
	{
		if( (*iterator).endpoint().address().is_v4() ) fprintf( fp , "%d]  %s\n" , count++ , (*iterator).endpoint().address().to_string().c_str() );
//		else                                           fprintf( fp , "%d]* %s\n" , count++ , (*iterator).endpoint().address().to_string().c_str() );
		iterator++;
	}
#else // !USE_BOOST_SOCKETS
	IOServer::SystemLock lock;
	char hostName[512];
	gethostname( hostName , 512 );
	hostent* host = gethostbyname( hostName );
	for( int i=0 ; ; i++ )
		if( host->h_addr_list[i] == NULL ) break;
		else fprintf( fp , "Address[%d]: %s\n" , i , inet_ntoa( *(struct in_addr*)host->h_addr_list[i] ) );
	for( int i=0 ; ; i++ )
		if( host->h_aliases[i] == NULL ) break;
		else fprintf( fp , "Aliases[%d]: %s\n" , i , host->h_aliases[i] );
#endif // USE_BOOST_SOCKETS
}
#ifndef USE_BOOST_SOCKETS
int inet_aton( const char *cp , struct in_addr *inp )
{
    unsigned int a = 0, b = 0, c = 0, d = 0;
    int n = 0, r;
    unsigned long int addr = 0;
    r = sscanf(cp, "%u.%u.%u.%u%n", &a, &b, &c, &d, &n);
    if (r == 0 || n == 0) return 0;
    cp += n;
    if (*cp) return 0;
    if (a > 255 || b > 255 || c > 255 || d > 255) return 0;
    if (inp) {
        addr += a; addr <<= 8;
        addr += b; addr <<= 8;
        addr += c; addr <<= 8;
        addr += d;
        inp->s_addr = htonl(addr);
    }
    return 1;
}
const char *wstrerror( int err )
{
    switch (err)
	{
        case WSAEINTR: return "Interrupted function call";
        case WSAEACCES: return "Permission denied";
        case WSAEFAULT: return "Bad address";
        case WSAEINVAL: return "Invalid argument";
        case WSAEMFILE: return "Too many open files";
        case WSAEWOULDBLOCK: return "Resource temporarily unavailable";
        case WSAEINPROGRESS: return "Operation now in progress";
        case WSAEALREADY: return "Operation already in progress";
        case WSAENOTSOCK: return "Socket operation on nonsocket";
        case WSAEDESTADDRREQ: return "Destination address required";
        case WSAEMSGSIZE: return "Message too long";
        case WSAEPROTOTYPE: return "Protocol wrong type for socket";
        case WSAENOPROTOOPT: return "Bad protocol option";
        case WSAEPROTONOSUPPORT: return "Protocol not supported";
        case WSAESOCKTNOSUPPORT: return "Socket type not supported";
        case WSAEOPNOTSUPP: return "Operation not supported";
        case WSAEPFNOSUPPORT: return "Protocol family not supported";
        case WSAEAFNOSUPPORT: return "Address family not supported by protocol family"; 
        case WSAEADDRINUSE: return "Address already in use";
        case WSAEADDRNOTAVAIL: return "Cannot assign requested address";
        case WSAENETDOWN: return "Network is down";
        case WSAENETUNREACH: return "Network is unreachable";
        case WSAENETRESET: return "Network dropped connection on reset";
        case WSAECONNABORTED: return "Software caused connection abort";
        case WSAECONNRESET: return "Connection reset by peer";
        case WSAENOBUFS: return "No buffer space available";
        case WSAEISCONN: return "Socket is already connected";
        case WSAENOTCONN: return "Socket is not connected";
        case WSAESHUTDOWN: return "Cannot send after socket shutdown";
        case WSAETIMEDOUT: return "Connection timed out";
        case WSAECONNREFUSED: return "Connection refused";
        case WSAEHOSTDOWN: return "Host is down";
        case WSAEHOSTUNREACH: return "No route to host";
        case WSAEPROCLIM: return "Too many processes";
        case WSASYSNOTREADY: return "Network subsystem is unavailable";
        case WSAVERNOTSUPPORTED: return "Winsock.dll version out of range";
        case WSANOTINITIALISED: return "Successful WSAStartup not yet performed";
        case WSAEDISCON: return "Graceful shutdown in progress";
        case WSAHOST_NOT_FOUND: return "Host not found";
        case WSATRY_AGAIN: return "Nonauthoritative host not found";
        case WSANO_RECOVERY: return "Nonrecoverable name lookup error"; 
        case WSANO_DATA: return "Valid name, no data record of requested type";
        default: return "Unknown error";
    }
}
#endif // !USE_BOOST_SOCKETS