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

#include "eventDispatcher.h"

#include <stdio.h>
#include <string.h>
#include <errno.h>

#ifdef UNIX
#include <unistd.h>
#include <sys/time.h>
#include <sys/select.h>
#endif // UNIX

#ifdef UNIX
const EventDispatcher::Descriptor EventDispatcher::INVALID_DESCRIPTOR = -1;
#endif // UNIX
#ifdef WIN32
const EventDispatcher::Descriptor EventDispatcher::INVALID_DESCRIPTOR = (HANDLE)INVALID_SOCKET;
#endif // WIN32

/* Should we ever need a "normal" new operator
void* EventDispatcher::Item::operator new(size_t objSize)
{
    return (void*)::new int[(objSize/sizeof(int)];
}*/

void* EventDispatcher::Item::operator new(size_t objSize, EventDispatcher::Item* item)
{
    if (item)
    {
        return ((void*)item);
    }
    else
    {
        size_t numInts = objSize / sizeof(int);
        if ((numInts * sizeof(int)) < objSize) numInts++;
        return ((void*)new int[numInts]);
    }
}  // end EventDispatcher::Item::operator new()
  
EventDispatcher::Item::Item(EventDispatcher& theDispatcher)
 : dispatcher(theDispatcher), flags(NONE), descriptor(INVALID_DESCRIPTOR), 
   data(NULL), callback(NULL)
{
}


EventDispatcher::Item::Item(EventDispatcher& theDispatcher,
                            EventDispatcher::Descriptor theDescriptor, 
                            EventDispatcher::Callback func,
                            const void* userData)
 : dispatcher(theDispatcher), type(GENERIC), flags(NONE), descriptor(theDescriptor), 
   data(userData), callback(func)
{
}

EventDispatcher::Item::Item(EventDispatcher& theDispatcher, UdpSocket* theSocket)
 : dispatcher(theDispatcher), type(SOCKET), flags(NONE), descriptor((Descriptor)theSocket->Handle()), 
   data(theSocket), callback(NULL)
{
}

EventDispatcher::Item::Item(EventDispatcher& theDispatcher, ProtocolTimerMgr* timerMgr)
 : dispatcher(theDispatcher), type(TIMER), flags(NONE), descriptor(INVALID_DESCRIPTOR), 
   data(timerMgr), callback(NULL) 
{
   timer.Init(0.0, 0, (ProtocolTimerOwner*)this, 
              (ProtocolTimeoutFunc)&Item::OnTimeout);
}

EventDispatcher::Item::~Item()
{
    // (TBD) If open, dispatch "DETACH" event ???
}

void EventDispatcher::Item::OnInputEvent()
{
    switch (type)
    {
        case GENERIC:
            if (callback) callback(descriptor, EVENT_INPUT, data);
            break;
        case SOCKET: 
            ((UdpSocket*)data)->OnReceive();
            break;
        case TIMER:
            // Shouldn't happen
            ASSERT(0);
            break;
    }   
}  // end EventDispatcher::Item::OnInputEvent()

void EventDispatcher::Item::OnOutputEvent()
{
    switch (type)
    {
        case GENERIC:
            if (callback) callback(descriptor, EVENT_OUTPUT, data);
            break;
        case SOCKET: 
            DMSG(0, "EventDispatcher::Item::OnOutputEvent() Async socket output not yet supported\n");
            //((UdpSocket*)data)->OnReceive();
            break;
        case TIMER:
            // Shouldn't happen
            ASSERT(0);
            break;
    }   
}  // end EventDispatcher::Item::OnOutputEvent()

void EventDispatcher::Item::SetTimeout(double theDelay)     
{
    timer.SetInterval(theDelay);
    if (timer.IsActive()) timer.Reset();
}  // end EventDispatcher::Item::SetTimeout()

bool EventDispatcher::Item::OnTimeout()
{
    ((ProtocolTimerMgr*)data)->DoTimers();
    timer.Deactivate();
    dispatcher.RemoveItem(this);
    return false;
}  // end EventDispatcher::Item::OnTimeout()

