#include "Desk.DeskMem.h"
#include "Desk.Time.h"
#include "Desk.LinkList.h"
#include "Desk.Event.h"
#include "Desk.WimpSWIs.h"
#include "Desk.Event.h"



typedef struct
{
	Desk_linklist_header	header;
	unsigned int		t;
	Desk_event_handler	handler;
	void*			reference;
	Desk_bool		active;
}
nullhandler;

static Desk_linklist_header	nullhandlers = { NULL, NULL};
static nullhandler*		global_activehandler = NULL;
static Desk_bool		global_in_nullhandler = Desk_bool_FALSE;


static Desk_bool	NullHandler( Desk_event_pollblock* event, void* reference);



static void	FreeHandler( nullhandler* handler)
{
	Desk_LinkList_Unlink( &nullhandlers, &handler->header);
	Desk_DeskMem_Free( handler);
	if ( !Desk_LinkList_FirstItem( &nullhandlers))
		Desk_Event_Release( Desk_event_NULL, Desk_event_ANY, Desk_event_ANY, NullHandler, NULL);
}


static Desk_bool	NullHandler( Desk_event_pollblock* event, void* reference)
{
	nullhandler*	nh;
	Desk_bool	handlerreturn = Desk_bool_FALSE;
	unsigned int	t = Desk_Time_Monotonic();
	
	global_in_nullhandler = Desk_bool_TRUE;
	
	for ( nh=Desk_LinkList_FirstItem( &nullhandlers); nh;)
	{
		if ( !nh->active)
		{
			nh=Desk_LinkList_NextItem( &nh->header);
			continue;
		}
		
		else if ( nh->t <= t)
		{
			global_activehandler = nh;
			handlerreturn = nh->handler( event, nh->reference);
			// The following will only be used if we have handlers that aren't removed
			// after the first time they are called.
			/*
			if ( global_activehandler == NULL)	// handler->handler tried to free itself
			{
				nullhandler*	nh2 = nh;
				nh=Desk_LinkList_NextItem( &nh->header);
				FreeHandler( nh2);
			}
			else	nh=Desk_LinkList_NextItem( &nh->header);
			*/
			{
				nullhandler*	nh2 = nh;
				nh=Desk_LinkList_NextItem( &nh->header);
				FreeHandler( nh2);
			}
			global_activehandler = NULL;
			
			if ( handlerreturn==Desk_bool_TRUE)
			{
				//Desk_Sound_SysBeep();
				break;
			}
				// Don't call any more handlers.
				// Mimics behaviour of normal Event dispatching.
		}
		else	break;
	}
	
	global_in_nullhandler = Desk_bool_FALSE;
	
	// Make all handlers active, if not already so. Only handlers registered
	// while this function was running will be inactive, to prevent them
	// being called before a call to Wimp_Poll.
	for ( nh=Desk_LinkList_FirstItem( &nullhandlers); nh; nh=Desk_LinkList_NextItem( &nh->header))
		nh->active = Desk_bool_TRUE;
	
	return handlerreturn;	// We return what the last idlehandler returned, or FALSE.
	
	Desk_UNUSED( event);
	Desk_UNUSED( reference);
}

void	Desk_Event_ClaimNullIdle( Desk_event_handler handlerfn, unsigned int earliest, void* reference)
{
	nullhandler*	nh;
	nullhandler*	handler = Desk_DeskMem_MallocType( nullhandler);
	
	handler->t		= earliest;
	handler->handler	= handlerfn;
	handler->reference	= reference;
	handler->active		= (global_in_nullhandler) ? Desk_bool_FALSE : Desk_bool_TRUE;
		// New handlers are marked as inactive if we are currently dispatching
		// handlers - otherwise the new handlers will get called without an
		// intervening call to Wimp_Poll.
	
	for ( nh=Desk_LinkList_FirstItem( &nullhandlers); nh; nh=Desk_LinkList_NextItem( &nh->header))
	{
		if ( nh->t > handler->t)	break;
	}
	if ( nh)	Desk_LinkList_InsertBefore( &nullhandlers, &nh->header, &handler->header);
	else		Desk_LinkList_AddToTail( &nullhandlers, &handler->header);
	
	if ( !Desk_LinkList_NextItem( &handler->header) && !Desk_LinkList_PreviousItem( &handler->header))
		Desk_Event_Claim( Desk_event_NULL, Desk_event_ANY, Desk_event_ANY, NullHandler, NULL);
		// Start claiming null events if the new item is the only one in the list.
}



void	Desk_Event_ClaimNullIdleInterval( Desk_event_handler handler, int interval, void* reference)
{
	Desk_Event_ClaimNullIdle( handler, Desk_Time_Monotonic()+interval, reference);
}


void	Desk_Event_ReleaseNullIdle( Desk_event_handler handlerfn, void* reference)
{
	nullhandler*	nh;
	
	for ( nh=Desk_LinkList_FirstItem( &nullhandlers); nh; nh=Desk_LinkList_NextItem( &nh->header))
	{
		if ( nh->handler==handlerfn && nh->reference==reference)
		{
			if ( global_activehandler == nh)
				global_activehandler = NULL;	// Causes it to be freed only when the handler returns.
			else	FreeHandler( nh);
			
			break;
		}
	}
	
	Desk_Debug_Printf( "Desk_Event_ReleaseNullIdle() called with unknown handlerfn=0x%p, reference=0x%p\n", handlerfn, reference);
	
	// Could actually always leave freeing of current handler to after the handler returns, without
	// using global_activehandler, because handlers are automatically freed after they return.
	// Using global_activehandler will help if we ever have repetative handlers...
}




void	Desk_Event_PollIdle3( void* pollword)
{
	nullhandler*	nh=Desk_LinkList_FirstItem( &nullhandlers);
	
	if ( nh)
		Desk_Wimp_PollIdle3( Desk_Event_mask, &Desk_Event_lastevent, nh->t, pollword);
	else	Desk_Wimp_Poll3( Desk_Event_mask, &Desk_Event_lastevent, pollword);
	
	Desk_Event_Process( &Desk_Event_lastevent);
}
