

#include "Includes.h"
//#include "Socket.h"

/*
 * Network server sample code
 * Just response to an HTTP GET request, but this is not a true HTTP server.
 * For example, you can test this program as follows,
 *  $ wget http://localhost:8081/etc/hosts
 * @remark Error checks omitted
 */

/*
 * Response a file content as a simple HTTP response protocol.
 * We assume that the request'soc_listeningSocket first line is 'GET file-path HTTP/1.x'
 */
// http://www.unixguide.net/network/socketfaq/

TSocket::TSocket() 
{
	
	//MA_apr_initialize();
	
  apr_pool_create(&apr_pool, NULL);	// apr_pool_t					*apr_pool;
	apr_socket  = NULL;								// apr_socket_t				*apr_socket;
  readBufferInUse = true;
  
	connectionId = -1;
  readBlob = NULL;
  readBlobStartPos = 0;
  readBlobDataLen = 0;
  
  receiveBufferSize = DEFBUFFSIZE;
  sendBufferSize = DEFBUFFSIZE;
  
	lastErrorText = L"no error";
  
	apr_status							= APR_SUCCESS;	// usually error codes from apr calls
	apr_socket_status				= SOCKET_STATUS_DISCONNECTED;
  
	// #define DEF_SOCK_TIMEOUT	(APR_USEC_PER_SEC * 0.001) // =0.001 seconds
  
	apr_socket_timeout_ms =	DEF_SOCK_TIMEOUT_MS; // currently 0, 1 = 1 millisecond
	apr_socket_timeout		= apr_socket_timeout_ms * 1000;
  
	// for pool different timeout?
	//  APR_DEF_POLL_TIMEOUT in milliseconds = 1 sec
	
}

TSocket::~TSocket() 
{
  Close();
  
	apr_pool_destroy(apr_pool);
	//MA_apr_terminate();
  
  if ( readBlob ){
    free( readBlob );
  }
}

long TSocket::ParseOption( wstring options, wstring optionName )
{
  // move these to listen on open!
  long options_len = options.length();
  if( options_len > 0) {
		wstring optionsIn = Substring( options, 1, options_len );
		optionsIn = Replace( optionsIn, ksCR, L" " );
		optionsIn = StringToUpper( optionsIn );
		optionName = StringToUpper( optionName );
		wstring optionValue = L"";
    
		long pos = Position( optionName, optionsIn );
		if( pos < 1 ) {
      return -1; // option not found
    } else {
			pos += optionName.length();
			optionValue = Substring( optionsIn, pos );

		  optionValue += L" "; // add at least one space for parse
			optionValue = Parse( optionValue, L" ", 1 );
			// if (( optionValue == L"1" ) || ( optionValue == L"TRUE" )) {		
			if (( optionValue.compare( L"1" ) != 0 ) || ( optionValue.compare( L"TRUE" ) != 0 )) {				
        return 1;
      }	else if (( optionValue.compare( L"0" ) != 0 ) || ( optionValue.compare( L"FALSE" ) != 0 )) {
        return 0;
      }	else {
        long retVal = StringToNum(optionValue);
        return retVal;
      }
		}	
  }
  return -1; // option not found
}

