#ifndef _PROTO_SIM_AGENT
#define _PROTO_SIM_AGENT

#include "protoTimer.h"
#include "protoSocket.h"

// The "ProtoSimAgent" class provides a base class for developing
// support for Protolib code in various network simulation environments
// (e.g. ns-2, OPNET, etc).  Protolib-based code must init its
// ProtoTimerMgr instances such that

class ProtoSimAgent : public ProtoSocket::Notifier
{
    public:
        virtual ~ProtoSimAgent();
              
        void ActivateTimer(ProtoTimer& theTimer)
            {timer_mgr.ActivateTimer(theTimer);}
        
        void DeactivateTimer(ProtoTimer& theTimer)
            {timer_mgr.DeactivateTimer(theTimer);}
                
        ProtoSocket::Notifier& GetSocketNotifier() 
        {
            return static_cast<ProtoSocket::Notifier&>(*this);
        }
        
        ProtoTimerMgr& GetTimerMgr() {return timer_mgr;}
        
        virtual bool GetLocalAddress(ProtoAddress& localAddr) = 0;

        // The "SocketProxy" provides liason between a "ProtoSocket"
        // instance and a corresponding simulation transport "Agent" 
        // instance.
        friend class ProtoSocket;
        class SocketProxy : public ProtoSocket::Proxy
        {
            friend class ProtoSimAgent;
            public:
                ~SocketProxy();
                        
                virtual bool Bind(UINT16& thePort) = 0;
                
                virtual bool SendTo(const char*         buffer, 
                                    unsigned int&       numBytes, 
                                    const ProtoAddress& dstAddr) = 0;
                virtual bool RecvFrom(char*         buffer, 
                                      unsigned int& numBytes, 
                                      ProtoAddress& srcAddr) = 0;                
                
                virtual bool JoinGroup(const ProtoAddress& groupAddr) = 0;
                virtual bool LeaveGroup(const ProtoAddress& groupAddr) = 0;
                virtual void SetTTL(unsigned char ttl) = 0;
                virtual void SetLoopback(bool loopback) = 0;
                ProtoSocket* GetSocket() {return proto_socket;}
                UINT16 GetPort()
                    {return (proto_socket ? proto_socket->GetPort() : 0);}
                      
            protected:
                SocketProxy();
                void AttachSocket(ProtoSocket& protoSocket)
                    {proto_socket = &protoSocket;}
                
                SocketProxy* GetPrev() {return prev;}
                void SetPrev(SocketProxy* broker) {prev = broker;}
                SocketProxy* GetNext() {return next;}
                void SetNext(SocketProxy* broker) {next = broker;}
                
                class List
                {
                    public:
                        List();
                        void Prepend(SocketProxy& broker);
                        void Remove(SocketProxy& broker);
                        SocketProxy* FindProxyByPort(UINT16 thePort);
                        
                    private:
                        SocketProxy*  head;
                };  // end class ProtoSimAgent::SocketProxy::List
                friend class List;
                     
                ProtoSocket*        proto_socket;
                
                SocketProxy*        prev;    
                SocketProxy*        next;    
        };  // end class ProtoSimAgent::SocketProxy
    
    protected:
        ProtoSimAgent();
    
        virtual bool UpdateSystemTimer(ProtoTimer::Command command,
                                       double              delay) = 0;
        void OnSystemTimeout() {timer_mgr.OnSystemTimeout();}
        
        virtual SocketProxy* OpenSocket(ProtoSocket& theSocket) = 0;
        virtual void CloseSocket(ProtoSocket& theSocket) = 0;
        
    private:
        class TimerMgr;
        friend class TimerMgr;
        class TimerMgr : public ProtoTimerMgr
        {
            public:
                TimerMgr(ProtoSimAgent& theAgent);
                bool UpdateSystemTimer(ProtoTimer::Command command,
                                       double              delay)
                {
                    return agent.UpdateSystemTimer(command, delay);   
                }
                
            private:
                ProtoSimAgent& agent;
        };  // end class TimerMgr
        
        
        TimerMgr    timer_mgr;
};  // end class ProtoSimAgent

#endif // _PROTO_SIM_AGENT
