/* LICENSE:
  =========================================================================
    CMPack'04 Source Code Release for OPEN-R SDK 1.1.5-r2 for ERS7
    Copyright (C) 2004 Multirobot Lab [Project Head: Manuela Veloso]
    School of Computer Science, Carnegie Mellon University
    All rights reserved.
  ========================================================================= */


#include <TCPEndpointMsg.h>
#include <UDPEndpointMsg.h>

#include "../headers/EasyNet.h"
#include "../headers/Util.h"

#include <stdio.h>

const bool print_to_file = false;

void printToFile(char *print_me) {
  if(print_to_file) {
    FILE *outfile = fopen("/MS/easynet.txt", "at");
    if(outfile) {
      fprintf(outfile, "%s\n", print_me);
      fclose(outfile);
    }
  }
}

// Error constants

const int NETERR_CONNECTION_INVALID = 1;

// All the following values are based on constants defined in the TCPEndpointMsg.h
// and UDPEndpointMsg.h files included above. If you get an unknown error, instead
// of trying them all you can print the number out and check the last two digits
// against the values declared in one of those files. (the BASE constants are
// multiples of 32000, so they won't affect the last byte or the last three digits)

const int NETERR_TCP_SUCCESS              = int(NETERR_TCP_BASE + TCP_SUCCESS); 
const int NETERR_TCP_FAIL                 = int(NETERR_TCP_BASE + TCP_FAIL);
const int NETERR_TCP_CONNECTION_CLOSED    = int(NETERR_TCP_BASE + TCP_CONNECTION_CLOSED);
const int NETERR_TCP_CONNECTION_RESET     = int(NETERR_TCP_BASE + TCP_CONNECTION_RESET);
const int NETERR_TCP_CONNECTION_TIMEOUT   = int(NETERR_TCP_BASE + TCP_CONNECTION_TIMEOUT);
const int NETERR_TCP_CONNECTION_BUSY      = int(NETERR_TCP_BASE + TCP_CONNECTION_BUSY);
const int NETERR_TCP_ADDRESSINUSE         = int(NETERR_TCP_BASE + TCP_ADDRESSINUSE);
const int NETERR_TCP_ADDRESSERROR         = int(NETERR_TCP_BASE + TCP_ADDRESSERROR);
const int NETERR_TCP_PORT_UNREACHABLE     = int(NETERR_TCP_BASE + TCP_PORT_UNREACHABLE);
const int NETERR_TCP_PROTOCOL_UNREACHABLE = int(NETERR_TCP_BASE + TCP_PROTOCOL_UNREACHABLE);
const int NETERR_TCP_HOST_UNREACHABLE     = int(NETERR_TCP_BASE + TCP_HOST_UNREACHABLE);
const int NETERR_TCP_NETWORK_UNREACHABLE  = int(NETERR_TCP_BASE + TCP_NETWORK_UNREACHABLE);
const int NETERR_TCP_MESSAGE_TOO_LONG     = int(NETERR_TCP_BASE + TCP_MESSAGE_TOO_LONG);
const int NETERR_TCP_TTL_EXCEEDED         = int(NETERR_TCP_BASE + TCP_TTL_EXCEEDED);
const int NETERR_TCP_TIME_EXCEEDED        = int(NETERR_TCP_BASE + TCP_TIME_EXCEEDED);
const int NETERR_TCP_WOULDBLOCK           = int(NETERR_TCP_BASE + TCP_WOULDBLOCK);
const int NETERR_TCP_BUFFER_INVALID       = int(NETERR_TCP_BASE + TCP_BUFFER_INVALID);
const int NETERR_TCP_OPERATION_INVALID    = int(NETERR_TCP_BASE + TCP_OPERATION_INVALID);
const int NETERR_TCP_OPERATION_UNKNOWN    = int(NETERR_TCP_BASE + TCP_OPERATION_UNKNOWN);

const int NETERR_UDP_SUCCESS              = int(NETERR_UDP_BASE + UDP_SUCCESS);
const int NETERR_UDP_FAIL                 = int(NETERR_UDP_BASE + UDP_FAIL);
const int NETERR_UDP_CONNECTION_CLOSED    = int(NETERR_UDP_BASE + UDP_CONNECTION_CLOSED);
const int NETERR_UDP_CONNECTION_BUSY      = int(NETERR_UDP_BASE + UDP_CONNECTION_BUSY);
const int NETERR_UDP_ADDRESSINUSE         = int(NETERR_UDP_BASE + UDP_ADDRESSINUSE);
const int NETERR_UDP_ADDRESSERROR         = int(NETERR_UDP_BASE + UDP_ADDRESSERROR);
const int NETERR_UDP_PORT_UNREACHABLE     = int(NETERR_UDP_BASE + UDP_PORT_UNREACHABLE);
const int NETERR_UDP_PROTOCOL_UNREACHABLE = int(NETERR_UDP_BASE + UDP_PROTOCOL_UNREACHABLE);
const int NETERR_UDP_HOST_UNREACHABLE     = int(NETERR_UDP_BASE + UDP_HOST_UNREACHABLE);
const int NETERR_UDP_NETWORK_UNREACHABLE  = int(NETERR_UDP_BASE + UDP_NETWORK_UNREACHABLE);
const int NETERR_UDP_MESSAGE_TOO_LONG     = int(NETERR_UDP_BASE + UDP_MESSAGE_TOO_LONG);
const int NETERR_UDP_TTL_EXCEEDED         = int(NETERR_UDP_BASE + UDP_TTL_EXCEEDED);
const int NETERR_UDP_TIME_EXCEEDED        = int(NETERR_UDP_BASE + UDP_TIME_EXCEEDED); 
const int NETERR_UDP_BUFFER_INVALID       = int(NETERR_UDP_BASE + UDP_BUFFER_INVALID); 
const int NETERR_UDP_OPERATION_INVALID    = int(NETERR_UDP_BASE + UDP_OPERATION_INVALID); 
const int NETERR_UDP_OPERATION_UNKNOWN    = int(NETERR_UDP_BASE + UDP_OPERATION_UNKNOWN);
const int NETERR_UDP_WOULDBLOCK           = int(NETERR_UDP_BASE + UDP_WOULDBLOCK);



// These are constants rather that #define's to accommodate class methods better
const netMemberCallback BLOCK_LISTEN  = &Net_Class::Block_Dummy_Func;
const netMemberCallback BLOCK_CONNECT = &Net_Class::Block_Dummy_Func;
const netMemberCallback BLOCK_SEND    = &Net_Class::Block_Dummy_Func;
const netMemberCallback BLOCK_RECEIVE = &Net_Class::Block_Dummy_Func;
const netMemberCallback BLOCK_CLOSE   = &Net_Class::Block_Dummy_Func;


// Global variables
NetConnectionItem   Net_Connections;
antStackRef         Net_IPStackRef;
NetError            NetLastError;
OID                 Net_myOID;



// Forward declaration of functions defined further down
NetConnection Net_NewTCPConnection(size_t SendBufSize, size_t RecvBufSize);
NetConnection Net_NewUDPConnection(size_t SendBufSize, size_t RecvBufSize);
void Net_AddConnection(NetConnection);
void Net_RemoveConnection(NetConnection);
bool Net_ConnectionValid(NetConnection);
void Net_InitializeIfNecessary();