apr_status_t TSocket::SocketOptionsSet( wstring options, int callType, apr_int32_t timeout )
{
	apr_status_t stat = 0;
  apr_int32_t option;
  
	option = ParseOption(options, L"no-block=");
  if( option < 0 ){
    option = 1; // default value
  }
  stat = apr_socket_opt_set( apr_socket, APR_SO_NONBLOCK, option );
  if ( stat ) {
    // "Problem setting socket option to non-blobk: %d\n", stat);
    SetErrorText( stat );
    return -abs( stat );
  }
  
	option = ParseOption(options, L"reuse-address=");
  if( option < 0 ){
    option = 1; // default value
  }
  stat = apr_socket_opt_set( apr_socket, APR_SO_REUSEADDR, option );
  if ( stat ) {
    SetErrorText( stat );
    return -abs( stat );
  }

  if ( callType == 2 ){  // callType == 2, only listening socket    
    option = timeout;
  } else if ( callType == 1 ){ // callType == 1 only outgoing connction
    option = ParseOption(options, L"connect-timeout="); // use NTK Plugin compatible options
    if( option < (100*1000) ){ // < 0.1 seconds
      option = timeout; // DEF_SOCK_TIMEOUT_OPEN_MS * 1000; // 3 sec
    }
  } else if ( callType == 0 ){ // 0 = normal socket
    option = ParseOption(options, L"timeout="); 
    if( option < 0 ){
      option = timeout; // set normal timeout, should be 0
    } else {
      assert(true); // should NEVER come here
    }
  }
  stat = apr_socket_timeout_set( apr_socket, option );
  if ( stat ) {
    // "Problem setting socket timeout: %d\n", stat);
    SetErrorText( stat );
    return -abs( stat );
  }
  // set timeout end
  
  option = ParseOption(options, L"keep-alive=");
  if( option < 0 ){
    option = 1; // default value
  }
	stat = apr_socket_opt_set( apr_socket, APR_SO_KEEPALIVE, option ); // PM 2008-02-07, was 1
	if ( stat ) {
    // "Problem setting socket option keep-alive: %d\n", stat);
		SetErrorText( stat );
		return -abs( stat );
  }
  
  if ( apr_socket_protocol == APR_PROTO_TCP) {
    option = ParseOption(options, L"tcp-no-delay=");
    if( option < 0 ){
      option = 1; // default value
    }
    stat = apr_socket_opt_set( apr_socket, APR_TCP_NODELAY, option ); // PM 2008-02-07, was 1
    if ( stat ) {
      // "Problem in setting socket option tcp-no-delay: %d\n", stat);
      SetErrorText( stat );
      return -abs( stat );
    }
  }
  
  option = ParseOption(options, L"send-buffer-size=");
  if( option < 0 ){
    option = this->sendBufferSize; // get default
  }
#if VERSIONWIN
  // http://support.microsoft.com/kb/823764

	stat = apr_socket_opt_set( apr_socket, APR_SO_SNDBUF, option + 1 );
	
  apr_int32_t	sndBufSize = 0;
  stat = apr_socket_opt_get(apr_socket, APR_SO_SNDBUF, &sndBufSize );
  if( sndBufSize > 0){
		sndBufSize++;
		stat = apr_socket_opt_set(apr_socket, APR_SO_SNDBUF, sndBufSize );
		sndBufSize = 0;
		stat = apr_socket_opt_get(apr_socket, APR_SO_SNDBUF, &sndBufSize ); // recheck
  } else {
		int winBufSize;
		apr_os_sock_t os_socket = NULL; // typedef int  apr_os_sock_t;  /**< native dir */
		apr_status_t stat = apr_os_sock_get( &os_socket, apr_socket );
		if( os_socket ){
			winBufSize = 0;
			int optlen = sizeof( winBufSize );
			getsockopt( os_socket, SOL_SOCKET, SO_SNDBUF, (char *)&winBufSize, &optlen );
			winBufSize = option + 1;
			setsockopt( os_socket, SOL_SOCKET, SO_SNDBUF, (char *)&winBufSize, optlen );
			winBufSize = 0;
			getsockopt( os_socket, SOL_SOCKET, SO_SNDBUF, (char *)&winBufSize, &optlen );
			winBufSize = winBufSize; // needed for debugger to show previous get value
		}
	}

#else
  stat = apr_socket_opt_set( apr_socket, APR_SO_SNDBUF, option );
#endif
  if ( stat ) {
    SetErrorText( stat );
    return -abs( stat );
  }
  this->sendBufferSize = option; // get default
  
  option = ParseOption(options, L"receive-buffer-size=");
  if( option < 0 ){
    option = this->receiveBufferSize;
  }
  stat = apr_socket_opt_set( apr_socket, APR_SO_RCVBUF, option ); 
  if ( stat ) {
    SetErrorText( stat );
    return -abs( stat );
  }
  this->receiveBufferSize = option;
  
  option = ParseOption(options, L"no-read-buffer=");
  if( option > 0 ){
    this->readBufferInUse = false;
  } else {
    this->readBufferInUse = true;
  } 
  
	return stat;
}

