
/* --------------------------------------------------------------------------------
 #PA_GetLongParameter
 #	TcpPlugin.c
 #	source generated by 4D Tcp Wizard
 #	Project : Sockets
 #	author : Pasi
 #	1.3.2006
 #
 # --------------------------------------------------------------------------------*/
// http://www.unixguide.net/network/socketfaq/

// in win put to project properties / Genereal / Additional include directories:
// 4D Tcp API,ppc_h,Sources,./include,./apr/apr/include,./apr/apr-util/include
// maybe also? :Sources\Matrix;Sources\FineLoad;Sources\Tree


#include "MA_Plugin_x64/Includes.h"

#include "MA_Plugin_x64.h"

// ---------------------------------------------------------
// globals
TTcp *Tcp;
static long MA_AprInitRealCount;
static long MA_AprInitCallCount;


/*
 #include <stdio.h>
 #include <string.h>
 
 #if VERSIONWIN
 #include <Windows.h>
 #endif
 */

/* -----------------
 
 PM: do / check these!
 now implemeted:
 CONNECTIONS=3 // default 3
 tcp-no-delay=TRUE or tcp-no-delay=1
 
 The options parameter describes optional parameters for creating the socket. It's a text 
 holding a name of keyword/value pairs, for example "connect-timeout=10 select-timeout=1 
 keep-alive=true".
 
 The following keywords can be used for the options parameter: 
 
 Keyword Type Description 
 
 connect-timeout integer The maximum time to wait for a connection to establish. The 
 default value is 5 seconds. 
 select-timeout float The maximum time slice used to test the connection status 
 while the connection is being established. The default value is 
 0.2 seconds. 
 Increase this value if you have a network with a lot of latency 
 and slow DNS lookups. However increasing it also means that 
 it will block other 4D processes for a longer time. 
 send-timeout float The maximum time slice used to send data. The plugin will 
 yield time to other 4D processes in between time slices while 
 data is being send. The default value is 0.1 second. 
 receive-timeout float The maximum time slice used to read data. The default value is 
 0.1 second. 
 send-buffer-size integer The buffer size for sending data. The default value is 81660 
 bytes. 
 receive-buffer-size integer The buffer size for receiving data. The default value is 81660 
 bytes. 
 keep-alive boolean Enables or disables keep-alive support on the socket. When 
 enabled a packet (called a 'keepalive probe') is sent to the 
 remote system if a long time (by default, more than 2 hours) 
 passes with no other data being sent or received. This packet is 
 designed to provoke an ACK response from the peer. This 
 enables detection of a peer which has become unreachable (e.g. 
 powered off or disconnected from the net). The default value is 
 false. 
 tcp-no-delay boolean If true this value will disable Nagle's buffering algorithm. This 
 will cause network traffic to increase with smaller than needed 
 packets wasting bandwidth. This option should only be used 
 for applications that frequently send small packets of data 
 where timely delivery is important (i.e. keystrokes or mouse 
 movement). 
 
 ----------------- */

long MA_SocketPluginMain( long selector, PA_PluginParameters params )
{
  
	switch( selector )
	{
      // --- MA_Socket General Commands
			
		case eCMD_MA_TCP_SQL_ReInitialize :
			MA_TCP_SQLReinitialize( params );
      break;
      
		case eCMD_MA_TCP_GetVersion :
      MA_TCP_GetVersion( params );
      break;
			
		case eCMD_MA_TCP_Get_Last_Error_Text :
			MA_TCP_GetLastErrorText( params );
			break;
      
		case eCMD_MA_Get_Host_Name :
			MA_GetHostName( params );
			break;
      
		case eCMD_MA_Get_Host_By_Name :
			MA_GetHostByName( params );
			break;
      
		case eCMD_MA_Get_Host_By_Address :
			MA_GetHostByAddress( params );
			break;
      
		case eCMD_MA_Get_Local_Addresses :
			MA_GetLocalAddresses( params );
			break;
      
      // --- TCP Commands
      
		case eCMD_MA_TCP_Connect :
			MA_TCP_Connect( params );
			break;
      
		case eCMD_MA_TCP_Close :
			MA_TCP_Close( params );
			break;
      
		case eCMD_MA_TCP_Listen :
			MA_TCP_Listen( params );
			break;
      
		case eCMD_MA_TCP_Accept :
			MA_TCP_Accept( params );
			break;
      
		case eCMD_MA_TCP_Send_Blob :
			MA_TCP_SendBlob( params );
			break;
      
		case eCMD_MA_TCP_Receive_Blob :
			MA_TCP_ReceiveBlob( params );
			break;
      
		case eCMD_MA_TCP_Lookahead_Blob :
			MA_TCP_LookaheadBlob( params );
			break;
			
		case eCMD_MA_TCP_Get_OS_Socket :
			MA_TCP_GetOSSocket( params );
			break;
			
		case eCMD_MA_TCP_Get_State :
			MA_TCP_GetState( params );
			break;
      
		case eCMD_MA_TCP_Get_Local_Address :
			MA_TCP_GetLocalAddress( params );
			break;
      
		case eCMD_MA_TCP_Get_Remote_Address :
			MA_TCP_GetRemoteAddress( params );
			break;
      
      
		case eCMD_MA_TCP_Set_Poll_Arrays :
			MA_TCP_SetPollArrays( params );
			break;
      
		case eCMD_MA_TCP_Ceate_Pollset :
			MA_TCP_CreatePollset( params );
			break;
			
		case eCMD_MA_TCP_Destroy_Pollset :
			MA_TCP_DestroyPollset( params );
			break;
      
		case eCMD_MA_TCP_Poll :
			MA_TCP_Poll( params );
			break;
      
		case eCMD_MA_TCP_Get_Poll_Counts :
			MA_TCP_GetPollCounts( params );
			break;
            
      // default
		default:
			selector = -1; // was not used
			break;
			/*
       case eCMD_MA_TCP_Send :
       MA_TCP_Send( params );
       break;
       
       case eCMD_MA_TCP_Receive :
       MA_TCP_Receive( params );
       break;
       
       case eCMD_MA_TCP_Lookahead :
       MA_TCP_Lookahead( params );
       break;
       
       case eCMD_MA_TCP_Get_SSL_Verify_Result :
       MA_TCP_Get_SSL_Verify_Result( params );
       break;
       
       case eCMD_MA_TCP_Is_Secure_Connection :
       MA_TCP_Is_Secure_Connection( params );
       break;
       
       // --- UDP Commands
       
       case eCMD_MA_UDP_Open :
       MA_UDP_Open( params );
       break;
       
       case eCMD_MA_UDP_Listen :
       MA_UDP_Listen( params );
       break;
       
       case eCMD_MA_UDP_Close :
       MA_UDP_Close( params );
       break;
       
       case eCMD_MA_UDP_Send :
       MA_UDP_Send( params );
       break;
       
       case eCMD_MA_UDP_Send_Blob :
       MA_UDP_Send_Blob( params );
       break;
       
       case eCMD_MA_UDP_Receive :
       MA_UDP_Receive( params );
       break;
       
       case eCMD_MA_UDP_Receive_Blob :
       MA_UDP_Receive_Blob( params );
       break;
       
       case eCMD_MA_UDP_Join_Group :
       MA_UDP_Join_Group( params );
       break;
       
       case eCMD_MA_UDP_Leave_Group :
       MA_UDP_Leave_Group( params );
       break;
       
       case eCMD_MA_UDP_Set_Max_Router_Hops :
       MA_UDP_Set_Max_Router_Hops( params );
       break;
       
       case eCMD_MA_UDP_Send_To_Self :
       MA_UDP_Send_To_Self( params );
       break;
       
       // --- IPC Commands
       
       case eCMD_MA_IPC_New_Channel :
       MA_IPC_New_Channel( params );
       break;
       
       case eCMD_MA_IPC_Delete_Channel :
       MA_IPC_Delete_Channel( params );
       break;
       
       case eCMD_MA_IPC_Get_Channel_Name :
       MA_IPC_Get_Channel_Name( params );
       break;
       
       case eCMD_MA_IPC_Send :
       MA_IPC_Send( params );
       break;
       
       case eCMD_MA_IPC_Broadcast :
       MA_IPC_Broadcast( params );
       break;
       
       case eCMD_MA_IPC_Receive :
       MA_IPC_Receive( params );
       break;
       
       case eCMD_MA_IPC_Count_Messages :
       MA_IPC_Count_Messages( params );
       break;
       
       case eCMD_MA_IPC_Get_Message_By_Index :
       MA_IPC_Get_Message_By_Index( params );
       break;
       
       case eCMD_MA_IPC_Count_Channels :
       MA_IPC_Count_Channels( params );
       break;
       
       case eCMD_MA_IPC_Get_Channel_By_Index :
       MA_IPC_Get_Channel_By_Index( params );
       break;
       
       case eCMD_MA_IPC_Get_Channel_By_Name :
       MA_IPC_Get_Channel_By_Name( params );
       break;
       
       */
      
	}
  return selector;
}