NetConnection Net_NewTCPConnection(size_t SendBufSize, size_t RecvBufSize)
{
    NetConnection Connection = new struct NetConnectionStruct; 

    mzero(*Connection);

    Connection->Protocol    = NETPROTOCOL_TCP;
    Connection->This        = NULL;
    Connection->Call        = NULL;
    Connection->SendBufSize = SendBufSize;
    Connection->RecvBufSize = RecvBufSize;
    Connection->SendingSize = 0;
    Connection->MinSend     = 0;
    Connection->MinReceive  = 1;
    Connection->MaxReceive  = RecvBufSize;
    Connection->Listening   = false;
    Connection->Connected   = false;
    Connection->Sending     = false;
    Connection->Receiving   = false;
    Connection->SendFlush   = false;
    Connection->CloseMode   = CLOSE_NONE;
    Connection->Error       = NETERR_TCP_SUCCESS;
    Connection->UserData    = 0;
    Connection->DroppedDataWarningPtr  = NULL;
    Connection->DroppedDataWarningSize = 0;

    NetLastError = NETERR_TCP_SUCCESS;

    Net_InitializeIfNecessary();

    // Create endpoint

    // The ANT engine here wants to be passed a value that is 'slightly bigger'
    // than the largest packet we're going to send. Description on page 8 of the
    // manual is contradictory, but all actual sample programs use 2x, so that's
    // what I'm using here. Note: if buffering is used, the 2x might end up being
    // more than we need, but network packets are usually small in size, so this
    // will likely not be a huge waste of memory

    antEnvCreateEndpointMsg tcpCreateMsg(EndpointType_TCP, SendBufSize * 2);
    tcpCreateMsg.Call(Net_IPStackRef, sizeof(tcpCreateMsg));
    if (tcpCreateMsg.error != ANT_SUCCESS) {
        NetLastError = NETERR_TCP_FAIL;
        return Connection;
    }
    Connection->antEndpoint = tcpCreateMsg.moduleRef;

    // Allocate send buffer
    antEnvCreateSharedBufferMsg sendBufferMsg(SendBufSize);
    sendBufferMsg.Call(Net_IPStackRef, sizeof(sendBufferMsg));
    if (sendBufferMsg.error != ANT_SUCCESS) {
        NetLastError = NETERR_TCP_FAIL;
        return Connection;
    }

    Connection->antSharedSend = sendBufferMsg.buffer;
    // An antSharedBuffer has a different addressing space, so we need
    // to get it 'mapped' onto a valid address for us. 
    Connection->antSharedSend.Map();
    Connection->SendBuffer = (char*)(Connection->antSharedSend.GetAddress());
    Connection->SendingStart = Connection->SendBuffer;
    Connection->SendingStop  = Connection->SendBuffer + Connection->SendBufSize;

    // Allocate receive buffer. Same as above.
    antEnvCreateSharedBufferMsg recvBufferMsg(RecvBufSize);
    recvBufferMsg.Call(Net_IPStackRef, sizeof(recvBufferMsg));
    if (recvBufferMsg.error != ANT_SUCCESS) {
        NetLastError = NETERR_TCP_FAIL;
        // Destroy Send shared buffer
        Connection->antSharedSend.UnMap();
        antEnvDestroySharedBufferMsg destroySendBufMsg(Connection->antSharedSend);
        destroySendBufMsg.Call(Net_IPStackRef, sizeof(antEnvDestroySharedBufferMsg));
        return Connection;
    }
    
    Connection->antSharedRecv = recvBufferMsg.buffer;
    Connection->antSharedRecv.Map();
    Connection->ReceiveBuffer = (char*)(Connection->antSharedRecv.GetAddress());

    return Connection;

}




NetConnection Net_NewUDPConnection(size_t SendBufSize, size_t RecvBufSize)
{
    NetConnection Connection = new struct NetConnectionStruct; 

    mzero(*Connection);

    Connection->Protocol    = NETPROTOCOL_UDP;
    Connection->This        = NULL;
    Connection->Call        = NULL;
    Connection->SendBufSize = SendBufSize;
    Connection->RecvBufSize = RecvBufSize;
    Connection->SendingSize = 0;
    Connection->MinSend     = 0;
    Connection->MinReceive  = 1;
    Connection->MaxReceive  = RecvBufSize;
    Connection->Listening   = false;
    Connection->Connected   = false;
    Connection->Sending     = false;
    Connection->Receiving   = false;
    Connection->SendFlush   = false;
    Connection->CloseMode   = CLOSE_NONE;
    Connection->Error       = NETERR_UDP_SUCCESS;
    Connection->UserData    = 0;
    Connection->DroppedDataWarningPtr  = NULL;
    Connection->DroppedDataWarningSize = 0;

    NetLastError = NETERR_UDP_SUCCESS;

    Net_InitializeIfNecessary();

    // Create endpoint

    // The ANT engine here wants to be passed a value that is 'slightly bigger'
    // than the largest packet we're going to send. Description on page 8 of the
    // manual is contradictory, but all actual sample programs use 2x, so that's
    // what I'm using here. Note: if buffering is used, the 2x might end up being
    // more than we need, but network packets are usually small in size, so this
    // will likely not be a huge waste of memory

    antEnvCreateEndpointMsg udpCreateMsg(EndpointType_UDP, SendBufSize * 2);
    udpCreateMsg.Call(Net_IPStackRef, sizeof(udpCreateMsg));
    if (udpCreateMsg.error != ANT_SUCCESS) {
      printToFile("Cannot create UDP message");
      NetLastError = NETERR_UDP_FAIL;
      return Connection;
    }
    Connection->antEndpoint = udpCreateMsg.moduleRef;

    // Allocate send/receive buffer (in a single allocation, to be faster)
    antEnvCreateSharedBufferMsg sendBufferMsg(SendBufSize + RecvBufSize);
    sendBufferMsg.Call(Net_IPStackRef, sizeof(sendBufferMsg));
    if (sendBufferMsg.error != ANT_SUCCESS) {
      printToFile("Cannont create shared buffer");
      NetLastError = NETERR_UDP_FAIL;
      return Connection;
    }

    Connection->antSharedSend = sendBufferMsg.buffer;
    // An antSharedBuffer has a different addressing space, so we need
    // to get it 'mapped' onto a valid address for us. 
    Connection->antSharedSend.Map();

    // Now assign buffer pointers
    Connection->SendBuffer    = (char*)(Connection->antSharedSend.GetAddress());
    Connection->ReceiveBuffer = Connection->SendBuffer + Connection->SendBufSize;
    Connection->SendingStart  = Connection->SendBuffer;
    Connection->SendingStop   = Connection->ReceiveBuffer;

    return Connection;

}



