/* 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.
  ========================================================================= */

#ifndef EasyNet_defs_macro_h_DEFINED
#define EasyNet_defs_macro_h_DEFINED


/* Implementation */

#include <string.h>
#include "/usr/local/OPEN_R_SDK/OPEN_R/include/TCPEndpointMsg.h"
#include "/usr/local/OPEN_R_SDK/OPEN_R/include/UDPEndpointMsg.h"


// 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);


// netObjectCallback typedef

typedef void (NET_OBJECT::*netObjCallback)(NetConnection, int);

// 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;


const netObjCallback NET_OBJECT::BLOCK_CONNECT = reinterpret_cast<netObjCallback>(&Net_Class::Block_Dummy_Func);\
const netObjCallback NET_OBJECT::BLOCK_LISTEN  = reinterpret_cast<netObjCallback>(&Net_Class::Block_Dummy_Func);\
const netObjCallback NET_OBJECT::BLOCK_SEND    = reinterpret_cast<netObjCallback>(&Net_Class::Block_Dummy_Func);\
const netObjCallback NET_OBJECT::BLOCK_RECEIVE = reinterpret_cast<netObjCallback>(&Net_Class::Block_Dummy_Func);\
const netObjCallback NET_OBJECT::BLOCK_CLOSE   = reinterpret_cast<netObjCallback>(&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);
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; 

    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; 

    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) {
        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) {
        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 NET_OBJECT::NetTCPConnect(IPAddress addr, Port port,
                 netObjCallback ConnectCallback, netObjCallback SendCallback,
                 netObjCallback ReceiveCallback, netObjCallback CloseCallback,
                 size_t SendBufSize, size_t RecvBufSize)
{

    NetConnection Connection = Net_NewTCPConnection(SendBufSize, RecvBufSize);

    Connection->ConnectCallback = reinterpret_cast<netMemberCallback>(ConnectCallback);
    Connection->SendCallback    = reinterpret_cast<netMemberCallback>(SendCallback);
    Connection->ReceiveCallback = reinterpret_cast<netMemberCallback>(ReceiveCallback);
    Connection->CloseCallback   = reinterpret_cast<netMemberCallback>(CloseCallback);

    if (NetLastError == NETERR_TCP_FAIL) {
        if ((ConnectCallback != NULL) && (ConnectCallback != BLOCK_CONNECT)) {
            (this->*(reinterpret_cast<netObjCallback>(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 Class version) 
    return NetDoTCPConnect(Connection, addr, port);

}



inline NetConnection NET_OBJECT::NetTCPConnect(IPAddress addr, Port port,
                 netObjCallback ConnectCallback, netObjCallback SendCallback,
                 netObjCallback ReceiveCallback, netObjCallback CloseCallback)
{
    return NetTCPConnect(addr, port, ConnectCallback, SendCallback,
              ReceiveCallback, CloseCallback, 8192, 8192);
}



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 NET_OBJECT::NetTCPListen(Port port,
                 netObjCallback ConnectCallback, netObjCallback SendCallback,
                 netObjCallback ReceiveCallback, netObjCallback CloseCallback,
                 size_t SendBufSize, size_t RecvBufSize)
{

    NetConnection Connection = Net_NewTCPConnection(SendBufSize, RecvBufSize);

    Connection->ConnectCallback = reinterpret_cast<netMemberCallback>(ConnectCallback);
    Connection->SendCallback    = reinterpret_cast<netMemberCallback>(SendCallback);
    Connection->ReceiveCallback = reinterpret_cast<netMemberCallback>(ReceiveCallback);
    Connection->CloseCallback   = reinterpret_cast<netMemberCallback>(CloseCallback);

    if (NetLastError == NETERR_TCP_FAIL) {
        if ((ConnectCallback != NULL) && (ConnectCallback != BLOCK_CONNECT)) {
            (this->*(reinterpret_cast<netObjCallback>(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 Class version) 
    return NetDoTCPListen(Connection, port);

}


inline NetConnection NET_OBJECT::NetTCPListen(Port port,
                 netObjCallback ConnectCallback, netObjCallback SendCallback,
                 netObjCallback ReceiveCallback, netObjCallback CloseCallback)
{
    return NetTCPListen(port, ConnectCallback, SendCallback,
              ReceiveCallback, CloseCallback, 8192, 8192);
}



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) {
        // 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>
        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)) {
            NetReceive(Connection, Connection->MaxReceive);
        }

        return Connection;
    } // if <blocking connect returned error or not>

}




NetConnection NET_OBJECT::NetUDPBind(IPAddress addr, Port port,
                 netObjCallback SendCallback, netObjCallback ReceiveCallback,
                 size_t SendBufSize, size_t RecvBufSize)
{

    NetConnection Connection = Net_NewUDPConnection(SendBufSize, RecvBufSize);

    Connection->SendCallback    = reinterpret_cast<netMemberCallback>(SendCallback);
    Connection->ReceiveCallback = reinterpret_cast<netMemberCallback>(ReceiveCallback);

    if (NetLastError == NETERR_UDP_FAIL) {
        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 Class version) 
    return NetDoUDPBind(Connection, addr, port);

}



inline NetConnection NET_OBJECT::NetUDPBind(IPAddress addr, Port port,
                 netObjCallback SendCallback, netObjCallback ReceiveCallback)
{
    return NetUDPBind(addr, port, SendCallback, ReceiveCallback, 8192, 8192);
}



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);

    Connection->This = ThisVoid;
    Connection->Call = Call;

    Connection->SendCallback    = SendCallback;
    Connection->ReceiveCallback = ReceiveCallback;

    if (NetLastError == NETERR_UDP_FAIL) {
        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 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) {
        // 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>
        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)) {
            NetReceive(Connection, Connection->MaxReceive);
        }

        return Connection;
    } // if <blocking connect returned error or not>

}




NetConnection NET_OBJECT::NetUDPListen(Port port,
                 netObjCallback SendCallback, netObjCallback ReceiveCallback,
                 size_t SendBufSize, size_t RecvBufSize)
{

    NetConnection Connection = Net_NewUDPConnection(SendBufSize, RecvBufSize);

    Connection->SendCallback    = reinterpret_cast<netMemberCallback>(SendCallback);
    Connection->ReceiveCallback = reinterpret_cast<netMemberCallback>(ReceiveCallback);

    if (NetLastError == NETERR_UDP_FAIL) {
        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 Class version) 
    return NetDoUDPListen(Connection, port);

}



inline NetConnection NET_OBJECT::NetUDPListen(Port port,
                 netObjCallback SendCallback, netObjCallback ReceiveCallback)
{
    return NetUDPListen(port, SendCallback, ReceiveCallback, 8192, 8192);
}



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) {
        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 NET_OBJECT::Net_ConnectCont(ANTENVMSG msg)
{

    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) {
                if (Connection->Call != NULL) {
                    (*Connection->Call)(Connection->ConnectCallback, Connection, 1);
                } else {
                    (this->*(reinterpret_cast<netObjCallback>(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) {
            if (Connection->Call != NULL) {
                (*Connection->Call)(Connection->ConnectCallback, Connection, 0);
            } else {
                (this->*(reinterpret_cast<netObjCallback>(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) {

        if (Connection->Sending == true) {
            NetLastError = Connection->Error = NETERR_UDP_CONNECTION_BUSY;
            return 0;
        }
        if (size > size_t(Connection->SendingStop - Connection->SendingStart)) {
            NetLastError = Connection->Error = NETERR_UDP_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
            UDPEndpointSendMsg sendMsg(Connection->antEndpoint, addr, port,
                          (byte*)Connection->SendingStart, Connection->SendingSize);
            sendMsg.Call(Net_IPStackRef, sizeof(TCPEndpointSendMsg));
            Connection->SendingStart = Connection->SendBuffer;
            Connection->SendingSize  = 0;
            NetLastError = Connection->Error = int(NETERR_UDP_BASE + sendMsg.error);
            if (sendMsg.error == UDP_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)) {

                // 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
                UDPEndpointSendMsg sendMsg(Connection->antEndpoint, addr, port,
                                           (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
                UDPEndpointSendMsg 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>

}



int NetSend(NetConnection Connection, const void *buf, size_t size)
{

    if (!Net_ConnectionValid(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 NET_OBJECT::Net_SendCont(ANTENVMSG msg)
{

    TCPEndpointSendMsg* TCPSendMsg = (TCPEndpointSendMsg*)antEnvMsg::Receive(msg);
    NetConnection Connection = (NetConnection)(TCPSendMsg->continuation);

    if (Connection->Protocol == NETPROTOCOL_TCP) {

        NetLastError = Connection->Error = int(NETERR_TCP_BASE + TCPSendMsg->error);
        Connection->Sending = false;

        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) {
                if (Connection->Call != NULL) {
                    (*Connection->Call)(Connection->SendCallback, Connection, 0);
                } else {
                    (this->*(reinterpret_cast<netObjCallback>(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
                    if (Connection->Call != NULL) {
                        (*Connection->Call)(Connection->SendCallback, Connection,
                                                                      TCPSendMsg->size);
                    } else {
                        (this->*(reinterpret_cast<netObjCallback>(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;

    } else { // if <connection is now UDP>

        UDPEndpointSendMsg* UDPSendMsg = (UDPEndpointSendMsg*) TCPSendMsg;
        NetLastError = Connection->Error = int(NETERR_UDP_BASE + UDPSendMsg->error);
        Connection->Sending = false;

        if (UDPSendMsg->error != UDP_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) {
                if (Connection->Call != NULL) {
                    (*Connection->Call)(Connection->SendCallback, Connection, 0);
                } else {
                    (this->*(reinterpret_cast<netObjCallback>(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;

                UDPEndpointSendMsg 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
                    if (Connection->Call != NULL) {
                        (*Connection->Call)(Connection->SendCallback, Connection,
                                                                      UDPSendMsg->size);
                    } else {
                        (this->*(reinterpret_cast<netObjCallback>(Connection->SendCallback)))
                                                               (Connection, UDPSendMsg->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>


    } // if <connection is TCP or UDP>
}



int NetReceive(NetConnection Connection,
                          void *buf, size_t min, size_t max)
{

    if (!Net_ConnectionValid(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.continuation = (void*)Connection;
            receiveMsg.Send(Net_IPStackRef, Net_myOID,
                            Extra_Entry[entryNet_ReceiveCont], sizeof(receiveMsg));
            Connection->Receiving = true;
            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>
}


inline int NetReceive(NetConnection Connection,
                                 size_t min, size_t max)
{
    return NetReceive(Connection, NULL, min, max);   
}


inline int NetReceive(NetConnection Connection, void* buf, size_t size)
{
    return NetReceive(Connection, buf, size, size);
}


inline int NetReceive(NetConnection Connection, size_t size)
{
    return NetReceive(Connection, NULL, size, size);
}


inline void NetNextReceive(NetConnection Connection, size_t min, size_t max)
{
    Connection->MinReceive = min;
    Connection->MaxReceive = max;
}


inline void NetNextReceive(NetConnection Connection, size_t size)
{
    Connection->MinReceive = Connection->MaxReceive = size;
}



void NET_OBJECT::Net_ReceiveCont(ANTENVMSG msg)
{

    TCPEndpointReceiveMsg* TCPReceiveMsg
        = (TCPEndpointReceiveMsg*)antEnvMsg::Receive(msg);
    NetConnection Connection = (NetConnection)(TCPReceiveMsg->continuation);

    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) {
                if (Connection->Call != NULL) {
                    (*Connection->Call)(Connection->ReceiveCallback, Connection, 0);
                } else {
                    (this->*(reinterpret_cast<netObjCallback>(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) {
                if (Connection->Call != NULL) {
                    (*Connection->Call)(Connection->ReceiveCallback,
                                                   Connection, TCPReceiveMsg->sizeMin);
                } else {
                    (this->*(reinterpret_cast<netObjCallback>(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 is now UDP>

        UDPEndpointReceiveMsg* UDPReceiveMsg = (UDPEndpointReceiveMsg*) TCPReceiveMsg;
        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) {
            if (Connection->ReceiveCallback != NULL) {
                if (Connection->Call != NULL) {
                    (*Connection->Call)(Connection->ReceiveCallback, Connection, 0);
                } else {
                    (this->*(reinterpret_cast<netObjCallback>(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) {
                if (Connection->Call != NULL) {
                    (*Connection->Call)(Connection->ReceiveCallback,
                                                   Connection, UDPReceiveMsg->size);
                } else {
                    (this->*(reinterpret_cast<netObjCallback>(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);
        }

    } // if <connection was TCP or UDP>

}


int NetClose(NetConnection Connection, NetCloseMode mode) 
{

    // 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>
}



inline int NetClose(NetConnection Connection)
{
    return NetClose(Connection, CLOSE_CLEAN_ABORT);
}



void NET_OBJECT::Net_CloseCont(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
            }
        } 

        if (Connection->Call != NULL) {
            (*Connection->Call)(Connection->CloseCallback, Connection, Result);
        } else {
            (this->*(reinterpret_cast<netObjCallback>(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) {

        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;

    while (This != NULL) {
       if (This->Connection == Connection) return true;
       This = This->next;
    }

    return false;
}



#endif // EasyNet_defs_macro_h_DEFINED






