void InitTcp() // called from MA_Plugin_x64.cpp
{
	Tcp  = new TTcp();
}

void DeInitTcp() // called from MA_Plugin_x64.cpp
{
	delete Tcp;
}


void MA_apr_terminate() // called from MA_Plugin_x64.cpp
{
	MA_AprInitCallCount--;
	if( MA_AprInitCallCount == 0 ){
		MA_AprInitRealCount--;
		apr_terminate2();
	}
}

void MA_apr_initialize() // called from MA_Plugin_x64.cpp
{
	if( MA_AprInitCallCount == 0 ){
		MA_AprInitRealCount++;
		apr_initialize();
	}
	MA_AprInitCallCount++;
}


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


// "MA_TCP_CreatePollset(&L,&L):L"
void MA_TCP_CreatePollset( PA_PluginParameters params )
{
  long returnValue = kNoErr;
  long pollsetMaxSize = PA_GetLongParameter( params, 1 );
  long option = PA_GetLongParameter( params, 2 );
  // no params in
  
  if( Tcp->pollsetId ){
		errTxtSocket = L"Pollset exists, only one pollset is allowed (MA_TCP_CreatePollset)";
    returnValue = -1; // pollset exists
  } else 
    if( pollsetMaxSize < 1 ){
      errTxtSocket = L"Pollset max size can not be less than one (MA_TCP_CreatePollset)";
      returnValue = -1; 
    } else {
      
      returnValue = Tcp->CreatePollset( pollsetMaxSize, option );
      
    }
  
	PA_ReturnLong( params, returnValue );
};




// "MA_TCP_DestroyPollset(&L):L"
void MA_TCP_DestroyPollset( PA_PluginParameters params )
{
  long returnValue = kNoErr;
  long pollsetId = PA_GetLongParameter( params, 1 );
  
  if( !Tcp->pollsetId ){
		errTxtSocket = L"Pollset does not exist (MA_TCP_DestroyPollset)";
    returnValue = -1;
  } else if( pollsetId != Tcp->pollsetId ){
		errTxtSocket = L"Pollset id was wrong (MA_TCP_DestroyPollset)";
    returnValue = -2;
  }
  
  Tcp->DestroyPollset();
  
  PA_ReturnLong( params, returnValue );
};


// "MA_TCP_SetPollArrays(&L,&Y;&Y;&Y):L"
void MA_TCP_SetPollArrays( PA_PluginParameters params )
{
  long returnValue = kNoErr;
  long pollsetId = PA_GetLongParameter( params, 1 );
  
  if( !Tcp->pollsetId ){
		errTxtSocket = L"Pollset does not exist (MA_TCP_SetPollArrays)";
    returnValue = -1;
  } else if( pollsetId != Tcp->pollsetId ){
		errTxtSocket = L"Pollset id was wrong (MA_TCP_SetPollArrays)";
    returnValue = -2;
  } else {
    // ok
    long count, arrIndex, socketNum, socketOption;
    
    PA_Variable arrSocketNum    = PA_GetVariableParameter( params, 2 );
    PA_Variable arrSocketOption = PA_GetVariableParameter( params, 3 );
    PA_Variable arrSocketValid  = PA_GetVariableParameter( params, 4 );
    
    if ( returnValue == kNoErr ) {	
      
      if ( arrSocketNum.fType != eVK_ArrayLongint ) {
        returnValue = -11;
        errTxtSocket = L"Parameter 2 is not a longint array (MA_TCP_SetPollArrays)";
      } else if ( arrSocketOption.fType != eVK_ArrayLongint ) {
        returnValue = -12;
        errTxtSocket = L"Parameter 3 is not a longint array (MA_TCP_SetPollArrays)";
      } else if ( arrSocketOption.fType != eVK_ArrayLongint ) {
        returnValue = -13;
        errTxtSocket = L"Parameter 4 is not a longint array (MA_TCP_SetPollArrays)";
      }      
      
      
      // add new sockets to pollset
      count       = PA_GetArrayNbElements( arrSocketNum );
      long count2 = PA_GetArrayNbElements( arrSocketOption );
      long count3 = PA_GetArrayNbElements( arrSocketValid );
      if( count < 1 ) { // we may poll for ex. 2 first events from 5 events array 
        returnValue = -14;
        errTxtSocket = L"Poll array size is smaller than 1 (MA_TCP_SetPollArrays)";
      }	else  if( count != count2 ) { 
        returnValue = -15;
        errTxtSocket = L"Poll option array size is not equal to poll array size (MA_TCP_SetPollArrays)";
      } else  if( count != count3 ) { 
        returnValue = -16;
        errTxtSocket = L"Poll valid array size is not equal to poll array size (MA_TCP_SetPollArrays)";
      }
      
      
    }
    
    if ( returnValue == kNoErr ) {		
      // clear old pollset before creating new (safer this way)
      Tcp->ClearPollset();
      Tcp->pollSocketApr.clear();
      Tcp->pollSocketOption.clear();
      
      // pollSocketId.clear();     // not in use currently
      // copy arrays to c vectors
      
      apr_socket_t *apr_socket;
      errTxtSocket = L""; // fill many errors 4
      long goodSocketCount = 0;
      
      for ( arrIndex = 0; arrIndex < count; arrIndex++ ) {
        socketNum    = PA_GetLongintInArray( arrSocketNum, arrIndex + 1 );
        socketOption = PA_GetLongintInArray( arrSocketOption, arrIndex + 1 );
        apr_socket   = NULL;
        
        // find server or normal socket
        TServer *server = Tcp->GetServerByRef( socketNum, L"" );
        if ( server ){
          apr_socket = server->GetAprSocket();
        } else {
          TSocket *socket = Tcp->GetSocketByRef( socketNum, L"-MA_TCP_SetPollArrays" ); // err message has been set here
          if( socket ) {
            apr_socket = socket->GetAprSocket();
          }
        }
        
        if( !apr_socket ){
          returnValue = -4;
          errTxtSocket = errTxtSocket + L"Array item is not a valid socket " + String( socketNum ) + L"(MA_TCP_SetPollArrays)" + ksCR;
          PA_SetLongintInArray( arrSocketValid, arrIndex + 1, -1); 
          // continue loop and add valid sockets
          
        } else {
          Tcp->pollSocketApr.push_back( apr_socket );
          Tcp->pollSocketOption.push_back( (apr_int16_t)socketOption );
          PA_SetLongintInArray( arrSocketValid, arrIndex + 1, 1);
          goodSocketCount++;
        }
      } // end for
      
      if (( returnValue != kNoErr ) && ( returnValue != -4 )) {
        Tcp->ClearPollset(); // error text is set before
        Tcp->pollSocketApr.clear();
        Tcp->pollSocketOption.clear();
      } else {
        if( Tcp->pollSocketApr.size() < 1) {
          returnValue = -5;
          errTxtSocket = L"No sockets to add (MA_TCP_SetPollArrays)";
        } else {
          returnValue = Tcp->AddSocketsToPollset(); // will add all sockets in pollSocketApr vector
          if( returnValue != kNoErr ) {
            errTxtSocket = L"Adding sockets to pollset failed (MA_TCP_SetPollArrays)";
          } else {
            returnValue = goodSocketCount;
          }
        }
      }
      
    } // if ( returnValue == kNoErr )
    
  } 
  PA_ReturnLong( params, returnValue );
};