NetConnection NetDoTCPConnect(NetConnection Connection, IPAddress addr, Port port)
{

    // Connect
    TCPEndpointConnectMsg connectMsg(Connection->antEndpoint,
                          IP_ADDR_ANY, IP_PORT_ANY, addr, port);

    if (Connection->ConnectCallback == BLOCK_CONNECT) {

        connectMsg.Call(Net_IPStackRef, sizeof(TCPEndpointConnectMsg));
        NetLastError = Connection->Error = int(NETERR_TCP_BASE + connectMsg.error);
        if (connectMsg.error != TCP_SUCCESS) {
            // Destroy Send shared buffer
            Connection->antSharedSend.UnMap();
            antEnvDestroySharedBufferMsg destroySendBufMsg(
                                         Connection->antSharedSend);
            destroySendBufMsg.Call(Net_IPStackRef,
                                   sizeof(antEnvDestroySharedBufferMsg));
    
            // Destroy Receive shared buffer
            Connection->antSharedRecv.UnMap();
            antEnvDestroySharedBufferMsg destroyRecvBufMsg(
                                         Connection->antSharedRecv);
            destroyRecvBufMsg.Call(Net_IPStackRef,
                                   sizeof(antEnvDestroySharedBufferMsg));

            // Close (abort) Endpoint
            TCPEndpointCloseMsg closeMsg(Connection->antEndpoint, TRUE);
            closeMsg.Call(Net_IPStackRef, sizeof(TCPEndpointCloseMsg));

            delete Connection;
            return NULL;
        } else {  // if now <blocking connect was successful> 
            Connection->LocalAddress  = connectMsg.lAddress;
            Connection->LocalPort     = connectMsg.lPort;
            Connection->RemoteAddress = connectMsg.fAddress;
            Connection->RemotePort    = connectMsg.fPort;
            Connection->Connected     = true;
            Net_AddConnection(Connection);

            // Unless the receive is blocking, post receive request
            if (Connection->ReceiveCallback != BLOCK_RECEIVE) {
                NetReceive(Connection,
                           Connection->MinReceive, Connection->MaxReceive);
            }

            return Connection;
        } // if <blocking connect returned error or not>

    } else { // if now <connect was non-blocking>

        connectMsg.continuation = (void*)Connection;
        connectMsg.Send(Net_IPStackRef, Net_myOID,
                        Extra_Entry[entryNet_ConnectCont], sizeof(connectMsg));
        return Connection;

    } // if <connect was blocking or not>
}






NetConnection NetTCPConnect_Class(IPAddress addr, Port port,
                 netMemberCallback ConnectCallback, netMemberCallback SendCallback,
                 netMemberCallback ReceiveCallback, netMemberCallback CloseCallback,
                 size_t SendBufSize, size_t RecvBufSize,
                 void* ThisVoid, netCallbackCaller Call)
{

    NetConnection Connection = Net_NewTCPConnection(SendBufSize, RecvBufSize);

    Connection->This = ThisVoid;
    Connection->Call = Call;

    Connection->ConnectCallback = ConnectCallback;
    Connection->SendCallback    = SendCallback;
    Connection->ReceiveCallback = ReceiveCallback;
    Connection->CloseCallback   = CloseCallback;

    if (NetLastError == NETERR_TCP_FAIL) {
        if ((ConnectCallback != NULL) && (ConnectCallback != BLOCK_CONNECT)) {
            (*Call)(ConnectCallback, Connection, 0);
        }
        delete Connection;
        return NULL;
    } // if <there was an error creating the Connection>

    // Now do the bulk of the work (which is in common with the non-Class version) 
    return NetDoTCPConnect(Connection, addr, port);

}



NetConnection NetDoTCPListen(NetConnection Connection, Port port)
{
    // Listen
    TCPEndpointListenMsg listenMsg(Connection->antEndpoint, IP_ADDR_ANY, port);

    if (Connection->ConnectCallback == BLOCK_LISTEN) {

        listenMsg.Call(Net_IPStackRef, sizeof(TCPEndpointConnectMsg));
        NetLastError = Connection->Error = int(NETERR_TCP_BASE + listenMsg.error);
        if (listenMsg.error != TCP_SUCCESS) {
            // Destroy Send shared buffer
            Connection->antSharedSend.UnMap();
            antEnvDestroySharedBufferMsg destroySendBufMsg(
                                         Connection->antSharedSend);
            destroySendBufMsg.Call(Net_IPStackRef,
                                   sizeof(antEnvDestroySharedBufferMsg));
    
            // Destroy Receive shared buffer
            Connection->antSharedRecv.UnMap();
            antEnvDestroySharedBufferMsg destroyRecvBufMsg(
                                         Connection->antSharedRecv);
            destroyRecvBufMsg.Call(Net_IPStackRef,
                                   sizeof(antEnvDestroySharedBufferMsg));

            // Close (abort) Endpoint
            TCPEndpointCloseMsg closeMsg(Connection->antEndpoint, TRUE);
            closeMsg.Call(Net_IPStackRef, sizeof(TCPEndpointCloseMsg));

            delete Connection;
            return NULL;
        } else {  // if now <blocking connect was successful> 
            Connection->LocalAddress  = listenMsg.lAddress;
            Connection->LocalPort     = listenMsg.lPort;
            Connection->RemoteAddress = listenMsg.fAddress;
            Connection->RemotePort    = listenMsg.fPort;
            Connection->Connected     = true;
            Net_AddConnection(Connection);

            // Unless the receive is blocking, post receive request
            if (Connection->ReceiveCallback != BLOCK_RECEIVE) {
                NetReceive(Connection,
                           Connection->MinReceive, Connection->MaxReceive);
            }

            return Connection;
        } // if <blocking connect returned error or not>

    } else { // if now <connect was non-blocking>

        Connection->Listening = true;
        listenMsg.continuation = (void*)Connection;
        listenMsg.Send(Net_IPStackRef, Net_myOID,
                        Extra_Entry[entryNet_ConnectCont], sizeof(listenMsg));
        return Connection;

    } // if <connect was blocking or not>
}



NetConnection NetTCPListen_Class(Port port,
                 netMemberCallback ConnectCallback, netMemberCallback SendCallback,
                 netMemberCallback ReceiveCallback, netMemberCallback CloseCallback,
                 size_t SendBufSize, size_t RecvBufSize,
                 void* ThisVoid, netCallbackCaller Call)
{

    NetConnection Connection = Net_NewTCPConnection(SendBufSize, RecvBufSize);

    Connection->This = ThisVoid;
    Connection->Call = Call;

    Connection->ConnectCallback = ConnectCallback;
    Connection->SendCallback    = SendCallback;
    Connection->ReceiveCallback = ReceiveCallback;
    Connection->CloseCallback   = CloseCallback;

    if (NetLastError == NETERR_TCP_FAIL) {
        if (ConnectCallback != NULL) {
            (*Call)(ConnectCallback, Connection, 0);
        }
        delete Connection;
        return NULL;
    } // if <there was an error creating the Connection>

    // Now do the bulk of the work (which is in common with the non-Class version) 
    return NetDoTCPListen(Connection, port);

}