// Function pointer for timer "installer" (select() timeout)
bool EventDispatcher::PrivateTimerInstaller(ProtocolTimerInstallCmd   cmd, 
                                            double                    theDelay,
                                            ProtocolTimerMgr*       /*timerMgr*/,
                                            const void*               installData)
{
    switch (cmd)
    {
        case PROTOCOL_TIMER_INSTALL:
        case PROTOCOL_TIMER_MODIFY:
            ((EventDispatcher*)installData)->SetTimeout(theDelay);
            break;
        case PROTOCOL_TIMER_REMOVE:
            ((EventDispatcher*)installData)->SetTimeout(-1.0);
            break;
    }
    return true;
}  // end EventDispatcher::PrivateTimerInstaller()

bool EventDispatcher::SocketInstaller(UdpSocketCmd  cmd,
                                      UdpSocket*    theSocket,
                                      const void*   installData)
{
    switch (cmd)
    {
        case UDP_SOCKET_INSTALL:
            return ((EventDispatcher*)installData)->AddSocketInput(theSocket);

        case UDP_SOCKET_REMOVE:
        {
            Item* item = 
                ((EventDispatcher*)installData)->FindItemByDescriptor((Descriptor)theSocket->Handle());
            if (item)
                ((EventDispatcher*)installData)->RemoveItem(item);
            else
                return false;
            break;
        }
    }
    return true;
}  // end EventDispatcher::SocketInstaller()

// Public timer install function which can be used by applications
bool EventDispatcher::TimerInstaller(ProtocolTimerInstallCmd    cmd, 
                                     double                     theDelay,
                                     ProtocolTimerMgr*          timerMgr, 
                                     const void*                installData)
{
    EventDispatcher* dispatcher = (EventDispatcher*)installData;  
    switch(cmd)
    {
        case PROTOCOL_TIMER_INSTALL:
            return dispatcher->InstallTimerMgr(timerMgr, theDelay);

        case PROTOCOL_TIMER_MODIFY:
            return dispatcher->ModifyTimerMgr(timerMgr, theDelay);

        case PROTOCOL_TIMER_REMOVE:
            return dispatcher->RemoveTimerMgr(timerMgr);
    }  // end switch(cmd)
    ASSERT(0);
    return false;
}  // end EventDispatcher::TimerInstaller()           
            

bool EventDispatcher::InstallTimerMgr(ProtocolTimerMgr* timerMgr, double theDelay)
{
   Item* item = GetItemFromPool();
   item = new(item) Item(*this, timerMgr); 
   if (item)
   {
       // Prepend item to beginning of item list
        item->prev = NULL;
        if ((item->next = top_item)) top_item->prev = item;
        top_item = item;
        item->SetTimeout(theDelay);
        InstallTimer(item->Timer());
        return true;
   }
   else
   {
       return false;
   }
}  // end bool EventDispatcher::InstallTimerMgr()  

bool EventDispatcher::ModifyTimerMgr(ProtocolTimerMgr* timerMgr, double theDelay)
{
    Item* item = top_item;
    while (item)
    {
        if ((Item::TIMER == item->type) && ((void*)timerMgr == item->data))
            break;
        item = item->next;   
    }
    if (item)
    {
        item->SetTimeout(theDelay);
        return true;
    }
    else
    {
        return false;   
    }
}  // end EventDispatcher::ModifyTimerMgr()

bool EventDispatcher::RemoveTimerMgr(ProtocolTimerMgr* timerMgr)
{
    Item* item = top_item;
    while (item)
    {
        if ((Item::TIMER == item->type) && ((void*)timerMgr == item->data)) 
            break;
        item = item->next;   
    }
    if (item)
    {
        if (item->Timer()->IsActive()) item->DeactivateTimer();
        RemoveItem(item);
        return true;
    }
    else
    {
        return false;   
    }
}  // end EventDispatcher::RemoveTimerMgr()



EventDispatcher::EventDispatcher()
    : delay(-1.0), top_item(NULL), item_pool(NULL), 
      max_descriptor(INVALID_DESCRIPTOR), run(false), exit_code(0)
#ifdef WIN32
      ,object_count(0), object_handles(NULL), object_items(NULL),
      msg_window(NULL)
#endif // WIN32
{
    timer_mgr.SetInstaller(EventDispatcher::PrivateTimerInstaller, this);
}

EventDispatcher::~EventDispatcher()
{
    Destroy();
}

