/*********************************************************************
 *
 * 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.
 ********************************************************************/

#ifndef _NS_PROTO_AGENT
#define _NS_PROTO_AGENT

#include "agent.h"
#include "timer-handler.h"

#include "protoSim.h"

// The "NsProtoAgent" class allows for binding between
// protocol implementations based on the NRL ProtoLib
// classes and the Berkeley ns-2 simulation environment

// Note that the "Agent" is _not_ an "Agent", but that
// the "Agent" will dynamically create ns-2 "Agent" 
// instances as sockets are opened and closed by the
// ProtoLib code.  The "Agent" _must_ be attached to
// an ns-2 "node" before it can be used.  An "attach-node"
// command is supported in the "NsProtoAgent" base class
// to perform this function.  

//It is expected that ProtoLib protocol implementations will 
// derive a simulation class from the "NsProtoAgent" base 
// class for ns-2 implementation As with other ns-2 derivations, 
// the "command()" method can be overridden as needed to control 
// the newly derived protocol simulation's behavior.

//class NsUdpSocketAgentClass;  // for TclObject creation/binding
//class NsUdpSocketAgentList;
class NsUdpSocketAgent : public Agent
{
    friend class NsUdpSocketAgentList;
    public:
        NsUdpSocketAgent();
        ~NsUdpSocketAgent();

        void recv(Packet* p, Handler* h);

        void AttachSocket(UdpSocket* theSocket) {udp_socket = theSocket;}
        UdpSocket* GetSocket() {return udp_socket;}

        bool SendTo(SIMADDR         theAddr, 
                    unsigned short  thePort,
                    const char*     buffer, 
                    unsigned int    len);

        bool RecvFrom(char*             buffer, 
                      unsigned int*     buflen,
                      SIMADDR*          srcAddr, 
                      unsigned short*   srcPort);

        bool JoinGroup(SIMADDR theGroup);
        void LeaveGroup(SIMADDR theGroup);
        void SetTTL(unsigned char theTTL) {mcast_ttl = theTTL;}
        void SetLoopback(bool state) {mcast_loopback = state;}  

    private:
        UdpSocket*      udp_socket;
        unsigned char   mcast_ttl;
        bool            mcast_loopback;
        char*           recv_data;
        unsigned int    recv_len;
        nsaddr_t        recv_addr;
        unsigned short  recv_port;

        NsUdpSocketAgent*   prev;
        NsUdpSocketAgent*   next;                  
};  // end class NsUdpSocketAgent 
  
class NsUdpSocketAgentList
{
    public:
        NsUdpSocketAgentList();
        ~NsUdpSocketAgentList();
        void Destroy();
        void Append(NsUdpSocketAgent* udpAgent);
        void Remove(NsUdpSocketAgent* udpAgent);
        NsUdpSocketAgent* Find(const UdpSocket* theSocket);

    private:
        NsUdpSocketAgent* head;
        NsUdpSocketAgent* tail;
};  // end class NsUdpSocketAgentList

class NsProtoAgent : public ProtoSim, public NsUdpSocketAgent
{
    public:
        NsProtoAgent();
        ~NsProtoAgent();       
        
        // (These "happen to be" the required overrides for 
        //  the ProtoSim base class)
        // UdpSocket management methods (TBD - support other socket types)
        bool OpenUdpSocket(UdpSocket* theSocket, unsigned short* thePort);
        void CloseUdpSocket(UdpSocket* theSocket);    
        bool SendTo(UdpSocket* theSocket, 
                            SIMADDR theAddr, 
                            unsigned short thePort,
                            const char* buffer, unsigned int len);       
        bool RecvFrom(UdpSocket* theSocket,
                              char* buffer, unsigned int* buflen,
                              SIMADDR* srcAddr, unsigned short* srcPort);       
        bool JoinGroup(UdpSocket* theSocket, SIMADDR theGroup);
        void LeaveGroup(UdpSocket* theSocket, SIMADDR theGroup);
        void SetSocketTTL(UdpSocket* theSocket, unsigned char theTTL);
        void SetSocketLoopback(UdpSocket* theSocket, bool state);
        
        // ProtocolTimer management routines
        virtual bool InstallTimer(ProtocolTimerMgr* timerMgr, double delay);
        virtual bool ModifyTimer(ProtocolTimerMgr* timerMgr, double delay)
            {return InstallTimer(timerMgr, delay);}
        virtual bool RemoveTimer(ProtocolTimerMgr* timerMgr);
        
    protected:
       virtual int command(int argc, const char*const* argv);
        
    private:  
        class TimerList;
        class Timer : public TimerHandler
        {
            friend class TimerList;
            public:
                Timer(ProtocolTimerMgr* timerMgr);
                ~Timer();
                // ns TimerHandler base class override  
                void expire(Event*)
                {
                    force_cancel();
                    timer_mgr->DoTimers();
                } 
                
            private:
                ProtocolTimerMgr*   timer_mgr; 
                Timer*              prev;
                Timer*              next;
        };
        
        class TimerList
        {
            public:
                TimerList();
                ~TimerList();
                void Destroy();
                Timer* GetTimer(ProtocolTimerMgr* timerMgr);
                
            private:
                Timer*      head;
                Timer*      tail;
        };
        
        NsUdpSocketAgentList udp_socket_agent_list;
        TimerList            timer_list;
        
};  // end class NsProtoAgent

// This is an simple example ns-2 agent which uses ProtoLib
// elements.  The same basic code (using ProtoLib elements)
// could also be used as part of a real world application.
// In this example, the ns-2 agent has two sockets open
// which send packets to each other.  
// This simple agent understands three commands: 
// "send <node> <port>", "recv [<port>]", and "stop"
// ("send" causes the agent to transmit one packet per second.)

class NsProtoExample : public NsProtoAgent 
{
    public:
        NsProtoExample();
        ~NsProtoExample();          
    
    protected:
        virtual int command(int argc, const char*const* argv);
    
    private:
        bool OnTxTimeout();
        bool OnReceive();
    
        ProtocolTimerMgr    timer_mgr;
        ProtocolTimer       tx_timer;
        UdpSocket           udp_socket;
        NetworkAddress      dst_addr;
    
};  // end class NsProtoExample


#endif // _NS_PROTO_AGENT