NetConnection NetDoUDPBind(NetConnection Connection, IPAddress addr, Port port)
{

    // Bind the UDP Endpoint -- to no address or port in particular...
    UDPEndpointBindMsg bindMsg(Connection->antEndpoint, IP_ADDR_ANY, IP_PORT_ANY);

    bindMsg.Call(Net_IPStackRef, sizeof(UDPEndpointBindMsg));
    NetLastError = Connection->Error = int(NETERR_UDP_BASE + bindMsg.error);
    if (bindMsg.error != UDP_SUCCESS) {

      printToFile("NetDoUDPBind: Binding failed");

        // Destroy shared buffer
        Connection->antSharedSend.UnMap();
        antEnvDestroySharedBufferMsg destroySendBufMsg(
                                     Connection->antSharedSend);
        destroySendBufMsg.Call(Net_IPStackRef,
                               sizeof(antEnvDestroySharedBufferMsg));

        // Close Endpoint
        UDPEndpointCloseMsg closeMsg(Connection->antEndpoint);
        closeMsg.Call(Net_IPStackRef, sizeof(UDPEndpointCloseMsg));

        delete Connection;
        return NULL;
    } else {  // if now <bind was successful>
      printToFile("NetDoUDPBind: Binding successful");

        Connection->LocalAddress  = bindMsg.address;
        Connection->LocalPort     = bindMsg.port;
        Connection->DestAddress   = addr;
        Connection->DestPort      = port;
        Net_AddConnection(Connection);

        // If we have a receive callback, post receive request
        if ((Connection->ReceiveCallback != BLOCK_RECEIVE) &&
            (Connection->ReceiveCallback != NULL)) {

	  printToFile("NetDoUDPBind: called receive");
            NetReceive(Connection, Connection->MaxReceive);
        }

        return Connection;
    } // if <blocking connect returned error or not>

}




NetConnection NetUDPBind_Class(IPAddress addr, Port port,
                 netMemberCallback SendCallback, netMemberCallback ReceiveCallback,
                 size_t SendBufSize, size_t RecvBufSize,
                 void* ThisVoid, netCallbackCaller Call)
{

    NetConnection Connection = Net_NewUDPConnection(SendBufSize, RecvBufSize);

    if (NetLastError == NETERR_UDP_FAIL) {

      printToFile("NetUDPBind_Class: Bind failed");

        delete Connection;
        return NULL;
    } // if <there was an error creating the Connection>

    Connection->This = ThisVoid;
    Connection->Call = Call;

    Connection->SendCallback    = SendCallback;
    Connection->ReceiveCallback = ReceiveCallback;

    // Now do the bulk of the work 
    return NetDoUDPBind(Connection, addr, port);

}




NetConnection NetDoUDPListen(NetConnection Connection, Port port)
{

    // Bind the UDP Endpoint to the desired port
    UDPEndpointBindMsg bindMsg(Connection->antEndpoint, IP_ADDR_ANY, port);

    bindMsg.Call(Net_IPStackRef, sizeof(UDPEndpointBindMsg));
    NetLastError = Connection->Error = int(NETERR_UDP_BASE + bindMsg.error);
    if (bindMsg.error != UDP_SUCCESS) {

      printToFile("NetDoUDPListen: Bind failed");

        // Destroy shared buffer
        Connection->antSharedSend.UnMap();
        antEnvDestroySharedBufferMsg destroySendBufMsg(
                                     Connection->antSharedSend);
        destroySendBufMsg.Call(Net_IPStackRef,
                               sizeof(antEnvDestroySharedBufferMsg));

        // Close Endpoint
        UDPEndpointCloseMsg closeMsg(Connection->antEndpoint);
        closeMsg.Call(Net_IPStackRef, sizeof(UDPEndpointCloseMsg));

        delete Connection;
        return NULL;
    } else {  // if now <bind was successful>

      printToFile("NetDoUDPListen: Bind worked");

        Connection->LocalAddress  = bindMsg.address;
        Connection->LocalPort     = bindMsg.port;
        Connection->Listening     = true;
        Net_AddConnection(Connection);

        // If we have a receive callback, post receive request
        if ((Connection->ReceiveCallback != BLOCK_RECEIVE) &&
            (Connection->ReceiveCallback != NULL)) {

	  printToFile("NetDoUDPListen: receive called");

            NetReceive(Connection, Connection->MaxReceive);
        }

        return Connection;
    } // if <blocking connect returned error or not>

}




NetConnection NetUDPListen_Class(Port port,
                 netMemberCallback SendCallback, netMemberCallback ReceiveCallback,
                 size_t SendBufSize, size_t RecvBufSize,
                 void* ThisVoid, netCallbackCaller Call)
{

    NetConnection Connection = Net_NewUDPConnection(SendBufSize, RecvBufSize);

    Connection->This = ThisVoid;
    Connection->Call = Call;

    Connection->SendCallback    = SendCallback;
    Connection->ReceiveCallback = ReceiveCallback;

    if (NetLastError == NETERR_UDP_FAIL) {
      printToFile("NetDoUDPListen_Class: Creation failed");
        delete Connection;
        return NULL;
    } // if <there was an error creating the Connection>

    // Now do the bulk of the work (which is in common with the non-Class version) 
    return NetDoUDPListen(Connection, port);

}





void NetDoConnectCont(ANTENVMSG msg)
{

//FILE* f = fopen("/MS/runlog.txt", "a");
//fprintf(f, "NetDoConnectCont\n");
//fclose(f);

    antEnvMsg* replyMsg = antEnvMsg::Receive(msg);
    NetConnection Connection = (NetConnection)replyMsg->continuation;

    if (Connection->Listening) {
        TCPEndpointListenMsg* listenMsg = (TCPEndpointListenMsg*)replyMsg;
        NetLastError = Connection->Error = int(NETERR_TCP_BASE + listenMsg->error);
        Connection->LocalAddress  = listenMsg->lAddress;
        Connection->LocalPort     = listenMsg->lPort;
        Connection->RemoteAddress = listenMsg->fAddress;
        Connection->RemotePort    = listenMsg->fPort;
    } else {
        TCPEndpointConnectMsg* connectMsg = (TCPEndpointConnectMsg*)replyMsg;
        NetLastError = Connection->Error = int(NETERR_TCP_BASE + connectMsg->error);
        Connection->LocalAddress  = connectMsg->lAddress;
        Connection->LocalPort     = connectMsg->lPort;
        Connection->RemoteAddress = connectMsg->fAddress;
        Connection->RemotePort    = connectMsg->fPort;
    }

    if (Connection->Error == NETERR_TCP_SUCCESS) {
        Connection->Connected = true;
        Connection->Listening = false;
        Net_AddConnection(Connection);

        if ((Connection->SendingSize > Connection->MinSend) ||
             ((Connection->SendFlush == true) && (Connection->SendingSize > 0))) {
            // if there's data pending, do send

            char*  SendingStart = Connection->SendingStart;
            char*  SendingStop  = Connection->SendingStop;
            size_t SendingSize  = Connection->SendingSize;

            TCPEndpointSendMsg sendMsg(Connection->antEndpoint,
                                     (byte*)SendingStart, SendingSize);
            sendMsg.continuation = (void*)Connection;
            sendMsg.Send(Net_IPStackRef, Net_myOID,
                         Extra_Entry[entryNet_SendCont], sizeof(sendMsg));

            // check whether there's more room at start or end of buffer
            // and prepare pointers to accept new data while this is sending
            if ((SendingStart - Connection->SendBuffer) >=
                (SendingStop - (SendingStart + SendingSize))) {
                SendingStop  = SendingStart;
                SendingStart = Connection->SendBuffer;
            } else {
                SendingStart += SendingSize;
            }
            Connection->Sending      = true;
            Connection->SendingStart = SendingStart;
            Connection->SendingStop  = SendingStop;
            Connection->SendingSize  = 0;
            Connection->SendFlush    = false;
            
        } else { // if now <there's no more data pending>
            // reset SendingStart so new sends will queue data from beginning
            Connection->SendingStart = Connection->SendBuffer;
            Connection->Sending      = false;
            Connection->SendingStop  =
                      Connection->SendingStart + Connection->SendBufSize;

            if (Connection->ConnectCallback != NULL) {
                (*Connection->Call)(Connection->ConnectCallback, Connection, 1);
            }

            // Unless the receive is blocking, post receive request
            if (Connection->ReceiveCallback != ::BLOCK_RECEIVE) {
                NetReceive(Connection,
                           Connection->MinReceive, Connection->MaxReceive);
            }

        }
    } else { // if now <connect returned an error>

        /* This is now unnecessary, since connection is destroyed *\
        // purge any pending sends if connect failed
        Connection->SendingStart = Connection->SendBuffer;
        Connection->SendingSize  = 0;
        Connection->SendFlush    = false;
        \*                                                        */

        if (Connection->ConnectCallback != NULL) {
            (*Connection->Call)(Connection->ConnectCallback, Connection, 0);
        }

        // Should still be CLOSE_NONE from the initialization, but just in case...
        Connection->CloseMode = CLOSE_NONE;

        // Send close message. Net_CloseCont will do the rest. 
        TCPEndpointCloseMsg closeMsg(Connection->antEndpoint, TRUE);
        closeMsg.continuation = (void*)Connection;
        closeMsg.Send(Net_IPStackRef, Net_myOID,
                        Extra_Entry[entryNet_CloseCont], sizeof(closeMsg));


    } // if <connect was successful or not>
}