void EventDispatcher::Destroy()
{
    Item* next;
    while (top_item) 
    {
        RemoveItem(top_item);
    }
    
    while((next = item_pool))
    {
        item_pool = next->next;
        ::delete next;
    }
    delay = -1.0;
    max_descriptor = INVALID_DESCRIPTOR;
    // (TBD) WIN32 needs to destroy our window ???
    run = false;
}  // end EventDispatcher::Destroy()

bool EventDispatcher::AddGenericItem(EventDispatcher::Item::Flag flags,
                                     EventDispatcher::Descriptor descriptor, 
                                     Callback func, const void* userData)
{
    Item* item = FindItemByDescriptor(descriptor);
    if (!item) 
    {
        item = GetItemFromPool();
        item = new(item) Item(*this, descriptor, func, userData);  
        if (item)
        {
            // Prepend item to beginning of item list
            item->prev = NULL;
            if ((item->next = top_item)) top_item->prev = item;
            top_item = item;
        
        }
        else 
        {
            DMSG(0, "EventDispatcher::AddGenericInput() memory allocation error: %s\n", strerror(errno));
            return false;
        }
#ifdef WIN32        
        HANDLE* hptr = new HANDLE[object_count+1];
        Item** iptr = new Item*[object_count+1];
        if (!hptr || !iptr)
        {
            DMSG(0, "EventDispatcher::AddGenericInput() memory allocation error: %s\n", strerror(errno));
            if (iptr) 
                delete iptr;
            else if (hptr) 
                delete hptr;
            RemoveItem(item);
            return false;
        }
        if (object_handles)
        {
            ASSERT(object_count > 1);
            memcpy(hptr, object_handles, object_count*sizeof(HANDLE));
            memcpy(iptr, object_items, object_count*sizeof(HANDLE));
            delete object_handles;
            delete object_items;
        }
        object_handles = hptr;
        object_items = iptr;
        object_handles[object_count] = (HANDLE)item->descriptor;
        object_items[object_count] = item;
        object_count++;
#endif // WIN32
        if ((INVALID_DESCRIPTOR == max_descriptor) || (descriptor > max_descriptor)) 
            max_descriptor = descriptor;
    }
    item->SetFlag((Item::Flag)flags);
    return true;
}  // end EventDispatcher::AddGenericItem()


bool EventDispatcher::AddSocketInput(UdpSocket* theSocket)
{
    Item* item = FindItemByDescriptor((Descriptor)theSocket->Handle());
#ifdef WIN32
    unsigned int index;
#endif // WIN32
    if (!item) 
    {
        item = GetItemFromPool();
        item = new(item) Item(*this, theSocket);   
        if (item)
        {
            // Prepend item to beginning of item list
            item->prev = NULL;
            if ((item->next = top_item)) top_item->prev = item;
            top_item = item;
        }
        else
        {
            DMSG(0, "EventDispatcher::AddSocketInput() memory allocation error: %s\n", strerror(errno));
            return false;
        }
#ifdef WIN32
        HANDLE* hptr = new HANDLE[object_count+1];
        Item** iptr = new Item*[object_count+1];
        WSAEVENT eventHandle = WSACreateEvent();
        if (!hptr || !iptr || !eventHandle)
        {
            DMSG(0, "EventDispatcher::AddSocketInput() memory allocation error: %s\n", strerror(errno));
            if (iptr) delete iptr;
            if (hptr) delete hptr;
            if (eventHandle) WSACloseEvent(eventHandle);
            RemoveItem(item);
            return false;
        }
        if (object_handles)
        {
            ASSERT(object_count);
            memcpy(hptr, object_handles, object_count*sizeof(HANDLE));
            memcpy(iptr, object_items, object_count*sizeof(HANDLE));
            delete object_handles;
            delete object_items;
        }
        object_handles = hptr;
        object_items = iptr;
        object_handles[object_count] = eventHandle;
        object_items[object_count] = item;
        index = object_count++;
#endif // WIN32
        if ((INVALID_DESCRIPTOR == max_descriptor) || ((Descriptor)theSocket->Handle() > max_descriptor))
            max_descriptor = (Descriptor)theSocket->Handle();
    }
#ifdef WIN32
    else
    {
        // Find the item's index
        for (unsigned int i = 0; i < object_count; i++)
        {
            if (object_items[i] == item)
            {
                index = i;
                break;
            }
        }
    }
#endif // WIN32
    item->SetFlag(Item::INPUT);
#ifdef WIN32
    ASSERT(index < object_count);
    if (!Win32SocketInstall(item, object_handles[index]))
    {
        RemoveInput(item);
        return false;
    }
#endif // WIN32  
    return true;
}  // end EventDispatcher::AddSocketInput()