// "MA_TCP_Poll(&L;&L;&Y;&Y):L"
void MA_TCP_Poll( PA_PluginParameters params )
{
  long returnValue = kNoErr;
  long pollsetId = PA_GetLongParameter( params, 1 );
  
  if( !Tcp->pollsetId ){
		errTxtSocket = L"Pollset does not exist (MA_TCP_Poll)";
    returnValue = -1;
  } else if( pollsetId != Tcp->pollsetId ){
		errTxtSocket = L"Pollset id was wrong (MA_TCP_Poll)";
    returnValue = -2;
  } else if( Tcp->pollSocketApr.size() < 1 ) {
    returnValue = -3;
    errTxtSocket = L"Poll item count < 1 (MA_TCP_Poll)";
  } else {
    
    // ok
    // Poll for events 
    
    long maxDelayCount  = PA_GetLongParameter( params, 2 );
    long delayCount = 0;
    while ( delayCount <= maxDelayCount ){ 
      returnValue = Tcp->Poll(); // shall wait up to timeout milliseconds
      if( returnValue != 0 ){
        break; // exit loop
      } else {
        delayCount++;
        if ( delayCount <= maxDelayCount ){
          PA_YieldAbsolute();  // MUST be PA_YieldAbsolute(); - not PA_Yield();
        }
      }
    }
    
    if( returnValue < 0 ){
      errTxtSocket = L"Poll error " + errTxtSocket + L" (MA_TCP_Poll)";
    } else 
      // returnValue is usually == 0
      if( returnValue > 0 ){	
        
        long signalledSocketCount = returnValue; // return value is now signalled socket count    
        returnValue = kNoErr;
        
        // no need to read arr params before this
        PA_Variable tcpPollArrReturnedIndex = PA_GetVariableParameter( params, 3 );
        PA_Variable tcpPollArrReturnedEvent = PA_GetVariableParameter( params, 4 );
        
        // must be longint arrays
        if ( tcpPollArrReturnedIndex.fType != eVK_ArrayLongint ) {
          returnValue = -13;
          errTxtSocket = L"Parameter 2 is not a longint array (MA_TCP_Poll)";
        } else if ( tcpPollArrReturnedEvent.fType != eVK_ArrayLongint ) {
          returnValue = -14;
          errTxtSocket = L"Parameter 3 is not a longint array (MA_TCP_Poll)";
        }
        
        long arrSize = PA_GetArrayNbElements( tcpPollArrReturnedIndex );
        if ( signalledSocketCount > arrSize ) {
          PA_ResizeArray( &tcpPollArrReturnedIndex, signalledSocketCount );
          // A "memory full" error (-108) may be returned if there is not enough room in the heap to allocate the array.
          returnValue = PA_GetLastError();
          // PA_SetVariableParameter( params, 1, arr, 0 );  // later
        }
        
        arrSize = PA_GetArrayNbElements( tcpPollArrReturnedEvent );
        if (( returnValue == kNoErr) && ( signalledSocketCount > arrSize )) {
          PA_ResizeArray( &tcpPollArrReturnedEvent, signalledSocketCount );
          
          // A "memory full" error (-108) may be returned if there is not enough room in the heap to allocate the array.
          returnValue = PA_GetLastError();
          // PA_SetVariableParameter( params, 1, arr, 0 );  // later
        }
        
        if( returnValue != kNoErr ) {	
          errTxtSocket = L"PA_ResizeArray error (" + String(returnValue) + L"), there is not enough room in the heap to allocate the array (if err=108) (MA_TCP_Poll)";
          returnValue = -abs( returnValue );
        } else {
          
          long i, arrIndex;    
          long returnedIndex, returnedEvent; // returnedSocketNum
          unsigned long findIndex;
          apr_socket_t* returnedSocketApr;
          
          for ( i = 0; i < signalledSocketCount; i++ ) {
            arrIndex = i+1;
            
            returnedSocketApr = Tcp->pollsetReturn_pfd[i].desc.s;
            returnedEvent     = Tcp->pollsetReturn_pfd[i].rtnevents;
            
            returnedIndex = -1; // 0 = not found // returnedSocketNum = 0;
            for ( findIndex = 0; findIndex < Tcp->pollSocketApr.size(); findIndex++){
              if( Tcp->pollSocketApr[findIndex] == returnedSocketApr ){
                // returnedSocketNum = Tcp->pollSocketNum[returnIndex];  // not in use currently 
                returnedIndex = findIndex + 1; // 4D indexes start from 1
                break; // exit loop
              }
            } // end for 
            
            PA_SetLongintInArray( tcpPollArrReturnedIndex, arrIndex, returnedIndex); // returnedSocketNum );
            PA_SetLongintInArray( tcpPollArrReturnedEvent, arrIndex, returnedEvent);
            Tcp->pollReceiveCount++;
            
          } // end for signalledSocketCount
          returnValue = signalledSocketCount;
          
          if( signalledSocketCount > arrSize ) {            
            PA_SetVariableParameter( params, 3, tcpPollArrReturnedIndex, 0 );
            PA_SetVariableParameter( params, 4, tcpPollArrReturnedEvent, 0 );
          }
          
        } // if( returnValue != kNoErr)
        
      } // else if returnValue > 0
    
  } // error
  
  PA_ReturnLong( params, returnValue );
};


//"MA_TCP_GetPollCounts(&L;&L;&L):L"
void MA_TCP_GetPollCounts( PA_PluginParameters params )
{
  long returnValue = kNoErr;
	long pollsetId = PA_GetLongParameter( params, 1 );
  
  if( !Tcp->pollsetId ){
		errTxtSocket = errTxtSocket + ksCR + L"Pollset does not exist: " + L" (MA_TCP_GetPollCounts)";
    returnValue = -1;
  } else if( pollsetId != Tcp->pollsetId ){
		errTxtSocket = errTxtSocket + ksCR + L"Pollset id was wrong: " + L" (MA_TCP_GetPollCounts)";
    returnValue = -2;
  } else {
    PA_SetLongParameter( params, 2, Tcp->pollCount );
    PA_SetLongParameter( params, 3, Tcp->pollReceiveCount );
  }
  
	PA_ReturnLong( params, returnValue );
};


void MA_TCP_SQLReinitialize( PA_PluginParameters params )
{
	long returnValue = 0;
	
	DeInitTcp();
	InitTcp();
	
	// in InitTcp:
	MA_AprInitRealCount = 0;
	MA_AprInitCallCount = 0;
	apr_terminate2();
	MA_apr_initialize();
	
	PA_ReturnLong( params, returnValue );
}


void MA_TCP_GetVersion( PA_PluginParameters params )
{
	PA_Unichar returnValue[512]; // should be dynamic?
  //const char *apr_version;
  string apr_version;
  wstring versionStr;
  
  apr_version = apr_version_string();
  // plg_CharToUnichar( apr_version, returnValue, sizeof( returnValue ) ); // sizeof( idUchar ) = 510 / 2 wide == 255
	
  versionStr = ksPlugInName  + L" " + ksPlugInVersion + ksCR + L"APR Version: " + plg_StringToWstring( apr_version );
  plg_WstringToUnichar( versionStr, returnValue, sizeof( returnValue ) );
  
	PA_ReturnString( params, returnValue );
}