int NetDoUDPSend(NetConnection Connection, IPAddress addr, Port port,
                                        const void *buf, size_t size)
{

    if (Connection->SendCallback != BLOCK_SEND) {

      // Send only if we're not already sending, else set appropriate
      // error and return 0.

      if (Connection->Sending) {
        NetLastError = Connection->Error = NETERR_UDP_CONNECTION_BUSY;
        return 0;
      }

      if (size > Connection->SendBufSize) {

      printToFile("NetDoUDPSend: Data too large");

        // If we're asked to send more data than we can handle, we could just
        // crop it, but it seems more sensible to return an error, so the user
        // can realize there's something wrong, if they check for errors.

        NetLastError = Connection->Error = NETERR_UDP_MESSAGE_TOO_LONG;
        if (Connection->SendCallback != NULL) {
          (*Connection->Call)(Connection->SendCallback, Connection, 0);
        }
        return 0;

      }

      // If the data is not already in the connection's send buffer,
      // copy it there.

      if (buf != Connection->SendBuffer) {
        memcpy(Connection->SendBuffer, buf, size);
      }

      // Now prepare and send the message to the IP Stack

      UDPEndpointSendMsg sendMsg(Connection->antEndpoint, addr, port,
                                (byte*)Connection->SendBuffer, size);
      sendMsg.continuation = (void*)Connection;
      sendMsg.error = UDP_SUCCESS;
      sendMsg.Send(Net_IPStackRef, Net_myOID,
                   Extra_Entry[entryNet_SendCont], sizeof(sendMsg));

      // The error is initialized to UDP_SUCCESS so if the call doesn't touch it
      // we will still receive a success.
      // Now if the error is still UDP_SUCCESS, set the connection as sending and
      // return 1 for success, otherwise return 0 for error.

      if(Connection->Sending) {
	printToFile("Connection->Sending has no business being true here");
      }

      if (sendMsg.error == UDP_SUCCESS) {
        Connection->Sending = true;
        return 1;
      } else {
	// sigh
	Connection->Sending = false;
        return 0;
      }


    } else { // if now <connection is blocking>

        if (Connection->Sending) {
            NetLastError = Connection->Error = NETERR_UDP_CONNECTION_BUSY;
            return 0;
        }

        // Do size checking, as in the non-blocking case
        if (size > Connection->SendBufSize) {
          NetLastError = Connection->Error = NETERR_UDP_MESSAGE_TOO_LONG;
          return 0;
        }

        if (buf != Connection->SendBuffer) {
            memcpy(Connection->SendBuffer, buf, size);
        }

        UDPEndpointSendMsg sendMsg(Connection->antEndpoint, addr, port,
                          (byte*)Connection->SendBuffer, size);
        sendMsg.Call(Net_IPStackRef, sizeof(UDPEndpointSendMsg));
        NetLastError = Connection->Error = int(NETERR_UDP_BASE + sendMsg.error);

        if (sendMsg.error == UDP_SUCCESS) {
          return sendMsg.size;
        } else {
          return 0;
        }

    } // if <connection is non-blocking or blocking>

    // Return 1 for success if we got this far.

    return 1;

}



int NetSend(NetConnection Connection, const void *buf, size_t size)
{

    if (!Net_ConnectionValid(Connection)) {
      printToFile("Trying to send to an invalid connection");

        NetLastError = NETERR_CONNECTION_INVALID;
        return 0;
    }

    if (Connection->Protocol == NETPROTOCOL_UDP) {

      if (Connection->Listening) {
	return NetDoUDPSend(Connection, Connection->RemoteAddress,
			    Connection->RemotePort, buf, size);
      } else { // if <connection was now opened with NetUDPBind>
	return NetDoUDPSend(Connection, Connection->DestAddress,
			    Connection->DestPort,   buf, size);
      } // if <connection was opened with NetUDPListen or NetUDPBind>

    } else {

        if (Connection->SendCallback == BLOCK_SEND) {

            if (Connection->Sending == true) {
                NetLastError = Connection->Error = NETERR_TCP_CONNECTION_BUSY;
                return 0;
            }
            if (size > size_t(Connection->SendingStop - Connection->SendingStart)) {
                NetLastError = Connection->Error = NETERR_TCP_MESSAGE_TOO_LONG;
                return 0;
            }
            if (buf != Connection->SendBuffer) {
                memcpy(Connection->SendingStart + Connection->SendingSize, buf, size);
            }
            Connection->SendingSize += size;
            if (Connection->SendingSize >= Connection->MinSend) { // if have enough bytes
                TCPEndpointSendMsg sendMsg(Connection->antEndpoint,
                              (byte*)Connection->SendingStart, Connection->SendingSize);
                sendMsg.Call(Net_IPStackRef, sizeof(TCPEndpointSendMsg));
                Connection->SendingStart = Connection->SendBuffer;
                Connection->SendingSize  = 0;
                NetLastError = Connection->Error = int(NETERR_TCP_BASE + sendMsg.error);
                if (sendMsg.error == TCP_SUCCESS) {
                    return sendMsg.size;
                } else {
                    return 0;
                }
            } else { // if now <we need to buffer more bytes before sending>
                return int(size);
            }
        } else { // if now <connection is non-blocking>

            char*  SendingStart = Connection->SendingStart;
            char*  SendingStop  = Connection->SendingStop;
            size_t SendingSize  = Connection->SendingSize;

            if ((SendingStart + SendingSize + size) <= SendingStop) {
                // if we have enough room to accept the data 
                if ((size == 0) || (buf == NULL)) {
                    // a call with size == 0 or NULL buf will flush right away
                    Connection->SendFlush = true;
                } else {
                    if (buf != Connection->SendBuffer) {
                        memcpy(SendingStart + SendingSize, buf, size);
                    }
                    SendingSize += size;
                }
                if (((SendingSize >= Connection->MinSend) ||
                                              (Connection->SendFlush == true)) &&
                     (Connection->Sending == false) && (Connection->Connected)) {
                    // Take action only if we gathered enough bytes (or user wants
                    // to flush buffer) and we're ready to send, otherwise exit.

                    // Submit send message
                    TCPEndpointSendMsg sendMsg(Connection->antEndpoint,
                                               (byte*)SendingStart, SendingSize);
                    sendMsg.continuation = (void*)Connection;
                    sendMsg.Send(Net_IPStackRef, Net_myOID,
                                 Extra_Entry[entryNet_SendCont], sizeof(sendMsg));
                    Connection->Sending = true;

                    // check whether there's more room at start or end of buffer
                    // and prepare pointers to accept new data while this is sending
                    if ((SendingStart - Connection->SendBuffer) >=
                        (SendingStop - (SendingStart + SendingSize))) {
                        SendingStop  = SendingStart;
                        SendingStart = Connection->SendBuffer;
                    } else {
                        SendingStart += SendingSize;
                    }
                    SendingSize = 0;
                    Connection->SendingStart = SendingStart;
                    Connection->SendingStop  = SendingStop;
                } // if <we did send data>
                Connection->SendingSize  = SendingSize;
                return 1;

            } else { // if now <we don't have enough room to accept data>
                // clip size to max available and copy that many bytes to buf
                size = SendingStop - (SendingStart + SendingSize);
                memcpy(SendingStart + SendingSize, buf, size);
                SendingSize += size;
                // overwrite last bytes with warning if one is present
                if (Connection->DroppedDataWarningPtr != NULL) {
                    memcpy(SendingStop - Connection->DroppedDataWarningSize,
                           Connection->DroppedDataWarningPtr,
                           Connection->DroppedDataWarningSize);
                }
                if (Connection->Sending == false) {
                    // Submit send message if no other send is in progress
                    TCPEndpointSendMsg sendMsg(Connection->antEndpoint,
                                               (byte*)SendingStart, SendingSize);
                    sendMsg.continuation = (void*)Connection;
                    sendMsg.Send(Net_IPStackRef, Net_myOID,
                                 Extra_Entry[entryNet_SendCont], sizeof(sendMsg));
                    Connection->Sending = true;

                    SendingSize = 0;
                    Connection->SendingStop  = SendingStart;
                    Connection->SendingStart = Connection->SendBuffer;
                }
                Connection->SendingSize = SendingSize;
                return 1;

            } // if <we have enough room to accept data or not>

        } // if <connection is blocking or not>

    } // if <the connection is TCP or UDP>
}


