#ifndef _OPNET_PROTO_SIM_PROCESS
#define _OPNET_PROTO_SIM_PROCESS

// The "OpnetProtoSimProcess" 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
// OpnetProtoSimProcess instance.  Similarly, ProtoTimerMgrs used by the 
// Protolib code _must_ have their "installer" set to their 
// corresponding "OpnetProtoSimProcess" instance.

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

#include "opnet.h"

class OpnetProtoSimProcess : public ProtoSimAgent
{
    public:
        virtual ~OpnetProtoSimProcess();
        
        // These must be overridden by derived OPNET process class
        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)
        {
            localAddr.SimSetAddress((unsigned long)op_id_self());
            return true;
        }
        
        // Some OPNET specific utility methods
        void SetUdpProcessId(Objid udpProcessId) 
            {udp_process_id = udpProcessId;}
        void OnReceive();
        
    protected:
        OpnetProtoSimProcess();
    
        // 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::MODIFY:
                    op_ev_cancel(timer_handle);
                case ProtoTimer::INSTALL:
                    timer_handle = 
                        op_intrpt_schedule_self(op_sim_time() + delay, 0);
                    break;
                case ProtoTimer::REMOVE:
                    op_ev_cancel(timer_handle);
                    timer_handle = NULL;  // ???
                    break;   
            }
            return true;
        }
        
        // The "UdpSocketProxy" provides liason between a UDP "ProtoSocket"
        // instance and the neighboring OPNET UDP process
        class UdpSocketProxy : public ProtoSimAgent::SocketProxy
        {
            friend class OpnetProtoSimProcess;
            
            public:
                UdpSocketProxy(Objid udpProcessId);
                virtual ~UdpSocketProxy();
            
                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:
                Objid           udp_process_id;
                unsigned char   mcast_ttl;
                bool            mcast_loopback;
                char*           recv_data;
                UINT16          recv_data_len;
                ProtoAddress    src_addr;
        };  // end class OpnetProtoSimProcess::SocketAgent
        
        ProtoSimAgent::SocketProxy* OpenSocket(ProtoSocket& theSocket);
        void CloseSocket(ProtoSocket& theSocket);
        
        Objid                             udp_process_id;
        ProtoSimAgent::SocketProxy::List  socket_proxy_list; 
        Evhandle                          timer_handle;
};  // end class OpnetProtoSimProcess

#endif  //_OPNET_PROTO_SIM_PROCESS