void EventDispatcher::RemoveGenericInput(EventDispatcher::Descriptor descriptor)
{
    Item* item = FindItemByDescriptor(descriptor);
    if (item) RemoveInput(item);
}  // end EventDispatcher::RemoveGenericInput()

void EventDispatcher::RemoveGenericOutput(EventDispatcher::Descriptor descriptor)
{
    Item* item = FindItemByDescriptor(descriptor);
    if (item) RemoveOutput(item);
}  // end EventDispatcher::RemoveGenericOutput()

void EventDispatcher::RemoveSocketInput(UdpSocket* theSocket)
{
    Item* item = FindItemByDescriptor((Descriptor)theSocket->Handle());
    if (item) RemoveInput(item);
}  // end EventDispatcher::RemoveSocketInput()

void EventDispatcher::ReduceItemStatus(Item *item, Item::Flag theFlag)
{
    item->UnsetFlag(theFlag);
#ifdef WIN32
    if (Item::SOCKET == item->type)
    {
        unsigned int index;
        for (index = 0; index < object_count; index++)
            if (object_items[index] == item) break;
        ASSERT(index < object_count);
        if (!Win32SocketInstall(item, object_handles[index])) 
            item->UnsetFlag(Item::OUTPUT);
    }
#endif  // WIN32
    if (!item->IsActive())
    {
#ifdef WIN32
        for (unsigned int i = 0; i < object_count; i++)
        {
            if (object_items[i] == item)
            {
                if (Item::SOCKET == item->type) WSACloseEvent(object_handles[i]);
                if (--object_count)
                {
                    memmove(object_handles+i+1, object_handles+i, (object_count-i)*sizeof(HANDLE));
                    memmove(object_items+i+1, object_items+i, (object_count-i)*sizeof(Item*));
                }
                else
                {
                    delete object_handles;
                    object_handles = NULL;
                    delete object_items;
                    object_items = NULL;
                }
            }
        }
#endif // WIN32
        RemoveItem(item);
    }
}  // end EventDispatcher::ReduceItemStatus()


EventDispatcher::Item* EventDispatcher::GetItemFromPool()
{   
    if (item_pool)
    {
        Item* item = item_pool;
        item_pool = item->next;
        return item;   
    }
    else
    {
        return NULL;  // Pool empty
    }
}  // end EventDispatcher::GetItemFromPool()

void EventDispatcher::ReturnItemToPool(Item* item)
{   
    item->next = item_pool;
    item_pool = item;
}  // end EventDispatcher::GetItemFromPool()



void EventDispatcher::RemoveItem(EventDispatcher::Item* item)
{
    if (item->prev)
        item->prev->next = item->next;
    else
        top_item = item->next;    
    if (item->next)
        item->next->prev = item->prev;
    if (item->descriptor == max_descriptor) max_descriptor = FindMaxDescriptor();
    ReturnItemToPool(item);    
}  // end void EventDispatcher::RemoveItem()


EventDispatcher::Descriptor EventDispatcher::FindMaxDescriptor()
{
    Item* next = top_item;
    if (next)
    {
        Descriptor max = next->descriptor;
        next = next->next;
        while (next)
        {
            if (next->descriptor > max) max = next->descriptor;
            next = next->next;
        }
        return max;
    }
    else
    {
        return INVALID_DESCRIPTOR;
    }
}  // end EventDispatcher::FindMaxDescriptor()

EventDispatcher::Item* EventDispatcher::FindItemByDescriptor(EventDispatcher::Descriptor descriptor)
{
    Item *next = top_item;
    while (next)
    {
        if (next->descriptor == descriptor) return next;
        next = next->next;
    }
    return NULL;
}  // end EventDispatcher::FindItemByDescriptor()


