#ifndef _MGEN
#define _MGEN

#include "protokit.h"
#include "mgenFlow.h"
#include "mgenMsg.h"


class DrecGroupList 
{
    public:
        DrecGroupList();
        ~DrecGroupList();
        
        // (TBD) make socketList argument of constructor or Init() method ???
        
        void Destroy(MgenSocketList& socketList);
        
        bool JoinGroup(MgenSocketList&        socketList,
                       const ProtoAddress&  groupAddress, 
                       const char*            interfaceName = NULL,
                       unsigned short         thePort = 0,
                       bool                   deferred = false);
        
        bool LeaveGroup(MgenSocketList&       socketList,
                        const ProtoAddress& groupAddress, 
                        const char*           interfaceName = NULL,
                        unsigned short        thePort = 0);
        
        bool JoinDeferredGroups(MgenSocketList& socketList);

    private:
       class Item
        {
            friend class DrecGroupList;
            public:
                Item(const ProtoAddress&  groupAddr, 
                     const char*            interfaceName,
                     unsigned short         thePort);
                ~Item();
                void SetInterface(const char* interfaceName)
                {
                    if (interfaceName)
                        strncpy(interface_name, interfaceName, 16);
                    else
                        interface_name[0] = '\0';
                }
                const char* GetInterface()
                    {return (('\0' != interface_name[0]) ? interface_name : NULL);}
                
                bool IsActive() {return (NULL != socket_item);}
                bool Activate(MgenSocketList& socketList)
                {
                    const char* iface = GetInterface();
                    socket_item = socketList.JoinGroup(group_addr, iface, port);
                    return IsActive();
                }
                unsigned short GetPort() {return port;}
                
            private:
                MgenSocketList::Item*   socket_item;
                ProtoAddress            group_addr;
                char                    interface_name[16];
                unsigned short          port;
                Item*                   prev;
                Item*                   next;
        };  // end class DrecGroupList::Item
        
        Item* FindItemByGroup(const ProtoAddress& groupAddr,
                              const char*           interfaceName = NULL,
                              unsigned short        thePort = 0);
                   
        
        void Append(Item* item);
        void Remove(Item* item);
        
        Item* head;  
        Item* tail;
};  // end class DrecGroupList


class MgenController
{
    public:
        virtual ~MgenController();
    
        virtual void OnMsgReceive(const MgenMsg&        msg, 
                                  const ProtoAddress&   src,
                                  const struct timeval& currentTime) = 0;
    
        protected:
        MgenController(); 
               
};  // end class MgenController

class Mgen 
{
    public:
        enum {SCRIPT_LINE_MAX = 512};  // maximum script line length
        
        enum LogEvent
        {
            INVALID_EVENT = 0,
            RECV_EVENT,
            RERR_EVENT,
            SEND_EVENT,
            LISTEN_EVENT,
            IGNORE_EVENT,
            JOIN_EVENT,
            LEAVE_EVENT,
            START_EVENT,
            STOP_EVENT
        };
            
        Mgen(ProtoTimerMgr&         timerMgr, 
             ProtoSocket::Notifier& socketNotifier);
        ~Mgen();
        
        // MGEN "global command" set
        enum Command
        {
            INVALID_COMMAND,
            EVENT,
            START,     // specify absolute start time
            INPUT,     // input and parse an MGEN script
            OUTPUT,    // open output (log) file 
            LOG,       // open log file for appending
            NOLOG,     // no output
            TXLOG,     // turn transmit logging on
            DLOG,      // set debug log file
            SAVE,      // save pending flow state/offset info on exit.
            DEBUG,     // specify debug level
            OFFSET,    // time offset into script
	        TXBUFFER,  // Tx socket buffer size
            RXBUFFER,  // Rx socket buffer size
            LABEL,     // IPv6 flow label
            TOS,       // IPV4 Type-Of-Service 
            TTL,       // IPV4 Time-To-Live
            INTERFACE, //Multicast Interface
            BINARY,    // turn binary logfile mode on
            FLUSH,     // flush log after _each_ event
            CHECKSUM,  // turn on _both_ tx and rx checksum options
            TXCHECKSUM,// include checksums in transmitted MGEN messages
            RXCHECKSUM // force checksum validation at receiver _always_
        };
        static Command GetCommandFromString(const char* string);
        enum CmdType {CMD_INVALID, CMD_ARG, CMD_NOARG};
        static CmdType GetCmdType(const char* cmd);
        
        bool OnCommand(Mgen::Command cmd, const char* arg, bool override = false);
        
        
        void SetController(MgenController* theController)
            {controller = theController;}
        MgenSink* GetSink() {return sink;}
        void SetSink(MgenSink* theSink) 
        {
            sink = theSink;
            flow_list.SetSink(theSink);
        }
        void HandleMgenMessage(char*                 buffer, 
                               unsigned int          len, 
                               const ProtoAddress& srcAddr);