int NetSend(NetConnection Connection, const void *buf)
{
    if (buf != NULL) {
        return NetSend(Connection, buf, strlen((char*)buf)); //  no end \0
    } else {
        return NetSend(Connection, NULL, 0); // flush buffer
    } 
}



void NetDoSendCont(ANTENVMSG msg)
{

    UDPEndpointSendMsg* UDPSendMsg = (UDPEndpointSendMsg*)antEnvMsg::Receive(msg);
    NetConnection Connection = (NetConnection)(UDPSendMsg->continuation);

    if (Connection->Protocol == NETPROTOCOL_UDP) {

        NetLastError = Connection->Error = int(NETERR_UDP_BASE + UDPSendMsg->error);

        // First thing, set this connection to no longer sending

        Connection->Sending = false;

        if (UDPSendMsg->error != UDP_SUCCESS) {
	  if(UDPSendMsg->error!=3) {
	    char temp_buf[128];
	    sprintf(temp_buf, "NetDoSendCont: Continuation reports an error %d",
		    UDPSendMsg->error);
	    printToFile(temp_buf);
	  }
            // If there was an error, pass a 0 to the callback if the connection has one.

	  if (Connection->SendCallback != NULL) {
	    (*Connection->Call)(Connection->SendCallback, Connection, 0);
	  }

        } else { // Send was successful

            // invoke call back if one is specified

            if (Connection->SendCallback != NULL) {
                (*Connection->Call)(Connection->SendCallback, Connection,
                                                            UDPSendMsg->size);
            }

        } // if <send returned an error or not>


    } else { // if <connection is now TCP>

        TCPEndpointSendMsg* TCPSendMsg = (TCPEndpointSendMsg*) UDPSendMsg;
        NetLastError = Connection->Error = int(NETERR_TCP_BASE + TCPSendMsg->error);

        if (TCPSendMsg->error != TCP_SUCCESS) {
            // purge any further, pending operations if this failed (except for close)
            Connection->SendingStart = Connection->SendBuffer;
            Connection->SendingSize  = 0;
            Connection->SendFlush    = false;

            if (Connection->SendCallback != NULL) {
                (*Connection->Call)(Connection->SendCallback, Connection, 0);
            }

            // This is debatable, but maybe the best thing to do is to proceed with a
            // close if one close was queued, even if the last send returned an error.
            // If the user has sent some more data in the send callback above, the close
            // will remain queued anyways.

            if (Connection->CloseMode != CLOSE_NONE) {
                NetClose(Connection, Connection->CloseMode);
            }

        } else { // Send was successful

            if ((Connection->SendingSize > Connection->MinSend) ||
                 ((Connection->SendFlush == true) && (Connection->SendingSize > 0))) {
                // if there's data pending, do send

                char*  SendingStart = Connection->SendingStart;
                char*  SendingStop  = Connection->SendingStop;
                size_t SendingSize  = Connection->SendingSize;

                TCPEndpointSendMsg newSendMsg(Connection->antEndpoint,
                                         (byte*)SendingStart, SendingSize);
                newSendMsg.continuation = (void*)Connection;
                newSendMsg.Send(Net_IPStackRef, Net_myOID,
                             Extra_Entry[entryNet_SendCont], sizeof(newSendMsg));

                // check whether there's more room at start or end of buffer
                // and prepare pointers to accept new data while this is sending
                if ((SendingStart - Connection->SendBuffer) >=
                    (SendingStop - (SendingStart + SendingSize))) {
                    SendingStop  = SendingStart;
                    SendingStart = Connection->SendBuffer;
                } else {
                    SendingStart += SendingSize;
                }
                Connection->Sending      = true;
                Connection->SendingStart = SendingStart;
                Connection->SendingStop  = SendingStop;
                Connection->SendingSize  = 0;
                Connection->SendFlush    = false;
            
            } else { // if now <there's no more data pending>
                // reset SendingStart so new sends will queue data from beginning
                Connection->SendingStart = Connection->SendBuffer;
                Connection->Sending      = false;
                Connection->SendingStop  =
                          Connection->SendingStart + Connection->SendBufSize;

                if (Connection->SendCallback != NULL) {
                    // invoke call back if one is specified
                    (*Connection->Call)(Connection->SendCallback, Connection,
                                                                  TCPSendMsg->size);
                }
                // if a close was queued, post close message now that send is done
                if (Connection->CloseMode != CLOSE_NONE) {
                    NetClose(Connection, Connection->CloseMode);
                }
            } // if <there's more data to send or not>
        } // if <send returned an error or not>
        return;
    } // if <connection is UDP or TCP>

}



