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

#include <X11/Intrinsic.h>

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

#include <X11Reactor.h>

// ----------------------------------------------------------------------
// OBX11Reactor input callback procedures
// ----------------------------------------------------------------------

static void
ReadInputCB(XtPointer clientData, int*, XtInputId*)
{
    OBEventHandler* handler = (OBEventHandler*)clientData;
    handler -> handleEvent(OBEventRead);
}

static void
WriteInputCB(XtPointer clientData, int*, XtInputId*)
{
    OBEventHandler* handler = (OBEventHandler*)clientData;
    handler -> handleEvent(OBEventWrite);
}

static void
TimeoutCB(XtPointer clientData, XtIntervalId*)
{
    *(bool*)clientData = true;
}

// ----------------------------------------------------------------------
// OBX11Reactor constructor and destructor
// ----------------------------------------------------------------------

OBX11Reactor::OBX11Reactor(XtAppContext context)
    : stop_(false), context_(context), handlerInfoList_(0)
{
}

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

// ----------------------------------------------------------------------
// OBX11Reactor public member implementation
// ----------------------------------------------------------------------

OBReactor*
OBX11Reactor::instance(XtAppContext context)
{
    if(!instance_)
	instance_ = new OBX11Reactor(context);

    return instance_;
}

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

    HandlerInfo* info = handlerInfoList_;
    while(info)
    {
	if(info -> handler == handler)
	{
	    //
	    // Handler already in list, change inputs if necessary and
	    // update mask and handle
	    //
	    
	    if((info -> mask & OBEventRead) != (mask & OBEventRead) ||
	       info -> handle != handle)
	    {
		if(info -> mask & OBEventRead)
		{
		    XtRemoveInput(info -> readId);
		}

		if(mask & OBEventRead)
		{
		    info -> readId =
			XtAppAddInput(context_,
				      handle,
				      (XtPointer)XtInputReadMask,
				      ReadInputCB,
				      (XtPointer)info -> handler);
		}
	    }
	    
	    if((info -> mask & OBEventWrite) != (mask & OBEventWrite) ||
	       info -> handle != handle)
	    {
		if(info -> mask & OBEventWrite)
		{
		    XtRemoveInput(info -> writeId);
		}
		
		if(mask & OBEventWrite)
		{
		    info -> writeId =
			XtAppAddInput(context_,
				      handle,
				      (XtPointer)XtInputWriteMask,
				      WriteInputCB,
				      (XtPointer)info -> handler);
		}
	    }

	    info -> mask = mask;
	    info -> handle = handle;
	    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 inputs
    //
    if(mask & OBEventRead)
	info -> readId = XtAppAddInput(context_,
				    info -> handle,
				    (XtPointer)XtInputReadMask,
				    ReadInputCB,
				    (XtPointer)info -> handler);
    
    if(mask & OBEventWrite)
	info -> writeId = XtAppAddInput(context_,
				     info -> handle,
				     (XtPointer)XtInputWriteMask,
				     WriteInputCB,
				     (XtPointer)info -> handler);
}

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

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

	if(info -> handler == handler)
	{
	    //
	    // Handler info found, remove inputs if necessary, and
	    // invalidate the handler
	    //

	    if(info -> mask & OBEventRead)
		XtRemoveInput(info -> readId);
	    
	    if(info -> mask & OBEventWrite)
		XtRemoveInput(info -> writeId);
	    
	    *p = info -> next;
	    delete info;
	    return;
	}
	
	p = &(info -> next);
    }
	
    assert(false);
}

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

    while(!stop_ && handlerInfoList_)
    {
	//
	// Process one X11 event
	//
	XtAppProcessEvent(context_, XtIMAll);
    }
}

bool
OBX11Reactor::dispatchOneEvent(CORBA_Long timeout)
{
    if(stop_ || !handlerInfoList_)
	return false;

    if(timeout <= 0)
    {
	if(timeout == 0)
	{
	    //
	    // Handle zero timeouts
	    //
	    if(XtAppPending(context_) == 0)
	    {
		return false;
	    }
	}
	    
	//
	// Process one X11 event
	//
	XtAppProcessEvent(context_, XtIMAll);

	//
	// No timeout - return true
	//
	return true;
    }
    else
    {
	//
	// Set timeout callback
	//
	bool b = false;
	XtIntervalId id = XtAppAddTimeOut(context_, timeout, TimeoutCB, &b);

	//
	// Process one X11 event
	//
	XtAppProcessEvent(context_, XtIMAll);

	if(b)
	{
	    //
	    // There was a timeout
	    // Timeout callback has been removed automatically
	    //
	    return false;
	}
	else
	{
	    //
	    // Remove timeout callback
	    //
	    XtRemoveTimeOut(id);

	    //
	    // Return true - no timeout
	    //
	    return true;
	}
    }
}

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

void
OBX11Init(XtAppContext appContext)
{
    //
    // This will choose OBX11Reactor as reactor singleton
    //
    OBX11Reactor::instance(appContext);
}