void TSocket::SocketSet( apr_socket_t *new_opened_socket )
{
	apr_socket = new_opened_socket;
  apr_socket_status = SOCKET_STATUS_OPEN;
}

// open apr_socket
long TSocket::Connect( wstring address, long port, wstring options )
{
  
	apr_status_t		stat;
	apr_sockaddr_t	*new_remote_sa;
	apr_socket_t		*new_apr_socket;
	apr_port_t portNum = (apr_port_t)port;
  
	apr_socket_status = SOCKET_STATUS_INIT; // SOCKET_STATUS_DISCONNECTED
  
  // "\tClient:  Making socket address...............");
	string c_address = plg_WstringToString( address );
  
  long option = ParseOption(options, L"ipv6=");
  if( option != 1 ){
      option = 0;
  }
  if( option == 1 ){
    stat = apr_sockaddr_info_get( &new_remote_sa, c_address.c_str(), AF_INET6, portNum, 0, apr_pool);
  } else {
    stat = apr_sockaddr_info_get( &new_remote_sa, c_address.c_str(), AF_INET, portNum, 0, apr_pool);
  }
  if ( stat != APR_SUCCESS ) {
    //"Failed!\n");
    //"Address resolution failed for %s: %s\n", 
    // dest, apr_strerror(stat, msgbuf, sizeof(msgbuf)));
		SetErrorText( stat );
		return -abs( stat );
  }
  
  apr_socket_protocol = APR_PROTO_UDP;
  option = ParseOption(options, L"udp=");
  if( option != 1 ){
    option = ParseOption(options, L"sctp=");
    if( option == 1 ){
      apr_socket_protocol = APR_PROTO_SCTP;
      option = 2;
    } else {
      option = ParseOption(options, L"sctp-dgarm=");
      if( option == 1 ){
        apr_socket_protocol = APR_PROTO_SCTP;
        option = 3;
      } else {
        apr_socket_protocol = APR_PROTO_TCP;
        option = 0;
      }
    }
  }
  if( option == 1 ){
    stat = apr_socket_create( &new_apr_socket, new_remote_sa->family, SOCK_DGRAM, APR_PROTO_UDP, apr_pool);
  } else if ( option == 2 ){
    stat = apr_socket_create( &new_apr_socket, new_remote_sa->family, SOCK_STREAM, APR_PROTO_SCTP, apr_pool);
  } else if ( option == 3 ){
    stat = apr_socket_create( &new_apr_socket, new_remote_sa->family, SOCK_DGRAM, APR_PROTO_SCTP, apr_pool);
  } else {
    stat = apr_socket_create( &new_apr_socket, new_remote_sa->family, SOCK_STREAM, APR_PROTO_TCP, apr_pool);
  }
  if ( stat != APR_SUCCESS ) {
		// "Couldn't create socket\n");
		SetErrorText( stat );
		return -abs( stat );
	}
  
	SocketSet( new_apr_socket ); // new_apr_socket will be apr_socket after SocketSet
  apr_socket_address = new_remote_sa; // MUST be here
  
  // it is a good idea to specify socket options explicitly.
	stat = SocketOptionsSet( options, 1, DEF_SOCK_TIMEOUT_OPEN_MS * 1000 ); // kTrue = set connect timeout
  if ( stat != APR_SUCCESS ) {
		SetErrorText( stat );
		return -abs( stat );
  }
  
  stat = apr_socket_connect( apr_socket, new_remote_sa ); //, new_remote_sa
  if ( stat != APR_SUCCESS ) {
		// apr_socket_status = SOCKET_STATUS_NEEDS_CLOSE;	// apr_socket_close( apr_socket );
		SetErrorText( stat );
		return -abs( stat );
  }
  
  stat = SocketOptionsSet( options, 0, apr_socket_timeout ); // MUST call again, not connect call any more
  if ( stat != APR_SUCCESS ) {
		// apr_socket_status = SOCKET_STATUS_NEEDS_CLOSE;	// apr_socket_close( apr_socket );
		SetErrorText( stat );
		return -abs( stat );
  }
  
  return APR_SUCCESS;
}

void TSocket::SetConnectionId(long id) {
	if( connectionId == -1 )
		connectionId = id; 
	else
		connectionId = id; // bug here, should not be set twice
}