void MA_TCP_GetLastErrorText( PA_PluginParameters params )
{
	PA_Unichar returnValue[1024]; // should be dynamic?
	
	// wstring returnStr;
	// long nullChar = 0;
	long socketNum;	
	socketNum = PA_GetLongParameter( params, 1 );
	
	if (socketNum != 0) {
		TSocket *socket = Tcp->GetSocketByRef( socketNum, L"-MA_TCP_GetLastErrorText" );
		
		if( socket ){
			errTxtSocket = socket->GetErrorText();
      //} else if( server ) {
      //	errTxtSocket = server->GetErrorText();
		}	else {
      TServer *server = Tcp->GetServerByRef( socketNum, L"-MA_TCP_GetLastErrorText" );
      if( server ){
        errTxtSocket = server->GetListenSocket()->GetErrorText();
      }	else {
        errTxtSocket = errTxtSocket + ksCR + L"Error: socket was null";
      }
		}
    
	} else {
		errTxtSocket = errTxtSocket; // just return previous set error text
	}
	
	plg_WstringToUnichar( errTxtSocket, returnValue, sizeof(returnValue) );
	errTxtSocket = L""; // clear after read
	PA_ReturnString( params, returnValue );
}

// ----------------------------- MA_Socket General Commands -----------------------------

void MA_GetHostName( PA_PluginParameters params )
{
	PA_Unichar returnValue[256];
  
	// --- write the code of MA_GetHostName here...
  
	PA_ReturnString( params, returnValue );
}

void MA_GetHostByName( PA_PluginParameters params )
{
	wstring Arg1;
	PA_Unichar returnValue[256];
  
	plg_GetWstringParameter( params, 1, Arg1 );
  
	// --- write the code of MA_GetHostByName here...
  
	PA_ReturnString( params, returnValue );
}

void MA_GetHostByAddress( PA_PluginParameters params )
{
	wstring Arg1;
	PA_Unichar returnValue[256];
  
	plg_GetWstringParameter( params, 1, Arg1 );
  
	// --- write the code of MA_GetHostByAddress here...
  
	PA_ReturnString( params, returnValue );
}

void MA_GetLocalAddresses( PA_PluginParameters params )
{
	long returnValue = 0;
	
	long socketNum;
	PA_Unistring *ipAddressUnistring;
	long portNum = 0;
	wstring ipAddressWstring = L"";
	PA_Unichar ipAddressUchar[255];
	
	socketNum = PA_GetLongParameter( params, 1 );
	// plg_GetWstringParameter( params, 2, ipAddress );
  //	ipAddressLen = PA_GetTextParameter( params, 2, ipAddress );
	ipAddressUnistring = PA_GetStringParameter( params, 2 );
	portNum = PA_GetLongParameter( params, 3 );
	
	// --- write the code of MA_TCP_GetRemoteAddress here...
	
	TSocket *socket = Tcp->GetSocketByRef( socketNum, L"-MA_GetLocalAddresses" ); // err message has been set here
	if( socket ) {
		apr_socket_t *apr_socket;
    apr_sockaddr_t *sa;
		apr_status_t	status;
		char ipAddress[255]; // must be same 255 len in apr_socket_addr_get()
		
		apr_socket = socket->GetAprSocket();
		apr_socket_addr_get( &sa, APR_LOCAL, apr_socket );
		status = apr_sockaddr_ip_getbuf(	ipAddress, 255, sa ); // we should check status
		
		ipAddressWstring = plg_StringToWstring(ipAddress);
		portNum = (long)sa->port;
		returnValue = 0;
	} else {
		returnValue = K_SOCKETIDNOTVALID;
		errTxtSocket = errTxtSocket+ ksCR + L"Invalid socket reference: " + String(socketNum) + L" (MA_GetLocalAddresses)";
	}
	
	plg_WstringToUnichar(ipAddressWstring, ipAddressUchar, sizeof(ipAddressUchar) );
	PA_SetUnistring( ipAddressUnistring, ipAddressUchar ); // return back parameter 2 ipAddressUnistring
  /*
   The following comments on top of PA_GetStringParameter in 4DPluginAPI.c will give you the answer
   
   // Use PA_SetUnistring or PA_GetUnistring to set or read the parameter content
   PA_Unistring* PA_GetStringParameter( PA_PluginParameters params, short index )
   
   So to set a string parameter, do the following:
   
   PA_Unistring* aString;
   
   aString = PA_GetStringParameter( params, 1 );
   PA_SetUnistring( aString, L"hello World" );
   */
	PA_SetLongParameter( params, 3, portNum);
	
	PA_ReturnLong( params, returnValue );
}

// --------------------------------- TCP Commands ---------------------------------


void MA_TCP_Connect( PA_PluginParameters params )
{
	wstring address;	// address
	long port;					// port
	wstring options;
	long returnValue = kNOT_DONE_YET;
  
	/*
	 PA_Unichar* Arg1_uchars;
	 PA_Unistring* Arg1;
	 long Arg2;
	 PA_Unichar* Arg3_uchars;
	 PA_Unistring* Arg3;
	 long returnValue;
	 
	 Arg1 = PA_GetStringParameter( params, 1 );
	 Arg1_uchars = PA_GetUnistring( Arg1 );
	 Arg2 = PA_GetLongParameter( params, 2 );
	 Arg3 = PA_GetStringParameter( params, 3 );
	 Arg3_uchars = PA_GetUnistring( Arg3 );
   */
	
	plg_GetWstringParameter( params, 1, address );
	port = PA_GetLongParameter( params, 2 );
	plg_GetWstringParameter( params, 3, options );
  
	// --- write the code of MA_TCP_Connect here...
	
	returnValue = Tcp->NewOutgoingConnectionOpen( address, port, options );
  
	PA_ReturnLong( params, returnValue );
}

void MA_TCP_Close( PA_PluginParameters params )
{
	long refNum, returnValue = 0;
  
	refNum = PA_GetLongParameter( params, 1 );
	// --- write the code of MA_TCP_Close here...
  
	TServer *server = Tcp->GetServerByRef( refNum, L"-MA_TCP_Close" );
	if ( server ){
		returnValue = Tcp->ServerDelete( server );
	} else {
		TSocket *socket = Tcp->GetSocketByRef( refNum, L"-MA_TCP_Close" ); // err message has been set here
		if( socket ) {
			returnValue = Tcp->SocketClose( socket );
		} else {
			returnValue = K_SOCKETIDNOTVALID;
			// MA_TCP_Close 
			// plg_ErrMessage( L"-Invalid server or socket reference: " + String(refNum), L"-MA_TCP_Close" );
			errTxtSocket = errTxtSocket+ ksCR + L"Invalid server or socket reference: " + String(refNum) + L"(MA_TCP_Close)";
		}
	}
	
	PA_ReturnLong( params, returnValue );
}

void MA_TCP_Listen( PA_PluginParameters params )
{
	// start a listener
	// note - it is not necessary to start a new listener after a connection is received
	// the accept operation will spawn a separate listener and this listener instance
	// will continue to wait for the next one
	
	wstring address;
	long portNum;
	wstring options;
	long returnValue = 0;
	
	plg_GetWstringParameter( params, 1, address );
	portNum = PA_GetLongParameter( params, 2 );
	plg_GetWstringParameter( params, 3, options ); // options
  
	// --- write the code of MA_TCP_Listen here...
	
	TServer* server = Tcp->ServerAdd( address, portNum, options );
	if( server ) {
		TSocket* socket = server->GetListenSocket();
		returnValue = socket->GetConnectionId();
	} else {
		returnValue = -1;
	}
	
	PA_ReturnLong( params, returnValue );
}

