/*********************************************************************
 *
 * AUTHORIZATION TO USE AND DISTRIBUTE
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that: 
 *
 * (1) source code distributions retain this paragraph in its entirety, 
 *  
 * (2) distributions including binary code include this paragraph in
 *     its entirety in the documentation or other materials provided 
 *     with the distribution, and 
 *
 * (3) all advertising materials mentioning features or use of this 
 *     software display the following acknowledgment:
 * 
 *      "This product includes software written and developed 
 *       by Brian Adamson and Joe Macker of the Naval Research 
 *       Laboratory (NRL)." 
 *         
 *  The name of NRL, the name(s) of NRL  employee(s), or any entity
 *  of the United States Government may not be used to endorse or
 *  promote  products derived from this software, nor does the 
 *  inclusion of the NRL written and developed software  directly or
 *  indirectly suggest NRL or United States  Government endorsement
 *  of this product.
 * 
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 ********************************************************************/
 
#include "protoSim.h"

// These installers assumes the "installData" points to a 
// (ProtoSim) instance.  Make sure that UdpSocket and
// ProtocolTimerMgr class install_data is properly inited
// with the actual ProtoSim pointer! For example, use 
// a static_cast to get to the actual _ProtoSim_ pointer 
// in any derived classes. 

bool ProtoSim::TimerInstaller(ProtocolTimerInstallCmd cmd,
                                   double                  delay,
                                   ProtocolTimerMgr*       timerMgr, 
                                   const void*             installData)
{
    ProtoSim* agent = 
        static_cast<ProtoSim*>((ProtoSim*)installData);
    switch(cmd)
    {
        case PROTOCOL_TIMER_INSTALL:
            return agent->InstallTimer(timerMgr, delay);
            break;
            
        case PROTOCOL_TIMER_MODIFY:
            return agent->ModifyTimer(timerMgr, delay);
            break;
            
        case PROTOCOL_TIMER_REMOVE:
            return agent->RemoveTimer(timerMgr);
            break;
    }
}  // end ProtoSim::TimerInstaller()

////////////////////////////////////////////////////////////////
// Simulation proxy UdpSocket implementation
// These UdpSocket methods are used _instead_ of the
// normal UdpSocket implementation found in "udpSocket.cpp"
// This binds the UdpSocket input/output to the simulation
// environment instead of a real socket

UdpSocket::UdpSocket()
    : handle(INVALID_SOCKET), addr_type(SIM),
      port(0), connected(false), owner(NULL),
      recv_handler(NULL), install_func(NULL), install_data(NULL),
      user_data(NULL)
{
    
}

UdpSocket::~UdpSocket()
{
    
}

// Tells simulation agent we wish to open a UdpSocket
UdpSocketError UdpSocket::Open(unsigned short       thePort, 
                               NetworkAddressType   /*addressType*/,
                               bool                 /* bindOnOpen*/)
{
    // Let optional install_func manipulate the socket first
    // if needed
    if (install_func) install_func(UDP_SOCKET_INSTALL, this, install_data);  
    ProtoSim* agent = 
        static_cast<ProtoSim*>((ProtoSim*)install_data);
    if (agent->OpenUdpSocket(this, &thePort))
    {
        port = thePort;
        handle = 0;
        return UDP_SOCKET_ERROR_NONE;
    }
    else
    {
        port = 0;
        handle = INVALID_SOCKET;
        return UDP_SOCKET_ERROR_OPEN;   
    }
}  // end UdpSocket::Open()

UdpSocketError UdpSocket::Bind(unsigned short thePort)
{
    if (thePort == port)
        return UDP_SOCKET_ERROR_NONE;
    else
        return UDP_SOCKET_ERROR_BIND;
}  // end UdpSocket::Bind()

void UdpSocket::Close()
{
    if (install_func) install_func(UDP_SOCKET_REMOVE, this, install_data);
    ProtoSim* agent = static_cast<ProtoSim*>((ProtoSim*)install_data);
    agent->CloseUdpSocket(this);
    handle = INVALID_SOCKET;
    port = 0;
}  // end UdpSocket::Close()

UdpSocketError UdpSocket::SendTo(const NetworkAddress*  dest, 
                                 const char*            buffer, 
                                 unsigned int           len)
{
    // The socket's agent will send the packet
    ProtoSim* agent = static_cast<ProtoSim*>((ProtoSim*)install_data);
    agent->SendTo(this, dest->SimHostAddr(), dest->Port(), buffer, len);
    return UDP_SOCKET_ERROR_NONE;
}  // end UdpSocket::Sendto()

UdpSocketError UdpSocket::RecvFrom(char*            buffer, 
                                   unsigned int*    buflen, 
                                   NetworkAddress*  sourceAddr)
{
    SIMADDR srcAddr;
    unsigned short srcPort;
    ProtoSim* agent = static_cast<ProtoSim*>((ProtoSim*)install_data);   
    if (agent->RecvFrom(this, buffer, buflen, &srcAddr, &srcPort))
    {
        sourceAddr->SimSetAddress(srcAddr);
        sourceAddr->SetPort(srcPort);
        return UDP_SOCKET_ERROR_NONE; 
    }
    else
    {
        return UDP_SOCKET_ERROR_RECV;    
    }
}

UdpSocketError UdpSocket::JoinGroup(const NetworkAddress* theAddress,
                                    const char* /*interfaceName*/)
{
    ProtoSim* agent = static_cast<ProtoSim*>((ProtoSim*)install_data);  
    ASSERT(agent);
    agent->JoinGroup(this, theAddress->SimHostAddr()); 
    return UDP_SOCKET_ERROR_NONE;
}  // end UdpSocket::JoinGroup()

UdpSocketError UdpSocket::LeaveGroup(const NetworkAddress* theAddress,
                                     const char* /*interfaceName*/)
{
    ProtoSim* agent = static_cast<ProtoSim*>((ProtoSim*)install_data);  
    agent->LeaveGroup(this, theAddress->SimHostAddr()); 
    return UDP_SOCKET_ERROR_NONE;
}

UdpSocketError UdpSocket::SetTTL(unsigned char ttl)
{
    ProtoSim* agent = static_cast<ProtoSim*>((ProtoSim*)install_data);  
    ASSERT(agent);
    agent->SetSocketTTL(this, ttl);
    return UDP_SOCKET_ERROR_NONE;
}

UdpSocketError UdpSocket::SetLoopback(bool state)
{
    ProtoSim* agent = static_cast<ProtoSim*>((ProtoSim*)install_data);  
    agent->SetSocketLoopback(this, state);
    return UDP_SOCKET_ERROR_NONE;
}

UdpSocketError UdpSocket::SetMulticastInterface(const char* /*interfaceName*/)
{
    return UDP_SOCKET_ERROR_NONE;
}

UdpSocketError UdpSocket::SetTOS(unsigned char /*tos*/)
{
    return UDP_SOCKET_ERROR_NONE;
}

UdpSocketError UdpSocket::SetReuse(bool /*state*/)
{
    return UDP_SOCKET_ERROR_NONE;
}

bool UdpSocket::SetTxBufferSize(unsigned int /*bufferSize*/)
{
    return true;
}

bool UdpSocket::SetRxBufferSize(unsigned int /*bufferSize*/)
{
    return true;
}