socket_status_t TSocket::TestStatus() 
{	
	apr_int32_t disconnected = 0;
	
	//if( true ) { // PM ???, true or false, or true only in windows?
  apr_socket_opt_get(apr_socket, APR_SO_DISCONNECTED, &disconnected);
  if( disconnected ) {
    apr_socket_status = SOCKET_STATUS_CONN_ABORTED;
  }
	//} 
	
	if( !disconnected ) {
    apr_status = ReceiveBytes( 1 );  // read 1 byte, leave in buffer
		// SetErrorText( apr_status ); // not actually an error
	}
	
	return apr_socket_status;
}

// closing apr_socket finally tells web browser that everything was received
void TSocket::Close() 
{
	if ( apr_socket_status != SOCKET_STATUS_CLOSED ) {
		if( apr_socket_status != SOCKET_STATUS_INIT ) { // not yet opened
			apr_socket_close(apr_socket);
		}
		apr_socket_status = SOCKET_STATUS_CLOSED;
	} else {
		//apr_socket_status= apr_socket_status; // apr_socket_close(apr_socket);  // this will crash
	}
}

long TSocket::SocketStatusSet() {
	long err;
	
	err = apr_status;
  
	return err;
}

void TSocket::SetErrorText(apr_status_t status) {
	char errbuf[512]; // why this is needed?, why not lastErrorText = apr_strerror (apr_status);
  
	if ( status == 0 ) {
		lastErrorText = L"0: no error";
	} else {
		apr_status = status;
		lastErrorText =  String( (long)status ) + L": ";
		lastErrorText += plg_StringToWstring( apr_strerror (apr_status, errbuf, sizeof (errbuf)) );
	}
  
}


void TSocket::SetTimeout(long timeoutIn ) {
  apr_interval_time_t timeout = timeoutIn;
  
	apr_status = apr_socket_timeout_set( apr_socket, timeout );
	if( apr_status ) {
		SetErrorText( apr_status );
	}
  return;
}

long TSocket::GetTimeout() {
	apr_status_t	stat;
	apr_interval_time_t		timeout_apr;
	apr_int32_t						timeout;
  
	stat = apr_socket_timeout_get( apr_socket, &timeout_apr ); 
	if( stat )
		return -abs( stat );
  
  timeout = (apr_int32_t)timeout_apr;	// check this code!
	if( apr_socket_timeout != timeout) 
		apr_socket_timeout = timeout;  // logical error here!
  
	if( apr_socket_timeout_ms != (apr_socket_timeout * 1000) ) 
		apr_socket_timeout_ms = apr_socket_timeout * 1000;  // logical error here!
  
  return (long)apr_socket_timeout_ms;
}


long TSocket::MoveReadBlobDataToReadBlobStart() {
  /* void * memmove ( void * destination, const void * source, size_t num );
   Move block of memory
   Copies the values of num bytes from the location pointed by source to the memory block pointed by destination. 
   Copying takes place as if an intermediate buffer were used, allowing the destination and source to overlap.
   */
  if( readBlobDataLen < 1 ){
    return -121; // should NEVER come here
  } else if( readBlobStartPos < receiveBufferSize ){ // readBlobStartPos shoul be on spare receive buffer side
    return -122; // should NEVER come here
  } else {
    memmove( readBlob, readBlob + readBlobStartPos, readBlobDataLen ); // move to start
    readBlobStartPos = 0;
    // readBlobDataLen = readBlobDataLen; // data len stays the same
  }
  return 0;
}

