// **********************************************************************
//
// Copyright (c) 1999
// Object Oriented Concepts, Inc.
// Billerica, MA, USA
//
// All Rights Reserved
//
// **********************************************************************

#include <OB/Basic.h>
#include <OB/Except.h>
#include <OB/Reactor.h>
#include <OB/Net.h>
#include <OB/Util.h>

#include <WinReactor.h>

#define WM_WINSOCK (WM_USER + 1)

// ----------------------------------------------------------------------
// Windows message processing function
// ----------------------------------------------------------------------

static OBWinReactor* WinReactor = 0;

static LRESULT CALLBACK
WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    assert(WinReactor);

    return WinReactor -> wndProc(hWnd, message, wParam, lParam);
}

// ----------------------------------------------------------------------
// OBWinReactor constructor and destructor
// ----------------------------------------------------------------------

OBWinReactor::OBWinReactor(HINSTANCE hInstance)
    : stop_(false), handlerInfoList_(0)
{
    assert(WinReactor == 0);
    WinReactor = this;
    
    static char szAppName[] = "OBReactor";
    WNDCLASSEX  wndclass;
    
    wndclass.cbSize        = sizeof(wndclass);
    wndclass.style         = 0;
    wndclass.lpfnWndProc   = WndProc;
    wndclass.cbClsExtra    = 0;
    wndclass.cbWndExtra    = 0;
    wndclass.hInstance     = hInstance;
    wndclass.hIcon         = 0;
    wndclass.hCursor       = 0;
    wndclass.hbrBackground = 0;
    wndclass.lpszMenuName  = 0;
    wndclass.lpszClassName = szAppName;
    wndclass.hIconSm       = 0;
    
    RegisterClassEx(&wndclass);
    
    hWnd_ = CreateWindow(szAppName,
			 "The Windows Reactor",
			 WS_OVERLAPPEDWINDOW,
			 CW_USEDEFAULT,
			 CW_USEDEFAULT,
			 CW_USEDEFAULT,
			 CW_USEDEFAULT,
			 0,
			 0,
			 hInstance,
			 0);
}

OBWinReactor::~OBWinReactor()
{
    DestroyWindow(hWnd_);

    assert(WinReactor == this);
    WinReactor = 0;

    //
    // Delete all handler infos
    //
    while(handlerInfoList_)
    {
	HandlerInfo* next = handlerInfoList_ -> next;
	delete handlerInfoList_;
	handlerInfoList_ = next;
    }
}

// ----------------------------------------------------------------------
// OBWinReactor public member implementation
// ----------------------------------------------------------------------

OBReactor*
OBWinReactor::instance(HINSTANCE hInstance)
{
    if(!instance_)
	instance_ = new OBWinReactor(hInstance);

    return instance_;
}

void
OBWinReactor::registerHandler(OBEventHandler* handler,
			      OBMask mask, OBHandle handle)
{
    assert(handler);

    HandlerInfo* info = handlerInfoList_;
    while(info)
    {
	if(info -> handler == handler)
	{
	    //
	    // Handler already in list, change notification messages
	    // and update mask and handle if necessary
	    //
	    if(info -> mask != mask || info -> handle != handle)
	    {
		WSAAsyncSelect(info -> handle, hWnd_, WM_WINSOCK, 0);

		info -> handle = handle;
		info -> mask = mask;

		long lEvent = 0;

		if(mask & OBEventRead)
		    lEvent |= FD_READ | FD_ACCEPT | FD_CLOSE;

		if(mask & OBEventWrite)
		    lEvent |= FD_WRITE | FD_CONNECT;

		WSAAsyncSelect(info -> handle, hWnd_, WM_WINSOCK, lEvent);
	    }

	    return;
	}
	
	info = info -> next;
    }

    //
    // Add new handler info
    //
    info = new HandlerInfo;
    info -> handler = handler;
    info -> mask = mask;
    info -> handle = handle;
    info -> next = handlerInfoList_;
    handlerInfoList_ = info;

    //
    // Add notification messages
    //
    long lEvent = 0;

    if(mask & OBEventRead)
	lEvent |= FD_READ | FD_ACCEPT | FD_CLOSE;
    
    if(mask & OBEventWrite)
	lEvent |= FD_WRITE | FD_CONNECT;
    
    WSAAsyncSelect(info -> handle, hWnd_, WM_WINSOCK, lEvent);
}

void
OBWinReactor::unregisterHandler(OBEventHandler* handler)
{
    assert(handler);

    HandlerInfo** p = &handlerInfoList_;
    while(*p)
    {
	HandlerInfo* info = *p;

	if(info -> handler == handler)
	{
	    //
	    // Handler info found, remove notification messages and
	    // invalidate the handler
	    //
	    WSAAsyncSelect(info -> handle, hWnd_, WM_WINSOCK, 0);
	    *p = info -> next;
	    delete info;
	    return;
	}
	
	p = &(info -> next);
    }

    assert(false);
}

void
OBWinReactor::dispatch()
{
    stop_ = false;

    MSG msg;
    while(!stop_ && handlerInfoList_ && GetMessage(&msg, 0, 0, 0))
    {
	//
	// Process one Windows message
	//
	TranslateMessage(&msg);
	DispatchMessage(&msg);
    }
}

bool
OBWinReactor::dispatchOneEvent(CORBA_Long timeout)
{
    assert_nca(timeout == -1 || timeout == 0, OBNCANotImpl);

    if(stop_ || !handlerInfoList_)
	return false;

    MSG msg;
    BOOL result;

    if(timeout == -1)
	result = GetMessage(&msg, 0, 0, 0);
    else if(timeout == 0)
	result = PeekMessage(&msg, 0, 0, 0, PM_REMOVE);

    if(result)
    {
	//
	// Process one Windows message
	//
	TranslateMessage(&msg);
	DispatchMessage(&msg);

	return true;
    }
    else
	return false;
}

LRESULT
OBWinReactor::wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    if(hWnd == hWnd_ && message == WM_WINSOCK)
    {
	HandlerInfo* info = handlerInfoList_;
	while(info)
	{
	    if((WPARAM)info -> handle == wParam)
	    {
		//
		// Calculate the event mask
		//
		OBMask mask = 0;
		
		switch(WSAGETSELECTEVENT(lParam))
		{
		case FD_READ:
		case FD_ACCEPT:
		case FD_CLOSE:
		    mask |= OBEventRead;
		    break;
		
		case FD_WRITE:
		case FD_CONNECT:
		    mask |= OBEventWrite;
		    break;
		}

		//
		// Execute the event
		//
		if(info -> mask & mask)
		    info -> handler -> handleEvent(mask);
		
		//
		// No further looping necessary, only one event can be
		// delivered through this operation
		//
		return 0;
	    }
	
	    info = info -> next;
	}
    }

    return DefWindowProc(hWnd, message, wParam, lParam);
}

// ----------------------------------------------------------------------
// OBWinViewer public member implementation
// ----------------------------------------------------------------------

OBMessageViewer*
OBWinViewer::instance()
{
    if(!instance_)
	instance_ = new OBWinViewer();

    return instance_;
}

void
OBWinViewer::message(const char* msg)
{
    MessageBox(0, msg, 0, MB_ICONEXCLAMATION);
}

// ----------------------------------------------------------------------
// Initialization functions
// ----------------------------------------------------------------------

void
OBWindowsInit(HINSTANCE hInstance)
{
    //
    // This will choose OBWinReactor as reactor singleton
    //
    OBWinReactor::instance(hInstance);

    //
    // This will choose OBWinViewer as message viewer singleton
    //
    OBWinViewer::instance();
}