void MA_TCP_Accept( PA_PluginParameters params )
{
	long serverNum;
	long returnValue = 0;
	
  
	serverNum = PA_GetLongParameter( params, 1 );
  
	// --- write the code of MA_TCP_Accept here...
	TServer *server = Tcp->GetServerByRef( serverNum, L"MA_TCP_Accept" );
	if( server ) {
		returnValue = Tcp->NewIncomingConnection( server, server->Options() );
	}
	else {
		returnValue = -1;
	}
  
	PA_ReturnLong( params, returnValue );
}

void MA_TCP_SendBlob( PA_PluginParameters params )
{
	long socketNum;
	PA_Handle paramBlobHandle;
	long returnValue = 0;
	char *datatosend;
	long offset = 0; //, bytesNeedToSendParam;
	long bytesNeedToSend, blobSize;
	TSocket *socket;
	
	// --- write the code of MA_TCP_SendBlob here...
	socketNum = PA_GetLongParameter( params, 1 );
	
  
  if ( Tcp->currentSocketNum == socketNum ){
		socket = Tcp->currentSocket; // quick optimization for usual case when instance is already right
	} else {
    socket = Tcp->GetSocketByRef( socketNum, L"-MA_TCP_SendBlob" );
  }
  
	if( socket == NULL ) {
		returnValue = -1; // err message has been set in Tcp->GetSocketByRef
	} else {
    
		paramBlobHandle = PA_GetBlobHandleParameter( params, 2 );
		offset = PA_GetLongParameter( params, 3 );
		bytesNeedToSend = PA_GetLongParameter( params, 4 );
		//bytesNeedToSend= (unsigned long)bytesNeedToSendParam;
		blobSize = PA_GetHandleSize(paramBlobHandle);	// how many bytes will we send
    
    apr_size_t bytesToSend;
    
    bytesToSend = blobSize - offset;
		if(( bytesNeedToSend > 0 ) && ( bytesNeedToSend < (long)bytesToSend ) ){
      bytesToSend = bytesNeedToSend;
    }
		
    if( offset < 0 ) {
			returnValue = -11;
		} else if( bytesToSend < 1 ) {
			returnValue = -12;
		} else if( ( (long)bytesToSend + offset ) > blobSize ) {
			returnValue = -13;
		} else {
      datatosend = PA_LockHandle( paramBlobHandle );
			returnValue = socket->SendBytes( datatosend + offset, &bytesToSend );
      offset += bytesToSend; // bytesToSend is now actual bytes sent, offset will be returned
      PA_UnlockHandle( paramBlobHandle );
		}
    
	}
  
  PA_SetLongParameter( params, 3, offset ); // return new send offset
	PA_ReturnLong( params, returnValue );
}


void ReceiveBlob( PA_PluginParameters params, int isReceiveCall ){
  
	long returnValue = 0; // should return 0,1,2 or negative
  TSocket *socket;
	apr_size_t blobLen = 0;
	apr_size_t bytesToRead;
  
	long socketNum = PA_GetLongParameter( params, 1 );    
  if ( Tcp->currentSocketNum == socketNum ){
		socket = Tcp->currentSocket; // quick optimization for usual case when instance is already right
	} else {
    
    if( isReceiveCall ){
      socket = Tcp->GetSocketByRef( socketNum, L"-MA_TCP_ReceiveBlob" );
    } else {
      socket = Tcp->GetSocketByRef( socketNum, L"-MA_TCP_LookaheadBlob" );
    }
    
    if ( !socket ) {
      // only when apr_socket_protocol == APR_PROTO_UDP
      TServer *server;
      if( isReceiveCall ){
        server = Tcp->GetServerByRef( socketNum, L"-MA_TCP_ReceiveBlob" );
      } else {
        server = Tcp->GetServerByRef( socketNum, L"-MA_TCP_LookaheadBlob" );
      }
      if ( server ) {
        socket = server->GetListenSocket();
      }
     }
  }
  
	if( !socket ) {
		returnValue = -1; // err message has been set in Tcp->GetSocketByRef
  //} else if( socket->GetStatus() != SOCKET_STATUS_OPEN ) {
    //returnValue = K_SOCKETCLOSED;
	} else {
    
    
    long maxBytesToRead = PA_GetLongParameter( params, 3 ); // (unsigned long)
    if( ( maxBytesToRead < 0 ) || ( maxBytesToRead > (long)socket->receiveBufferSize )){
      maxBytesToRead = socket->receiveBufferSize; // try to get full buffer
    }
    
    // check should we return from the buffer, bytesToRead WILL get some value here
		if ( socket->readBlobDataLen >= socket->receiveBufferSize ){
			bytesToRead = 0; // return from buffer
		} else {
			if( maxBytesToRead == 0 ){
				if( socket->readBlobDataLen > 0 ){  // ( socket->readBlobDataLen >= socket->receiveBufferSize ){
					bytesToRead = 0; // return from buffer
				} else {
					bytesToRead = socket->receiveBufferSize;  // make real system tcp read call
				}
			} else {
				// maxBytesToRead > 0
				if( ( (long)socket->readBlobDataLen > 0 ) && ( (long)socket->readBlobDataLen < maxBytesToRead ) ){
					bytesToRead = maxBytesToRead - socket->readBlobDataLen; // remove part what we already have in buffer, read rest from tcp
				} else {
					if( ( (long)socket->readBlobDataLen > 0 ) && ( (long)socket->readBlobDataLen >= maxBytesToRead ) ){ // socket->readBufferInUse &&
						bytesToRead = 0; // return from buffer
					} else {
						bytesToRead = maxBytesToRead; // make real system tcp read call
					}
				}
      }
    } 
		// now bytesToRead has been set
    
    if( bytesToRead < 1 ){
      returnValue = 2; // return from the buffer
    } else {
      // make real system tcp read call
      
      //long   currentProcess = PA_GetCurrentProcessNumber();
      long maxDelayCount  = PA_GetLongParameter( params, 4 );
      long delayCount = 0;
      while ( delayCount <= maxDelayCount ){
        if ( socket->readBufferInUse ){ // && isReceiveCall ) {
          returnValue = socket->ReceiveBytes( socket->receiveBufferSize ); // will set return blob
        } else {
          returnValue = socket->ReceiveBytes( bytesToRead ); // will set return blob
        }
        
        if( returnValue ){
          break; // exit loop
        } else {
          delayCount++;
          if ( delayCount <= maxDelayCount ){
            PA_Yield(); //PA_PutProcessToSleep( currentProcess, 0 ); // PA_Yield(); //better with PA_Yield(); - not PA_YieldAbsolute();
          }
        }
      } // end while
      
      if( returnValue < 0 ){
        if( isReceiveCall ){
          errTxtSocket = errTxtSocket+ ksCR + L"ReceiveBytes error: " + String(returnValue) + L" (MA_TCP_ReceiveBlob)";
        } else {
          errTxtSocket = errTxtSocket+ ksCR + L"ReceiveBytes error: " + String(returnValue) + L" (MA_TCP_LookaheadBlob)";
        }
      } else {
        returnValue = 1; // read from tcp
      }
    }
    
    blobLen = socket->readBlobDataLen; // return all that we have (may be 0)
    if( blobLen < 1 ) {
      if( returnValue > 0 ){ // do not change negative error returnValue
        returnValue = 0; // nothing to return even if we did try to read from tcp
      }
    } else {
      
      if( ( maxBytesToRead > 0 ) && ( maxBytesToRead < (long)socket->readBlobDataLen ) ){
        blobLen = maxBytesToRead; // return only needed amount
      }

      if ( !socket->readBlob ) {
        returnValue = -102;
        if( isReceiveCall ){
          errTxtSocket = errTxtSocket+ ksCR + L"Return blob is null (MA_TCP_ReceiveBlob)";
        } else {
          errTxtSocket = errTxtSocket+ ksCR + L"Return blob is null (MA_TCP_LookaheadBlob)";
        }
      } else {
        char	*returnBlobPtr;
        returnBlobPtr = socket->readBlob + socket->readBlobStartPos; // read always from the start of the read blob
        
        PA_Handle paramBlobHandle = PA_GetBlobHandleParameter( params, 2 );
        if ( !paramBlobHandle ) {
          paramBlobHandle = PA_NewHandle( blobLen ); // dead store is ok here, empty (usually 4D $var) blob must be created
        }
        // Note that blob(message_data_ptr) is duplicated in the parameter, so blob(message_data_ptr) still belongs to the plug-in after the call.
        // PA_SetBlobParameter ( params, 2, returnBlobPtr, blobLen );
        plg_SetBlobParameter ( params, 2, returnBlobPtr, blobLen );
        
        if( isReceiveCall ){
          socket->readBlobStartPos += blobLen; // s0 b30-10=s10,b20 (ask40) s10,b50-40=s50,b20
          socket->readBlobDataLen -= blobLen;
          
          if( socket->readBlobDataLen == 0 ){
            socket->readBlobStartPos = 0; // mark as read and free buffer for reuse
          } else if ( (socket->readBlobStartPos + socket->readBlobDataLen) > socket->receiveBufferSize ){
            // blob end goes to spare buffer
            // returnValue = 3;
            long err = socket->MoveReadBlobDataToReadBlobStart(); // move data to start of buffer
            if( err ){
              returnValue = -abs( err );
              errTxtSocket = errTxtSocket+ ksCR + L"Return blob data length error (MA_TCP_ReceiveBlob)";
            }
          //} else {
            // partial answer, all ok
          }
        }
        
      }
    }
    
    /*
     if( blobLen > 0 ) {
     returnPA_Handle = PA_NewHandle( blobLen );
     err = PA_GetLastError();
     returnPA_HandlePtr = PA_LockHandle( returnPA_Handle );
     err = PA_GetLastError();
     PA_MoveBlock( hsrc, returnPA_HandlePtr, blobLen ); 
     err = PA_GetLastError();
     PA_UnlockHandle( returnPA_Handle );
     err = PA_GetLastError();
     
     }	else {
     // returnPA_Handle = PA_NewHandle( 0L );
     }
     
     // PA_SetBlobHandleParameter( params, 2, returnPA_Handle ); // return only this read
     // PA_SetBlobHandleParameter will cause malloc error later		
     // err = PA_GetLastError();
     // Never call DisposeHandle(h) after PA_SetBlobHandleParameter!
     */
  }
  
  PA_SetLongParameter( params, 3, blobLen ); // return read bytes even with error
  PA_ReturnLong( params, returnValue );
}