long TSocket::ReceiveBytes( apr_size_t maxBytesToRead ) 
{
	long returnValue = 0; // = nothing read
  apr_size_t sizeOfBuffer;
  
  if( apr_socket_status != SOCKET_STATUS_OPEN ) {
    if ( !( (apr_socket_status == SOCKET_STATUS_LISTEN ) && ( apr_socket_protocol == APR_PROTO_UDP ) ) ) {
      return K_SOCKETCLOSED;
    }
  }
  
  if( !readBlob ){
    readBlob = (char*) malloc( (receiveBufferSize * 2) + 10 ); // alloc spare space 2x receiveBufferSize +10 just to be sure for math
    if( !readBlob ){
      return -100;
    }
    readBlobStartPos = 0;
    readBlobDataLen = 0;
  }
  
  if( ( maxBytesToRead > 0 ) && ( maxBytesToRead < receiveBufferSize ) ){
    sizeOfBuffer = maxBytesToRead;  // try to read only needed amount
  } else {
    sizeOfBuffer = receiveBufferSize;  // try to read full buffer
  }
  // &sizeOfBuffer = how much we can receive, apr_socket_recv() will return actual received bytes to same variable
  if ( readBlobStartPos  > receiveBufferSize ){
		return -101; 
  } else if( readBlobDataLen > ( receiveBufferSize * 2) ){
		return -102; 
  }
  
  // readBlobStartPos is already next start pos
  if ( apr_socket_protocol == APR_PROTO_UDP) {
    /* apr_status_t rv = apr_socket_recvfrom(sa, s, 0, buf, &len);
     from 	The apr_sockaddr_t to fill in the recipient info
     sock 	The socket to use
     flags 	The flags to use
     buf 	The buffer to use
     len 	The length of the available buffer
    */
    // apr_status = apr_socket_recvfrom( apr_socket_address, apr_socket, 0, readBlob + readBlobStartPos + readBlobDataLen, &sizeOfBuffer );
    apr_status = apr_socket_recv( apr_socket, readBlob + readBlobStartPos + readBlobDataLen, &sizeOfBuffer );
  } else {
    apr_status = apr_socket_recv( apr_socket, readBlob + readBlobStartPos + readBlobDataLen, &sizeOfBuffer );
  }
  readBlobDataLen += sizeOfBuffer;
  
  // use macros because numbers may have different values
  // in different platforms (XP, Leopard...)
  
  if ( (apr_status != APR_SUCCESS) ) { 
    
    if ( APR_STATUS_IS_EAGAIN(apr_status) ) { 				// EAGAIN				= 730035
      // typical for non-blobking sockets and specially for lookahead-calls
      // 730035 ="A non-blocking socket operation could not be completed immediately."
      
    } else if ( APR_STATUS_IS_TIMEUP(apr_status) ) { 	// TIMEUP = 70007							 // APR_TIMEUP		= 730060
      // for blocking sockets this is a normal case
      
      // possible?
      // APR_STATUS_IS_INCOMPLETE
      // APR_STATUS_IS_ETIMEDOUT
      // APR_STATUS_IS_EINPROGRESS
      
    } else if ( APR_STATUS_IS_ECONNABORTED(apr_status) ) {	// ECONNABORTED	= 730053
      apr_socket_status = SOCKET_STATUS_CONN_ABORTED;
      SetErrorText( apr_status );
      if ( sizeOfBuffer < 1 ){
        returnValue = K_SOCKETCLOSED; // tell that nothing came and socket was closed
      }
      
    } else if ( APR_STATUS_IS_EOF(apr_status) ) { 	
      if ( sizeOfBuffer < 1 ){
        apr_socket_status = SOCKET_STATUS_NEEDS_CLOSE;
        SetErrorText( apr_status );
        returnValue = K_SOCKETCLOSED; // tell that nothing came and socket needs close
      }
    } else { 
      SetErrorText( apr_status );
      returnValue = -abs(apr_status);
    }
  }
  
  if ( returnValue >= 0 ){
    return sizeOfBuffer;
  }
  return returnValue;
}