// An EventDispatcher "main loop"
int EventDispatcher::Run()
{
    run = true;
    while (run)
    {     
        if (IsPending())
        {
            int status = WaitForNextEvent();
            DispatchEvents(status);
        }
        else
        {
            DMSG(4, "EventDispatcher::Run() would be stuck with infinite timeout & no inputs!\n");
            break;
        }
    }  // end while(run)
#ifdef WIN32
    Win32Cleanup();
#endif // WIN32
    return exit_code;
}  // end EventDispatcher::Run()
#ifdef UNIX

// Warning! This may block indefinitely is !IsPending() ...
EventDispatcher::WaitStatus EventDispatcher::WaitForNextEvent()
{
    struct timeval* timeoutPtr; 
    struct timeval timeout;
    if (delay < 0.0)
    {
        timeoutPtr = NULL;      
    }
    else
    {
        timeout.tv_sec = (unsigned long) delay;
        timeout.tv_usec = 
            (unsigned long)(1.0e+06 * (delay - (double)timeout.tv_sec));
        timeoutPtr = &timeout;
    }
    FD_ZERO(&input_set);
    FD_ZERO(&output_set);
    Item* next = top_item;
    while (next)
    {
        switch (next->type)
        {
            case Item::GENERIC:
            case Item::SOCKET:
                if (next->FlagIsSet(Item::INPUT))
                    FD_SET(next->descriptor, &input_set);
                if (next->FlagIsSet(Item::OUTPUT))
                    FD_SET(next->descriptor, &output_set);
                break;
            case Item::TIMER:
                break;
        }
        next = next->next;
    }
    // (TBD) It would be nice to use "pselect()" here
    return  select(max_descriptor+1, 
                   (fd_set*)&input_set, 
                   (fd_set*)&output_set, 
                   (fd_set*) NULL, 
                   timeoutPtr);
}  // end EventDispatcher::WaitForNextEvent()

void EventDispatcher::DispatchEvents(EventDispatcher::WaitStatus status)
{
    switch (status)
    {
        case -1:
            if (EINTR != errno)
                DMSG(0, "EventDispatcher::Run() select() error: %s\n", strerror(errno));
            break;

        case 0:
            // timeout only
            break;

        default:
            // (TBD) can we make this iterate through fd's?
            // (and use an array like the WIN32 stuff for a
            //  a little better potential performance?
            Item* next = top_item;
            while(next)
            {
                switch (next->type)
                {
                    case Item::GENERIC:
                    case Item::SOCKET:
                        if (next->FlagIsSet(Item::INPUT))
                        {
                            if (FD_ISSET(next->descriptor, &input_set)) 
                            {
                                next->OnInputEvent();
                                FD_CLR(next->descriptor, &input_set);
                            }
                        }
                        if (next->FlagIsSet(Item::OUTPUT))
                        {
                            if (FD_ISSET(next->descriptor, &output_set)) 
                            {
                                next->OnOutputEvent();
                                FD_CLR(next->descriptor, &output_set);   
                            }
                        }
                        break;
                    case Item::TIMER:
                        break;
                }
                next = next->next;
            }
            break;
    }  // end switch(status)
    delay = -1.0;  // the "timer_mgr" will set "delay" as needed
    timer_mgr.DoTimers();
}  // end EventDispatcher::DispatchEvents()
#endif // UNIX


#ifdef WIN32
bool EventDispatcher::Win32Init()
{
    HINSTANCE theInstance = GetModuleHandle(NULL);    
    // Register our msg_window class
    WNDCLASS cl;
    cl.style = CS_HREDRAW | CS_VREDRAW;
    cl.lpfnWndProc = MessageHandler;
    cl.cbClsExtra = 0;
    cl.cbWndExtra = 0;
    cl.hInstance = theInstance;
    cl.hIcon = NULL;
    cl.hCursor = NULL;
    cl.hbrBackground = NULL;
    cl.lpszMenuName = NULL;
    cl.lpszClassName = "ProteanEventDispatcher";
    if (!RegisterClass(&cl))
    {
        DMSG(0, "EventDispatcher::Win32Init() Error registering message window class!\n");
        return false;
    }
    // Create msg_window to receive event messages
    msg_window = CreateWindow("ProteanEventDispatcher",  // registered class name
                              "ProteanEventDispatcher",  // window name
                              WS_OVERLAPPED,             // window style
                              0,                // horizontal position of window
                              0,                // vertical position of window
                              0,              // window width
                              0,             // window height
                              NULL,     // handle to parent or owner window
                              NULL,          // menu handle or child identifier
                              theInstance,  // handle to application instance
                              this);        // window-creation data
    if (msg_window)
    {
        ShowWindow(msg_window, SW_HIDE);
        return true;
    }
    else
    {
        DMSG(0, "EventDispatcher::Win32Init() Error creating message window!\n");
        return false;
    }
}  // end EventDispatcher::Win32Init()