int NetReceive(NetConnection Connection,
                          void *buf, size_t min, size_t max)
{

    if (!Net_ConnectionValid(Connection)) {
      printToFile("NetReceive: Invalid connection");

        NetLastError = NETERR_CONNECTION_INVALID;
        return 0;
    }

    if (Connection->Protocol == NETPROTOCOL_UDP) {

        UDPEndpointReceiveMsg receiveMsg(Connection->antEndpoint,
                                         (byte*)Connection->ReceiveBuffer, max);

        if (Connection->ReceiveCallback == BLOCK_RECEIVE) {

            receiveMsg.Call(Net_IPStackRef, sizeof(UDPEndpointReceiveMsg));
            NetLastError = Connection->Error = int(NETERR_UDP_BASE + receiveMsg.error);
            if (receiveMsg.error == UDP_SUCCESS) {
                if (buf != NULL) {
                    memcpy(buf, Connection->ReceiveBuffer, receiveMsg.size);
                }
                return receiveMsg.size;
            } else {
                return 0;
            }

        } else {
	  receiveMsg.error = UDP_SUCCESS;
	  receiveMsg.continuation = (void*)Connection;
	  receiveMsg.Send(Net_IPStackRef, Net_myOID,
			  Extra_Entry[entryNet_ReceiveCont], sizeof(receiveMsg));
	  if(receiveMsg.error==UDP_SUCCESS) {
	    Connection->Receiving = true;
	  } else {
	    printToFile("Error receiving (clearing Receiving flag)");
	    Connection->Receiving = false;
	  }
	  return 1;
        }

    } else { // if <connection is now TCP>

        TCPEndpointReceiveMsg receiveMsg(Connection->antEndpoint,
                                         (byte*)Connection->ReceiveBuffer, min, max);

        if (Connection->ReceiveCallback == BLOCK_RECEIVE) {

            receiveMsg.Call(Net_IPStackRef, sizeof(TCPEndpointReceiveMsg));
            NetLastError = Connection->Error = int(NETERR_TCP_BASE + receiveMsg.error);
            if (receiveMsg.error == TCP_SUCCESS) {
                if (buf != NULL) {
                    memcpy(buf, Connection->ReceiveBuffer, receiveMsg.sizeMin);
                }
                return receiveMsg.sizeMin;
            } else {
                return 0;
            }

        } else {

            receiveMsg.continuation = (void*)Connection;
            receiveMsg.Send(Net_IPStackRef, Net_myOID,
                            Extra_Entry[entryNet_ReceiveCont], sizeof(receiveMsg));
            Connection->Receiving = true;
            return 1;

        }
    } // if <connection was UDP or TCP>
}



void NetDoReceiveCont(ANTENVMSG msg)
{

    TCPEndpointReceiveMsg* TCPReceiveMsg
        = (TCPEndpointReceiveMsg*)antEnvMsg::Receive(msg);

    UDPEndpointReceiveMsg* UDPReceiveMsg = 
      (UDPEndpointReceiveMsg*) TCPReceiveMsg;

    NetConnection Connection = (NetConnection)(TCPReceiveMsg->continuation);

    // This should never come up. Ever. But I'm paranoid and *weird* stuff
    // is happening.
    if(UDPReceiveMsg->continuation!=
       TCPReceiveMsg->continuation) {
      printToFile("Shouldn't happen. Different continuations.");
    }

    if (Connection->Protocol == NETPROTOCOL_TCP) {

        NetLastError = Connection->Error = int(NETERR_TCP_BASE + TCPReceiveMsg->error);
        Connection->Receiving = false;

        // Unless the close is blocking, intercept the "connection closed" errors 
        if ((Connection->CloseCallback != ::BLOCK_CLOSE) && (
            (TCPReceiveMsg->error == TCP_CONNECTION_CLOSED) ||
            (TCPReceiveMsg->error == TCP_CONNECTION_RESET ) ||
            (TCPReceiveMsg->error == TCP_CONNECTION_TIMEOUT))) {
            // This will close the connection and call the callback, if specified
            NetClose(Connection, CLOSE_PASSIVE_CLEAN);
            return;
        }

        if (TCPReceiveMsg->error != TCP_SUCCESS) {
            if (Connection->ReceiveCallback != NULL) {
                (*Connection->Call)(Connection->ReceiveCallback, Connection, 0);
            }
        } else { // if <there was no error>
            if (size_t(TCPReceiveMsg->sizeMin) < Connection->RecvBufSize) {
                Connection->ReceiveBuffer[TCPReceiveMsg->sizeMin] = '\0';
            }
            if (Connection->ReceiveCallback != NULL) {
                (*Connection->Call)(Connection->ReceiveCallback,
                                                Connection, TCPReceiveMsg->sizeMin);
            }
        } // if <receive returned error or not>

        // Post new receive request to keep receiving
        if (Connection->ReceiveCallback != ::BLOCK_RECEIVE) {
            NetReceive(Connection, Connection->MinReceive, Connection->MaxReceive);
        }

    } else if(Connection->Protocol == NETPROTOCOL_UDP) { // if <connection is now UDP>
      
      NetLastError = Connection->Error = int(NETERR_UDP_BASE + UDPReceiveMsg->error);
      Connection->Receiving = false;
      Connection->RemoteAddress = UDPReceiveMsg->address;
      Connection->RemotePort    = UDPReceiveMsg->port;


        if (UDPReceiveMsg->error != UDP_SUCCESS) {
	  char temp_buf[128];
	  sprintf(temp_buf, "receive cont: Continuation reports an error %d. Port=%d",
		  UDPReceiveMsg->error, UDPReceiveMsg->port);
	  printToFile(temp_buf);

            if (Connection->ReceiveCallback != NULL) {
                (*Connection->Call)(Connection->ReceiveCallback, Connection, 0);
            }
        } else { // if <there was no error>
            if (size_t(UDPReceiveMsg->size) < Connection->RecvBufSize) {
                Connection->ReceiveBuffer[UDPReceiveMsg->size] = '\0';
            }
            if (Connection->ReceiveCallback != NULL) {
                (*Connection->Call)(Connection->ReceiveCallback,
                                               Connection, UDPReceiveMsg->size);
            }
        } // if <receive returned error or not>

        // Post new receive request to keep receiving
        if (Connection->ReceiveCallback != ::BLOCK_RECEIVE) {
            NetReceive(Connection, Connection->MaxReceive);
        }

    } else { // if <connection was TCP or UDP>
      printToFile("Unknown protocol in receive continuation");
    }
}