long TSocket::SendBytes(const char *dataBlob, apr_size_t *sendBytes) 
{
  if( apr_socket_status != SOCKET_STATUS_OPEN ) {
		return K_SOCKETCLOSED; 
	}
  
  int loop;
  apr_size_t bytesSent, bytesToSend, sizeOfBuffer; // how much to send
  
  bytesSent = 0;
  bytesToSend = *sendBytes;

  do {
    
    loop = 0;  // default: do not loop again
    if( bytesToSend > sendBufferSize ) {
      sizeOfBuffer = sendBufferSize;
    } else {
      sizeOfBuffer = bytesToSend;
    }
    
    // readBlobStartPos is already next start pos
    if ( apr_socket_protocol == APR_PROTO_UDP) {
      /* apr_status_t rv = apr_socket_sendto(sock, ctx->mcast_addr, 0, buf, &len);
       sock 	The socket to send from
       where 	The apr_sockaddr_t describing where to send the data
       flags 	The flags to use
       buf 	The data to send
       len 	The length of the data to send
       */
      // apr_status = apr_socket_sendto( apr_socket, apr_socket_address, 0, dataBlob + bytesSent, &sizeOfBuffer );
      apr_status = apr_socket_send( apr_socket, dataBlob + bytesSent, &sizeOfBuffer ); // returns in len actual bytes sent
    } else {
      apr_status = apr_socket_send( apr_socket, dataBlob + bytesSent, &sizeOfBuffer ); // returns in len actual bytes sent
    }
    bytesSent += sizeOfBuffer; 
    *sendBytes = bytesSent;  // return sent bytes in 2nd &parameter
    bytesToSend -= sizeOfBuffer;   
    
    if ( apr_status == APR_SUCCESS ) {
      if ( bytesToSend > 0) {
        loop = 1; // do loop again
      }
    } else {
      // *sendBytes MUST have been set before return statements
      if ( APR_STATUS_IS_ECONNABORTED(apr_status) ) {	// ECONNABORTED	= 730053
        apr_socket_status = SOCKET_STATUS_CONN_ABORTED;
        SetErrorText( apr_status );
        return K_SOCKETCLOSED; // tell that socket was closed
      } else { 
        SetErrorText( apr_status );
        return -abs(apr_status);
      }
    }
  
  } while ( loop ); 
  
  return bytesSent; // no error, return same as 2nd &parameter
}

TListenSocket::TListenSocket() : TSocket()
{
  apr_socket_address = NULL;
  apr_listen_socket_timeout = DEF_LISTEN_SOCK_TIMEOUT_MICROSECONDS;
  return;  // deconstructor
}

TListenSocket::~TListenSocket()
{
  return;  // deconstructor
}

void TListenSocket::Listen( wstring address, long port, wstring options ) {
  
  apr_socket_status  = SOCKET_STATUS_INIT;
  apr_port_t portNum = (apr_port_t)port;
  
  
  long option = ParseOption(options, L"ipv6=");
  if( option != 1 ){
    option = 0;
  }
  if( option == 1 ){
    apr_status = apr_sockaddr_info_get( &apr_socket_address, NULL, AF_INET6, portNum, 0, apr_pool );
  } else {
    apr_status = apr_sockaddr_info_get( &apr_socket_address, NULL, AF_INET, portNum, 0, apr_pool );
  }
  if (apr_status != APR_SUCCESS) {
    goto error;
  }
  
  apr_socket_protocol = APR_PROTO_UDP;
  option = ParseOption(options, L"udp=");
  if( option != 1 ){
    option = ParseOption(options, L"sctp=");
    if( option == 1 ){
      apr_socket_protocol = APR_PROTO_SCTP;
      option = 2;
    } else {
      option = ParseOption(options, L"sctp-dgarm=");
      if( option == 1 ){
        apr_socket_protocol = APR_PROTO_SCTP;
        option = 3;
      } else {
        apr_socket_protocol = APR_PROTO_TCP;
        option = 0;
      }
    }
  }
  if( option == 1 ){
    apr_status = apr_socket_create( &apr_socket, apr_socket_address->family, SOCK_DGRAM, APR_PROTO_UDP, apr_pool );
  } else if ( option == 2 ){
    apr_status = apr_socket_create( &apr_socket, apr_socket_address->family, SOCK_STREAM, APR_PROTO_SCTP, apr_pool );
  } else if ( option == 3 ){
    apr_status = apr_socket_create( &apr_socket, apr_socket_address->family, SOCK_DGRAM, APR_PROTO_SCTP, apr_pool );
  } else {
    apr_status = apr_socket_create( &apr_socket, apr_socket_address->family, SOCK_STREAM, APR_PROTO_TCP, apr_pool );
  }
  if (apr_status != APR_SUCCESS) {
    goto error;
  }
  
  // see: http://dev.ariel-networks.com/apr/apr-tutorial/html/apr-tutorial-13.html
  
  /*
   #ifdef VERSIONWIN // PM 2011-06-03: http://support.microsoft.com/kb/823764
   apr_int32_t	sendBufSize = 0;
   apr_socket_opt_get(apr_socket, APR_SO_SNDBUF, &sendBufSize );
   if( sendBufSize > 0){
   sendBufSize++;
   apr_socket_opt_set(apr_socket, APR_SO_SNDBUF, sendBufSize );
   }
   #endif
   */
  
  //apr_listen_socket_timeout = 0;
  // it is a good idea to specify socket options explicitly.
  // DO SocketOptionsSet BEFORE apr_socket_bind
  apr_status = SocketOptionsSet( options, 2, apr_listen_socket_timeout );
  if ( apr_status != APR_SUCCESS ) {
    goto error;
  }
  
  apr_status = apr_socket_bind( apr_socket, apr_socket_address );
  if (apr_status != APR_SUCCESS) {
    goto error;
  }
  
  // intervals for I/O timeouts, in microseconds
  
  if ( apr_socket_protocol == APR_PROTO_UDP) {
    // http://apr.apache.org/docs/apr/1.3/group__apr__mcast.html
  } else {
    apr_status = apr_socket_listen( apr_socket, DEF_SOCKET_BACKLOG ); // DEF_SOCKET_BACKLOG = SOMAXCONN
    if (apr_status != APR_SUCCESS) {
      goto error;
    }
    
    /* see the tutorial about the reason why we have to specify options again */
    // http://dev.ariel-networks.com/apr/apr-tutorial/html/apr-tutorial-13.html
    apr_status = SocketOptionsSet( options, 2, apr_listen_socket_timeout );
    if ( apr_status != APR_SUCCESS ) {
      goto error;
    }
  }
  
  apr_socket_status = SOCKET_STATUS_LISTEN;
  
  return;
error:
  SetErrorText( apr_status );
  // apr_terminate();
  apr_socket_status = SOCKET_STATUS_CLOSED;
};