// "MA_TCP_ReceiveBlob(&L;&O;&L;&L):L"
void MA_TCP_ReceiveBlob( PA_PluginParameters params )
{
  ReceiveBlob( params, 1 ); // delete from buffer in succesful read
}

// "MA_TCP_LookaheadBlob(&L;&O;&L;&L):L"
void MA_TCP_LookaheadBlob( PA_PluginParameters params )
{
  ReceiveBlob( params, 0 ); // leave in buffer
}

void MA_TCP_GetLocalAddress( PA_PluginParameters params )
{
  
  long returnValue = 0;
  long socketNum;
  PA_Unistring *ipAddressUnistring;
  long portNum = 0;
  wstring ipAddressWstring = L"";
  PA_Unichar ipAddressUchar[255];
  
  socketNum = PA_GetLongParameter( params, 1 );
  // plg_GetWstringParameter( params, 2, ipAddress );
  //	ipAddressLen = PA_GetTextParameter( params, 2, ipAddress );
  ipAddressUnistring = PA_GetStringParameter( params, 2 );
  portNum = PA_GetLongParameter( params, 3 );
  
  // --- write the code of MA_TCP_GetRemoteAddress here...
  
  TSocket *socket = Tcp->GetSocketByRef( socketNum, L"-MA_TCP_GetLocalAddress" ); // err message has been set here
  if( socket ) {
    apr_socket_t *apr_socket;
    apr_sockaddr_t *sa;
    apr_status_t	status;
    char ipAddress[255]; // must be same 255 len in apr_socket_addr_get()
    
    apr_socket = socket->GetAprSocket();
    apr_socket_addr_get( &sa, APR_LOCAL, apr_socket );
    status = apr_sockaddr_ip_getbuf(	ipAddress, 255, sa ); // we should check status
    
    ipAddressWstring = plg_StringToWstring(ipAddress);
    portNum = (long)sa->port;
    returnValue = 0;
  } else {
    returnValue = K_SOCKETIDNOTVALID;
    errTxtSocket = errTxtSocket+ ksCR + L"Invalid socket reference: " + String(socketNum) + L" (MA_TCP_GetLocalAddress)";
  }
  
  plg_WstringToUnichar(ipAddressWstring, ipAddressUchar, sizeof(ipAddressUchar) );
  PA_SetUnistring( ipAddressUnistring, ipAddressUchar ); // return back parameter 2 ipAddressUnistring
  PA_SetLongParameter( params, 3, portNum);
  PA_ReturnLong( params, returnValue );
  
}

void MA_TCP_GetRemoteAddress( PA_PluginParameters params )
{
  long returnValue = 0;
  
  long socketNum;
  PA_Unistring *ipAddressUnistring;
  long portNum = 0;
  wstring ipAddressWstring = L"";
  PA_Unichar ipAddressUchar[255];
  
  socketNum = PA_GetLongParameter( params, 1 );
  // plg_GetWstringParameter( params, 2, ipAddress );
  //	ipAddressLen = PA_GetTextParameter( params, 2, ipAddress );
  ipAddressUnistring = PA_GetStringParameter( params, 2 );
  portNum = PA_GetLongParameter( params, 3 );
  
  // --- write the code of MA_TCP_GetRemoteAddress here...
  
  TSocket *socket = Tcp->GetSocketByRef( socketNum, L"-MA_TCP_GetRemoteAddress" ); // err message has been set here
  if( socket ) {
    apr_socket_t *apr_socket;
    apr_sockaddr_t *sa;
    apr_status_t	status;
    char ipAddress[255]; // must be same 255 len in apr_socket_addr_get()
    
    apr_socket = socket->GetAprSocket();
    apr_socket_addr_get( &sa, APR_REMOTE, apr_socket );
    status = apr_sockaddr_ip_getbuf(	ipAddress, 255, sa ); // we should check status
    
    ipAddressWstring = plg_StringToWstring(ipAddress);
    portNum = (long)sa->port;
    returnValue = 0;
  } else {
    returnValue = K_SOCKETIDNOTVALID;
    errTxtSocket = errTxtSocket+ ksCR + L"Invalid socket reference: " + String(socketNum) + L" (MA_TCP_GetRemoteAddress)";
  }
  
  plg_WstringToUnichar(ipAddressWstring, ipAddressUchar, sizeof(ipAddressUchar) );
  PA_SetUnistring( ipAddressUnistring, ipAddressUchar ); // return back parameter 2 ipAddressUnistring
  /*
   The following comments on top of PA_GetStringParameter in 4DPluginAPI.c will give you the answer
   
   // Use PA_SetUnistring or PA_GetUnistring to set or read the parameter content
   PA_Unistring* PA_GetStringParameter( PA_PluginParameters params, short index )
   
   So to set a string parameter, do the following:
   
   PA_Unistring* aString;
   
   aString = PA_GetStringParameter( params, 1 );
   PA_SetUnistring( aString, L"hello World" );
   */
  PA_SetLongParameter( params, 3, portNum);
  
  PA_ReturnLong( params, returnValue );
  
}


