#include "SysCallbacks.h"
#include "syscb/ifc_syscallback.h"

using namespace nu;

SysCallbacks::SysCallbacks() 
{
	reentry=0;
	inCallback=false;
}

//note: it's OK to add in the middle of an issueCallback
//because new callbacks go at the end of the list
//and the lockguard prevents list corruption
int SysCallbacks::SysCallbacks_RegisterCallback(ifc_sysCallback *cb)
{
	AutoLock lock(callbackGuard LOCKNAME("SysCallbacks::syscb_registerCallback"));
	callbacks.push_back(cb); 
	return 0;
}

int SysCallbacks::SysCallbacks_UnregisterCallback(ifc_sysCallback *cb)
{
	AutoLock lock(callbackGuard LOCKNAME("SysCallbacks::syscb_deregisterCallback"));
	if (inCallback)
		deleteMeAfterCallbacks.push_back(cb);
	else
	{
		//callbacks.eraseAll(cb);
		auto it = callbacks.begin();
		while (it != callbacks.end())
		{
			if (*it != cb)
			{
				it++;
				continue;
			}

			it = callbacks.erase(it);
		}
	}
	return 0;
}

int SysCallbacks::SysCallbacks_IssueCallback( GUID eventtype, int msg, intptr_t param1, intptr_t param2 )
{
	AutoLock lock( callbackGuard LOCKNAME( "SysCallbacks::syscb_issueCallback" ) );
	reentry++;
	inCallback = true;

	for ( ifc_sysCallback *l_call_back : callbacks )
	{
		//if (!deleteMeAfterCallbacks.contains(callbacks[i]) && callbacks[i]->GetEventType() == eventtype)
		if ( deleteMeAfterCallbacks.end() == std::find( deleteMeAfterCallbacks.begin(), deleteMeAfterCallbacks.end(), l_call_back ) && l_call_back->GetEventType() == eventtype )
			l_call_back->Notify( msg, param1, param2 );
	}
	inCallback = false;
	reentry--;
	if ( reentry == 0 )
	{
		for ( ifc_sysCallback *l_delete_me_after_call_back : deleteMeAfterCallbacks )
		{
			//callbacks.eraseAll(deleteMeAfterCallbacks[i]);
			auto it = callbacks.begin();
			while ( it != callbacks.end() )
			{
				if ( *it != l_delete_me_after_call_back )
				{
					it++;
					continue;
				}

				it = callbacks.erase( it );
			}
		}
		deleteMeAfterCallbacks.clear();
	}
	return 0;
}

ifc_sysCallback *SysCallbacks::SysCallbacks_Enum( GUID eventtype, size_t n )
{
	AutoLock lock( callbackGuard LOCKNAME( "SysCallbacks::syscb_enum" ) );
	// TODO: maybe check !deleteMeAfterCallbacks.contains(callbacks[i])
	for ( ifc_sysCallback *callback : callbacks )
	{
		if ( callback->GetEventType() == eventtype )
		{
			if ( n-- == 0 )
			{
				// benski> don't be fooled.  most objects don't actually support reference counting
				callback->Retain();
				return callback;
			}
		}
	}
	return 0;
}