void EventDispatcher::Win32Cleanup()
{
    if (msg_window)
    {
        DestroyWindow(msg_window);
        msg_window = NULL;
    }
}  // end Win32Cleanup()

EventDispatcher::WaitStatus EventDispatcher::WaitForNextEvent()
{
    DWORD msec = (delay < 0.0) ? INFINITE : ((DWORD)(1000.0 * delay));
    return MsgWaitForMultipleObjectsEx(object_count,   // number of handles in array
                                       object_handles, // object-handle array
                                       msec,           // time-out interval
                                       QS_ALLEVENTS,   // input-event type
                                       MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);
}  // end EventDispatcher::WaitForNextEvent()

void EventDispatcher::DispatchEvents(EventDispatcher::WaitStatus status)
{
    switch (status)
    {
        case -1:            // error status
            break;
        
        case WAIT_TIMEOUT:  // timeout condition
            break;
            
        default:
            if ((WAIT_OBJECT_0 <= status) && (status < (WAIT_OBJECT_0 + object_count)))
            {
                unsigned int index = status - WAIT_OBJECT_0;
                Item* item = object_items[index];
                ASSERT(item);
                switch (item->type)
                {
                    case Item::GENERIC:
                        if (item->IsInput() && item->callback) 
                            item->callback(item->descriptor, EVENT_INPUT, item->data);
                        break;
                    case Item::SOCKET:
                    {
                        WSANETWORKEVENTS event;
                        if (0 == WSAEnumNetworkEvents((SOCKET)item->descriptor, object_handles[index], &event))
                        {
                            if (0 != (event.lNetworkEvents & FD_READ)) ((UdpSocket*)item->data)->OnReceive();
                        }
                        else
                        {
                            DMSG(0, "EventDispatcher::Run() WSAEnumNetworkEvents() error\n");
                        }
                        break;
                    }
                    case Item::TIMER:
                        // should never occur
                        break;
                }
            }
            else if ((WAIT_OBJECT_0 + object_count) == status)
            {
                // Windows GUI message event
                MSG msg;
                if (GetMessage(&msg, NULL, 0, 0 ))
                {
                    TranslateMessage(&msg);
                    DispatchMessage(&msg);
                }
                else
                {
                    exit_code = msg.wParam;
                    break;  // we're done
                }
            }
            else
            {
                // (TBD) handle these conditions when needed
                // WAIT_ABANDONED or WAIT_IO_COMPLETION
            } 
            break;
    }  // end switch(status)
    delay = -1.0;  // the "timer_mgr" will set "delay" as needed
    timer_mgr.DoTimers();
}  // end EventDispatcher::DispatchEvents()

LRESULT CALLBACK EventDispatcher::MessageHandler(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message) 
    {
        case WM_CREATE:
        {
            CREATESTRUCT *info = (CREATESTRUCT *) lParam;
            EventDispatcher* dp = (EventDispatcher*)info->lpCreateParams;
            SetWindowLong(hwnd, GWL_USERDATA, (DWORD)dp);
            return 0;
        }    

        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;

        default:
            break;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}  // end EventDispatcher::MessageHandler()


bool EventDispatcher::Win32SocketInstall(EventDispatcher::Item* item, HANDLE eventHandle)
{
    long events = 0;
    if (item->IsInput()) events |= (FD_READ);
    if (item->IsOutput()) events |= (FD_WRITE | FD_CONNECT);
    if (WSAEventSelect((SOCKET)item->descriptor, eventHandle, events))
    {
        DMSG(0, "EventDispatcher::Win32SocketInstall() WSAEventSelect() error!\n");
        return false;
    }
    return true;
}  // end EventDispatcher::Win32SocketInstall()

#endif // WIN32
        