void MA_TCP_GetOSSocket( PA_PluginParameters params )
{
  long socketNum;
  long returnValue = K_SOCKETIDNOTVALID;
  
  socketNum = PA_GetLongParameter( params, 1 );
  returnValue = Tcp->GetOSSocket( socketNum );
  
  PA_ReturnLong( params, returnValue );
}

void MA_TCP_GetState( PA_PluginParameters params )
{
  long socketNum;
  long returnValue = K_SOCKETIDNOTVALID;
  
  socketNum = PA_GetLongParameter( params, 1 );
  // --- write the code of MA_TCP_GetState here...
  returnValue = Tcp->GetStatus( socketNum );
  
  /*
   SOCKET_STATUS_CLOSED = 0, // socket is idle, ready to start new connection
   SOCKET_STATUS_INIT, // socket is busy, establishing a new connection
   SOCKET_STATUS_READ, // socket is busy, reading data
   SOCKET_STATUS_WRITE,  // socket is busy, writing data
   SOCKET_STATUS_LISTEN // socket is listening for new connections
   SOCKET_STATUS_OPEN
   SOCKET_STATUS_NEEDS_CLOSE
   SOCKET_STATUS_CONN_ABORTED
   
   TCP Connection Closed (0)
   TCP Connection Established (8)
   TCP Listening (2) 
   
   NTK:
   The "TCP Listening" state only applies to a Tcp->currentServer socket. 
   The state only changes to "closed" if:
   You close the connection.    
   The remote end closes the connection and 
   all incoming data has been read from the buffer.
   
   
   ITK:
   Positive values are current status:
   0 Closed
   no connection exists on this stream
   2 Listen
   listening for an incoming connexion.
   4 SYN received
   incoming connection is being established
   6 SYN sent
   outgoing connection is being established
   8 Established connection is up
   10 FIN Wait 1
   connection is up; close has been sent
   12 FIN Wait 2
   connection is up; close has been sent and acknowledged
   14 Close Wait
   connection is up; close has been received
   16 Closing
   connection is up; close has been issued and received
   18 Last ACK
   connection is up; close has been issued and received
   20 Time Wait
   connection is being broken
   see RFC#793, page 21 
   */
  switch( returnValue )
  {
    case (-1):	// invalid socket
      break;
      
    case SOCKET_STATUS_CLOSED:
      returnValue = 0; // TCP Connection Closed
      break;
      
    case SOCKET_STATUS_INIT:
      returnValue = 6; // TCP Connection opening
      break;
      
    case SOCKET_STATUS_READ:
      returnValue = 8; // TCP Connection Established, out opened socket
      break;
      
    case SOCKET_STATUS_WRITE:
      returnValue = 8; // TCP Connection Established, out opened socket
      break;
      
    case SOCKET_STATUS_LISTEN:
      returnValue = 2; // TCP Listening
      break;
      
    case SOCKET_STATUS_OPEN:
      returnValue = 8; // TCP Connection Established, out opened socket
      break;
      
    case SOCKET_STATUS_NEEDS_CLOSE:
      // Tcp->SocketClose( socket );
      returnValue = 14; // Close Wait, connection is up; close has been received
      break;
      
    case SOCKET_STATUS_CONN_ABORTED:
      // Tcp->SocketClose( socket );
      returnValue = 20; // 20 Time Wait, connection is being broken
      break;
      
    default:
      returnValue = -abs( returnValue ); // closed -2; // unknown?
  }
  
  PA_ReturnLong( params, returnValue );
}