int NetClose(NetConnection Connection, NetCloseMode mode) 
{

  printToFile("NetClose: we are here");

    // This check will also prevent NetClose from sending another close
    // message if it's called from within the callback of a failing connect.
    // Now the close message is sent automatically after the connect callback
    // returns.

    if (!Net_ConnectionValid(Connection)) {
        NetLastError = NETERR_CONNECTION_INVALID;
        return 0;
    }

    // Set the connection's close mode;
    Connection->CloseMode = mode;

    if (Connection->Protocol == NETPROTOCOL_UDP) {

        // If we're sending, just return, the connection will be closed in
        // Net_CloseCont
        if (Connection->Sending) {
            return 1;
        }

        // Prepare the close message 
        UDPEndpointCloseMsg UDPCloseMsg(Connection->antEndpoint);

        // Tell ANT to destroy send/receive shared buffer
        Connection->antSharedSend.UnMap();
        antEnvDestroySharedBufferMsg destroySendBufMsg(Connection->antSharedSend);
        destroySendBufMsg.Call(Net_IPStackRef, sizeof(antEnvDestroySharedBufferMsg));

        // Send close message
        UDPCloseMsg.continuation = (void*)Connection;
        UDPCloseMsg.Send(Net_IPStackRef, Net_myOID,
                            Extra_Entry[entryNet_CloseCont], sizeof(UDPCloseMsg));
        return 1;
    } // if <connection was UDP>

    // Else, this is a TCP connection.

    if ((Connection->Sending) && (Connection->CloseCallback != BLOCK_CLOSE)) {
        // If a send is in progress and the close is not blocking, just return,
        // Net_CloseCont will call us when it's ready. Calling a blocking close
        // while a non-blocking send is progress will probably cause a
        // TCP_CONNECTION_BUSY error. As it should.
        return 1;
    }

    // Let's tag this connection as no longer connected so that other parts of
    // the program will not try to send stuff before the callback gets called.
    Connection->Connected = false;

    // antTypes defines a 'boolean' type with uppercase TRUE, so I will use
    // it here, but the rest of open-r uses type 'bool' with lowercase values. 
    boolean forceAbort = false;
    if ((mode == CLOSE_ABORT) || (mode == CLOSE_PASSIVE_ABORT)) {
        forceAbort = TRUE;
    }

    // Prepare the close message 
    TCPEndpointCloseMsg closeMsg(Connection->antEndpoint, forceAbort);

    if (Connection->CloseCallback == BLOCK_CLOSE) {

        // Tell ANT to destroy Send shared buffer
        Connection->antSharedSend.UnMap();
        antEnvDestroySharedBufferMsg destroySendBufMsg(Connection->antSharedSend);
        destroySendBufMsg.Call(Net_IPStackRef, sizeof(antEnvDestroySharedBufferMsg));

        // Tell ANT to destroy Receive shared buffer
        Connection->antSharedRecv.UnMap();
        antEnvDestroySharedBufferMsg destroyRecvBufMsg(Connection->antSharedRecv);
        destroyRecvBufMsg.Call(Net_IPStackRef, sizeof(antEnvDestroySharedBufferMsg));

        // Call the close message
        closeMsg.Call(Net_IPStackRef, sizeof(TCPEndpointCloseMsg));
        NetLastError = Connection->Error = int(NETERR_TCP_BASE + closeMsg.error);
        if ((closeMsg.error != TCP_SUCCESS) && (mode == CLOSE_CLEAN_ABORT)) {
            TCPEndpointCloseMsg abortMsg(Connection->antEndpoint, TRUE);
            abortMsg.Call(Net_IPStackRef, sizeof(TCPEndpointCloseMsg));
            NetLastError = abortMsg.error;
        }
        if ((mode != CLOSE_CLEAN_ONLY) || (Connection->Error == NETERR_TCP_SUCCESS)) {
            Net_RemoveConnection(Connection);
            delete Connection;
            return 1;
        } else {
            return 0;
        }

    } else { // if now <close is non-blocking>

        closeMsg.continuation = (void*)Connection;
        closeMsg.Send(Net_IPStackRef, Net_myOID,
                        Extra_Entry[entryNet_CloseCont], sizeof(closeMsg));
        return 1;
    } // if <close was blocking or not>
}



void NetDoCloseCont(ANTENVMSG msg)
{
    TCPEndpointCloseMsg* closeMsg
        = (TCPEndpointCloseMsg*)antEnvMsg::Receive(msg);
    NetConnection Connection = (NetConnection)(closeMsg->continuation);

    // For UDP connections, only really need to delete the connection
    if (Connection->Protocol == NETPROTOCOL_UDP) {
        delete Connection;
        return;
    }

    // Else, this is a TCP connection.

    // Set the error only if we care about it
    if (Connection->CloseMode == CLOSE_CLEAN_ONLY) {
        NetLastError = Connection->Error = int(NETERR_TCP_BASE + closeMsg->error);
    }

    if (closeMsg->error != TCP_SUCCESS) {

        // If the close resulted in an error, check if we want to automatically
        // sumbit a new close as an abort. Aborts will proceed here in any case.

        if (Connection->CloseMode == CLOSE_CLEAN_ABORT) {
            NetClose(Connection, CLOSE_ABORT);
            return;
        }

        if (Connection->CloseMode == CLOSE_PASSIVE_CLEAN) {
            NetClose(Connection, CLOSE_PASSIVE_ABORT);
            return;
        }

    }

    // If we have a callback and this was not an aborted connect, call it now.
    if ((Connection->CloseCallback != NULL) && (Connection->CloseMode != CLOSE_NONE)) {

        int Result = 0;

        // If this was _already_ an abort attempt, do as if everything were fine
        // and after the callback we'll just remove the connection and forget
        // about it. The only case in which Result will remain 0 is if the close
        // was explicitely called with a CLOSE_CLEAN_ONLY

        if ((Connection->CloseMode == CLOSE_PASSIVE_CLEAN) ||
            (Connection->CloseMode == CLOSE_PASSIVE_ABORT)) {
            Result = -1;    // -1 means a passive close
        } else {
            if (Connection->CloseMode == CLOSE_ABORT) {
                Result = 1; // +1 means we closed the connection
            }
        } 

        (*Connection->Call)(Connection->CloseCallback, Connection, Result);
    }

    // Tell ANT to destroy Send shared buffer
    Connection->antSharedSend.UnMap();
    antEnvDestroySharedBufferMsg destroySendBufMsg(Connection->antSharedSend);
    destroySendBufMsg.Call(Net_IPStackRef, sizeof(antEnvDestroySharedBufferMsg));

    // Tell ANT to destroy Receive shared buffer
    Connection->antSharedRecv.UnMap();
    antEnvDestroySharedBufferMsg destroyRecvBufMsg(Connection->antSharedRecv);
    destroyRecvBufMsg.Call(Net_IPStackRef, sizeof(antEnvDestroySharedBufferMsg));

    // Now remove the connection (if we ever managed to connect) and delete it.
    if (Connection->CloseMode != CLOSE_NONE) {
        Net_RemoveConnection(Connection);
    }
    delete Connection;

}




void Net_InitializeIfNecessary()
{
  static int Net_Initialized_State = 0;
  if (Net_Initialized_State == 0) {
    printToFile("Initializing network");
    
    Net_Initialized_State = 1;
    Net_IPStackRef = antStackRef("IPStack");
    WhoAmI(&Net_myOID);
    Net_Connections = NULL;
    
  }
}






void Net_AddConnection(NetConnection Connection)
{
    NetConnectionItem NewItem = new struct NetConnectionItemStruct;

    NewItem->Connection = Connection;
    NewItem->next       = Net_Connections;
    Net_Connections     = NewItem;
}


void Net_RemoveConnection(NetConnection Connection)
{
    NetConnectionItem This = Net_Connections;
    NetConnectionItem Last;

    if ((Net_Connections != NULL) &&
        (Net_Connections->Connection == Connection)) {
         Net_Connections = Net_Connections->next;
         delete This;
         return;
    }

    Last = Net_Connections;
    This = Net_Connections->next;

    while (This != NULL) {
        if (This->Connection == Connection) {
            Last->next = This->next;
            delete This;
            return;
        }
        Last = This;
        This = This->next;
    }
}


bool Net_ConnectionValid(NetConnection Connection)
{
    NetConnectionItem This = Net_Connections;

    if (Connection == NULL) return false;

    while (This != NULL) {
       if (This->Connection == Connection) return true;
       This = This->next;
    }

    return false;
}












