#ifndef _NS_PROTO_SIM_AGENT
#define _NS_PROTO_SIM_AGENT

// The "NsProtoSimAgent" plays a role similar to that of the 
// "ProtoDispatcher" class which provides liason between Protolib 
// classes and the computer operating system.

// ns-2 agents based on Protolib code can use this as a base
// class.  ProtoSockets used by the Protolib code _must_ have
// their "notifier" set as a pointer to their corresponding
// NsProtoSimAgent instance.  Similarly, ProtoTimerMgrs used by the 
// Protolib code _must_ have their "installer" set to their 
// corresponding "NsProtoSimAgent" instance.

#include "protoSimAgent.h"
#include "protoSocket.h"
#include "protoTimer.h"
#include "agent.h"  // for class Agent
#include "timer-handler.h"  // for class TimerHandler
#include "udp.h"  // for class UdpAgent

class NsProtoSimAgent : public Agent, public TimerHandler, public ProtoSimAgent
{
    public:
        virtual ~NsProtoSimAgent();
              
        virtual bool OnStartup(int argc, const char*const* argv) = 0;
        virtual bool ProcessCommands(int argc, const char*const* argv) = 0;
        virtual void OnShutdown() = 0;    
        
        // Some helper methods
        void ActivateTimer(ProtoTimer& theTimer)
            {ProtoSimAgent::ActivateTimer(theTimer);}
        
        void DeactivateTimer(ProtoTimer& theTimer)
            {ProtoSimAgent::DeactivateTimer(theTimer);}
        
        ProtoSocket::Notifier& GetSocketNotifier() 
            {return ProtoSimAgent::GetSocketNotifier();}
        
        ProtoTimerMgr& GetTimerMgr()
            {return ProtoSimAgent::GetTimerMgr();}
        
        bool GetLocalAddress(ProtoAddress& localAddr)
        {
            // if attached to node
            localAddr.SimSetAddress(Agent::addr());
            localAddr.SetPort(Agent::port());
            return true;   
            // (TB) if not attached to node, return false;
        }
        
    protected:
        NsProtoSimAgent();
    
        // override of ns-2 Agent::command() method
        virtual int command(int argc, const char*const* argv);
        
        // override of ns-2 TimerHandler::expire() method
        void expire(Event*)
        {
            force_cancel();
            ProtoSimAgent::OnSystemTimeout();
        } 
        
        // override of ProtoSimAgent::UpdateSystemTimer() method
        bool UpdateSystemTimer(ProtoTimer::Command command,
                               double              delay)
        {
            switch (command)
            {
                case ProtoTimer::INSTALL:
                case ProtoTimer::MODIFY:
                    resched(delay);
                    break;
                case ProtoTimer::REMOVE:
                    force_cancel();
                    break;   
            }
            return true;
        }
        
        // The "SocketAgent" provides liason between a "ProtoSocket"
        // instance and a corresponding ns-2 transport "ProtoSocketAgent" 
        // instance.
        class SocketAgent : public ProtoSimAgent::SocketProxy
        {
            friend class NsProtoSimAgent;
            
            public:
                virtual ~SocketAgent();
            
                bool Bind(UINT16& thePort);
                bool RecvFrom(char* buffer, unsigned int& numBytes, ProtoAddress& srcAddr);
                                
                bool JoinGroup(const ProtoAddress& groupAddr);
                bool LeaveGroup(const ProtoAddress& groupAddr);
                void SetTTL(unsigned char ttl) {mcast_ttl = ttl;}
                void SetLoopback(bool loopback) {mcast_loopback == loopback;}
                
                virtual bool SendTo(const char*         buffer, 
                                    unsigned int&       numBytes, 
                                    const ProtoAddress& dstAddr) = 0;
                
            protected:
                SocketAgent();
                
                unsigned char   mcast_ttl;
                bool            mcast_loopback;
                char*           recv_data;
                UINT16          recv_data_len;
                ProtoAddress    src_addr;
        };  // end class NsProtoSimAgent::SocketAgent
        
        ProtoSimAgent::SocketProxy* OpenSocket(ProtoSocket& theSocket);
        void CloseSocket(ProtoSocket& theSocket);
        
        // Extends ns-2 UdpAgent to provide interface to SocketAgent
        friend class ProtoSocketUdpAgentClass;
        class UdpSocketAgent : public Agent, public SocketAgent
        {
            friend class ProtoSocketUdpAgentClass;
            public:
                bool SendTo(const char*         buffer, 
                            unsigned int&       numBytes, 
                            const ProtoAddress& dstAddr);
            private:
                UdpSocketAgent();
                void recv(Packet* pkt, Handler* h);
        };  // end class NsProtoSimAgent::UdpSocketAgent
        
        // (TBD) Extend an appropriate ns-2 TCP agent
        
        bool                              startup_ok;
        ProtoSimAgent::SocketProxy::List  socket_proxy_list; 
};  // end class NsProtoSimAgent


// See common/protoExample.cpp for an example application/agent
// using Protolib code.

#endif  //_NS_PROTO_SIM_AGENT