// ----------- old NTK example code ---------------
/*
 void MA_TCP_Send( PA_PluginParameters params )
 {
 long Arg1;
 long Arg2_len;
 char Arg2[32000];
 long returnValue = kNOT_DONE_YET;
 
 Arg1 = PA_GetLongParameter( params, 1 );
 Arg2_len = PA_GetTextParameter( params, 2, Arg2 );
 
 // --- write the code of MA_TCP_Send here...
 
 PA_ReturnLong( params, returnValue );
 
 
 }
 
 void MA_TCP_Receive( PA_PluginParameters params )
 {
 long Arg1;
 long Arg2_len;
 char Arg2[32000];
 long Arg3;
 long returnValue = kNOT_DONE_YET;
 
 Arg1 = PA_GetLongParameter( params, 1 );
 Arg2_len = PA_GetTextParameter( params, 2, Arg2 );
 Arg3 = PA_GetLongParameter( params, 3 );
 
 // --- write the code of MA_TCP_Receive here...
 
 PA_ReturnLong( params, returnValue );
 }
 
 void MA_TCP_Lookahead( PA_PluginParameters params )
 {
 long Arg1;
 long Arg2_len;
 char Arg2[32000];
 long Arg3;
 long returnValue = kNOT_DONE_YET;
 
 Arg1 = PA_GetLongParameter( params, 1 );
 Arg2_len = PA_GetTextParameter( params, 2, Arg2 );
 Arg3 = PA_GetLongParameter( params, 3 );
 
 // --- write the code of MA_TCP_Lookahead here...
 
 PA_ReturnLong( params, returnValue );
 }
 
 
 
 void MA_TCP_Get_SSL_Verify_Result( PA_PluginParameters params )
 {
 long Arg1;
 long Arg2;
 char Arg3[256];
 
 Arg1 = PA_GetLongParameter( params, 1 );
 Arg2 = PA_GetLongParameter( params, 2 );
 plg_GetWstringParameter( params, 3, Arg3 );
 
 // --- write the code of MA_TCP_Get_SSL_Verify_Result here...
 
 }
 
 void MA_TCP_Is_Secure_Connection( PA_PluginParameters params )
 {
 long Arg1;
 long returnValue = kNOT_DONE_YET;
 
 Arg1 = PA_GetLongParameter( params, 1 );
 
 // --- write the code of MA_TCP_Is_Secure_Connection here...
 
 PA_ReturnLong( params, returnValue );
 }
 
 // --------------------------------- UDP Commands ---------------------------------
 
 
 void MA_UDP_Open( PA_PluginParameters params )
 {
 long returnValue = kNOT_DONE_YET;
 
 // --- write the code of MA_UDP_Open here...
 
 PA_ReturnLong( params, returnValue );
 }
 
 void MA_UDP_Listen( PA_PluginParameters params )
 {
 char Arg1[256];
 long Arg2;
 long returnValue = kNOT_DONE_YET;
 
 plg_GetWstringParameter( params, 1, Arg1 );
 Arg2 = PA_GetLongParameter( params, 2 );
 
 // --- write the code of MA_UDP_Listen here...
 
 PA_ReturnLong( params, returnValue );
 }
 
 void MA_UDP_Close( PA_PluginParameters params )
 {
 long Arg1;
 
 Arg1 = PA_GetLongParameter( params, 1 );
 
 // --- write the code of MA_UDP_Close here...
 
 }
 
 void MA_UDP_Send( PA_PluginParameters params )
 {
 long Arg1;
 long Arg2_len;
 char Arg2[32000];
 char Arg3[256];
 long Arg4;
 long returnValue = kNOT_DONE_YET;
 
 Arg1 = PA_GetLongParameter( params, 1 );
 Arg2_len = PA_GetTextParameter( params, 2, Arg2 );
 plg_GetWstringParameter( params, 3, Arg3 );
 Arg4 = PA_GetLongParameter( params, 4 );
 
 // --- write the code of MA_UDP_Send here...
 
 PA_ReturnLong( params, returnValue );
 }
 
 void MA_UDP_Send_Blob( PA_PluginParameters params )
 {
 long Arg1;
 PA_Handle Arg2;
 char Arg3[256];
 long Arg4;
 long returnValue = kNOT_DONE_YET;
 
 Arg1 = PA_GetLongParameter( params, 1 );
 Arg2 = PA_GetBlobHandleParameter( params, 2 );
 plg_GetWstringParameter( params, 3, Arg3 );
 Arg4 = PA_GetLongParameter( params, 4 );
 
 // --- write the code of MA_UDP_Send_Blob here...
 
 PA_ReturnLong( params, returnValue );
 }
 
 void MA_UDP_Receive( PA_PluginParameters params )
 {
 long Arg1;
 long Arg2_len;
 char Arg2[32000];
 char Arg3[256];
 long Arg4;
 long returnValue = kNOT_DONE_YET;
 
 Arg1 = PA_GetLongParameter( params, 1 );
 Arg2_len = PA_GetTextParameter( params, 2, Arg2 );
 plg_GetWstringParameter( params, 3, Arg3 );
 Arg4 = PA_GetLongParameter( params, 4 );
 
 // --- write the code of MA_UDP_Receive here...
 
 PA_ReturnLong( params, returnValue );
 }
 
 void MA_UDP_Receive_Blob( PA_PluginParameters params )
 {
 long Arg1;
 long Arg2_len;
 char Arg2[32000];
 char Arg3[256];
 long Arg4;
 long returnValue = kNOT_DONE_YET;
 
 Arg1 = PA_GetLongParameter( params, 1 );
 Arg2_len = PA_GetTextParameter( params, 2, Arg2 );
 plg_GetWstringParameter( params, 3, Arg3 );
 Arg4 = PA_GetLongParameter( params, 4 );
 
 // --- write the code of MA_UDP_Receive_Blob here...
 
 PA_ReturnLong( params, returnValue );
 }
 
 void MA_UDP_Join_Group( PA_PluginParameters params )
 {
 long Arg1;
 char Arg2[256];
 
 Arg1 = PA_GetLongParameter( params, 1 );
 plg_GetWstringParameter( params, 2, Arg2 );
 
 // --- write the code of MA_UDP_Join_Group here...
 
 }
 
 void MA_UDP_Leave_Group( PA_PluginParameters params )
 {
 long Arg1;
 char Arg2[256];
 
 Arg1 = PA_GetLongParameter( params, 1 );
 plg_GetWstringParameter( params, 2, Arg2 );
 
 // --- write the code of MA_UDP_Leave_Group here...
 
 }
 
 void MA_UDP_Set_Max_Router_Hops( PA_PluginParameters params )
 {
 long Arg1;
 long Arg2;
 
 Arg1 = PA_GetLongParameter( params, 1 );
 Arg2 = PA_GetLongParameter( params, 2 );
 
 // --- write the code of MA_UDP_Set_Max_Router_Hops here...
 
 }
 
 void MA_UDP_Send_To_Self( PA_PluginParameters params )
 {
 long Arg1;
 long Arg2;
 
 Arg1 = PA_GetLongParameter( params, 1 );
 Arg2 = PA_GetLongParameter( params, 2 );
 
 // --- write the code of MA_UDP_Send_To_Self here...
 
 }
 
 // --------------------------------- IPC Commands ---------------------------------
 
 
 void MA_IPC_New_Channel( PA_PluginParameters params )
 {
 long Arg1;
 char Arg2[256];
 long returnValue = kNOT_DONE_YET;
 
 Arg1 = PA_GetLongParameter( params, 1 );
 plg_GetWstringParameter( params, 2, Arg2 );
 
 // --- write the code of MA_IPC_New_Channel here...
 
 PA_ReturnLong( params, returnValue );
 }
 
 void MA_IPC_Delete_Channel( PA_PluginParameters params )
 {
 long Arg1;
 
 Arg1 = PA_GetLongParameter( params, 1 );
 
 // --- write the code of MA_IPC_Delete_Channel here...
 
 }
 
 void MA_IPC_Get_Channel_Name( PA_PluginParameters params )
 {
 long Arg1;
 char returnValue[256];
 
 Arg1 = PA_GetLongParameter( params, 1 );
 
 // --- write the code of MA_IPC_Get_Channel_Name here...
 
 PA_ReturnString( params, returnValue );
 }
 
 void MA_IPC_Send( PA_PluginParameters params )
 {
 long Arg1;
 long Arg2_len;
 char Arg2[32000];
 long Arg3_len;
 char Arg3[32000];
 long Arg4;
 
 Arg1 = PA_GetLongParameter( params, 1 );
 Arg2_len = PA_GetTextParameter( params, 2, Arg2 );
 Arg3_len = PA_GetTextParameter( params, 3, Arg3 );
 Arg4 = PA_GetLongParameter( params, 4 );
 
 // --- write the code of MA_IPC_Send here...
 
 }
 
 void MA_IPC_Broadcast( PA_PluginParameters params )
 {
 char Arg1[256];
 long Arg2_len;
 char Arg2[32000];
 long Arg3_len;
 char Arg3[32000];
 long Arg4;
 
 plg_GetWstringParameter( params, 1, Arg1 );
 Arg2_len = PA_GetTextParameter( params, 2, Arg2 );
 Arg3_len = PA_GetTextParameter( params, 3, Arg3 );
 Arg4 = PA_GetLongParameter( params, 4 );
 
 // --- write the code of MA_IPC_Broadcast here...
 
 }
 
 void MA_IPC_Receive( PA_PluginParameters params )
 {
 long Arg1;
 long Arg2_len;
 char Arg2[32000];
 long Arg3_len;
 char Arg3[32000];
 long Arg4;
 long returnValue = kNOT_DONE_YET;
 
 Arg1 = PA_GetLongParameter( params, 1 );
 Arg2_len = PA_GetTextParameter( params, 2, Arg2 );
 Arg3_len = PA_GetTextParameter( params, 3, Arg3 );
 Arg4 = PA_GetLongParameter( params, 4 );
 
 // --- write the code of MA_IPC_Receive here...
 
 PA_ReturnLong( params, returnValue );
 }
 
 void MA_IPC_Count_Messages( PA_PluginParameters params )
 {
 long Arg1;
 long returnValue = kNOT_DONE_YET;
 
 Arg1 = PA_GetLongParameter( params, 1 );
 
 // --- write the code of MA_IPC_Count_Messages here...
 
 PA_ReturnLong( params, returnValue );
 }
 
 void MA_IPC_Get_Message_By_Index( PA_PluginParameters params )
 {
 long Arg1;
 long Arg2;
 long Arg3_len;
 char Arg3[32000];
 long Arg4_len;
 char Arg4[32000];
 
 Arg1 = PA_GetLongParameter( params, 1 );
 Arg2 = PA_GetLongParameter( params, 2 );
 Arg3_len = PA_GetTextParameter( params, 3, Arg3 );
 Arg4_len = PA_GetTextParameter( params, 4, Arg4 );
 
 // --- write the code of MA_IPC_Get_Message_By_Index here...
 
 }
 
 void MA_IPC_Count_Channels( PA_PluginParameters params )
 {
 long returnValue = kNOT_DONE_YET;
 
 // --- write the code of MA_IPC_Count_Channels here...
 
 PA_ReturnLong( params, returnValue );
 }
 
 void MA_IPC_Get_Channel_By_Index( PA_PluginParameters params )
 {
 long Arg1;
 long returnValue = kNOT_DONE_YET;
 
 Arg1 = PA_GetLongParameter( params, 1 );
 
 // --- write the code of MA_IPC_Get_Channel_By_Index here...
 
 PA_ReturnLong( params, returnValue );
 }
 
 void MA_IPC_Get_Channel_By_Name( PA_PluginParameters params )
 {
 char Arg1[256];
 long returnValue = kNOT_DONE_YET;
 
 plg_GetWstringParameter( params, 1, Arg1 );
 
 // --- write the code of MA_IPC_Get_Channel_By_Name here...
 
 PA_ReturnLong( params, returnValue );
 }
 
 */

// #endif