        bool OpenLog(const char* path, bool append, bool binary);
        void CloseLog();
        void SetLogFile(FILE* filePtr);

        typedef int (*LogFunction)(FILE*, const char*, ...);
#ifndef _WIN32_WCE
        static LogFunction Log;
#else
        static LogFunction Log;
        // Alternative logging function for WinCE debug window when logging to stdout/stderr
        static int Mgen::LogToDebug(FILE* filePtr, const char* format, ...);
#endif  // if/else _WIN32_WCE
        bool ParseScript(const char* path);
        bool ParseEvent(const char* lineBuffer, unsigned int lineCount);
        
        double GetCurrentOffset() const;
        
        void InsertDrecEvent(DrecEvent* event);
        void SetDefaultSocketType(ProtoAddress::Type addrType) 
            {addr_type = addrType;}
        ProtoAddress::Type GetDefaultSocketType() {return addr_type;}
        
        void SetPositionCallback(MgenPositionFunc* callback,
                                 const void*       clientData) 
        {
            get_position = callback;
            get_position_data = clientData;
        }
        
        void SetHostAddress(const ProtoAddress hostAddr)
        {
            host_addr = hostAddr;
            flow_list.SetHostAddress(hostAddr);
        }
        void ClearHostAddress()
        {
            host_addr.Invalidate();
            flow_list.ClearHostAddress();   
        }
        
#ifdef HAVE_GPS
        void SetPayloadHandle(GPSHandle payloadHandle) 
        {
            payload_handle = payloadHandle;
        }
#endif // HAVE_GPS
        
        bool Start(); 
        void Stop();
        
        bool DelayedStart() {return start_timer.IsActive();}
        void GetStartTime(char* buffer)
        {
            sprintf(buffer, "%02d:%02d:%02.0f%s", 
                            start_hour, start_min, start_sec,
                            (start_gmt ? "GMT" : ""));
        }        
        bool ConvertBinaryLog(const char* path);  
        
        // A little utility class for reading script files line by line
        class FastReader
        {
            public:
                enum Result {OK, ERROR_, DONE};  // trailing '_' for WIN32
                FastReader();
                FastReader::Result Read(FILE* filePtr, char* buffer, unsigned int* len);
                FastReader::Result Readline(FILE* filePtr, char* buffer, 
                                            unsigned int* len);
                FastReader::Result ReadlineContinue(FILE* filePtr, char* buffer, 
                                                    unsigned int* len,
                                                    unsigned int* lineCount = NULL);

            private:
                enum {BUFSIZE = 1024};
                char         savebuf[BUFSIZE];
                char*        saveptr;
                unsigned int savecount;
        };  // end class FastReader        
        
    private:
        // MGEN script command types ("global" commands)
        
        // for mapping protocol types from script line fields
        static const StringMapper COMMAND_LIST[]; 
        
        bool OnStartTimeout(ProtoTimer& theTimer);
        bool OnDrecEventTimeout(ProtoTimer& theTimer);
        void ProcessDrecEvent(const DrecEvent& event);
        void LogDrecEvent(LogEvent          eventType, 
                          const DrecEvent&  event, 
                          unsigned short    portNumber);        

        void OnSocketEvent(ProtoSocket& theSocket, ProtoSocket::Event theEvent); 
        
        // Common state
        MgenController*    controller;  // optional MgenController
        ProtoTimerMgr&     timer_mgr;
        MgenSocketList     socket_list;
        MgenSink*          sink;
        FILE*              log_file;
        bool               log_binary;
        bool               log_flush;
        bool               log_file_lock;
        bool               log_tx;
        bool               log_open;
        bool               log_empty;

        char*              save_path;
        bool               save_path_lock;
        bool               started;
        
        ProtoTimer         start_timer;
        unsigned int       start_hour;            // absolute start time
        unsigned int       start_min;
        double             start_sec;
        bool               start_gmt;
        bool               start_time_lock;
        
        double             offset;
        bool               offset_lock;
        bool               offset_pending; 
        
        // (TBD) move these two to MgenSocketList ???
        UINT32             default_flow_label;
        bool		       default_label_lock; 
        
        ProtoAddress       host_addr;
        bool               checksum_enable;       
        
        MgenFlowList       flow_list;
        
        // Drec state
        ProtoTimer         drec_event_timer;
        MgenEventList      drec_event_list;
        DrecEvent*         next_drec_event;      // for iterating drec_event_list
        DrecGroupList      drec_group_list;
        ProtoAddress::Type addr_type;
        bool               checksum_force;       // force checksum validation at rcvr

        MgenPositionFunc*  get_position;
        const void*        get_position_data;
#ifdef HAVE_GPS
        GPSHandle          payload_handle;
#endif // HAVE_GPS        
             
}; // end class Mgen 

#endif  // _MGEN


syntax highlighted by Code2HTML, v. 0.9.1