TServer::TServer ( wstring address, int port, wstring options)
{
  this->address = address;
  this->port = port;
  this->options = options;
  this->connections = 3; //default connections
  
  long options_len = options.length();
  if( options_len > 0) {
    wstring optionsIn = Substring( options, 1, options_len );
    optionsIn = StringToUpper( optionsIn );
    wstring optionName = L"CONNECTIONS=";
    wstring optionValue = L"";
    
    long pos = Position( optionName, optionsIn );
    if( pos > 0 ) {
      pos += optionName.length();
      optionValue = Substring( optionsIn, pos );
      optionValue += ksCR; // add at least one kcCR for parse
      optionValue = Parse( optionValue, ksCR, 1 );
      long value =  StringToNum( optionValue );
      if ( value > 0 )
        this->connections = value;
    }
  }
  
  // lastError = L"no error";
  
  // MA_apr_initialize();
  apr_pool_create(&server_pool, NULL);
  server_incoming_socket = NULL;
  
  listen_socket = new TListenSocket();
  listen_socket->Listen( address, port, options );
  
}


TServer::~TServer()
{
  // delete[] server_sockets;
  delete listen_socket;
  
  apr_pool_destroy( server_pool );
  // MA_apr_terminate();
}


apr_socket_t* TServer::NewIncomingSocket()
{
  // returns server_incoming_socket if something is coming in
  // returns server_incoming_socket if something is coming in
  
  apr_status_t stat;
  server_incoming_socket = NULL;	// nulling not actually needed, but don't hurt 
  // = this->server_incoming_socket = NULL;
  
  
  stat = apr_socket_accept( &server_incoming_socket, listen_socket->GetAprSocket(), server_pool );
  if (stat != APR_SUCCESS) {
		listen_socket->SetErrorText( stat );
    server_incoming_socket = NULL; // nothing to serve
    return NULL; //this; //this is TServerSocket //apr_socket; 
  }
  
  return server_incoming_socket; //this; //this is TServerSocket //apr_socket; 
  
}

socket_status_t TServer::GetListenStatus()
{ 
  socket_status_t stat;
  
  stat = listen_socket->GetStatus();
  // stat = listen_socket->TestStatus();
  
  return stat;
}

// #endif
