//
//  GTRunLoop.mm
//  All
//
//  Created by nhn on 10. 12. 31..
//  Copyright 2010 nhn KEH. All rights reserved.
//

#import "GTRunLoopSource.h"


static void RunLoopSourceScheduleRoutine (void *info, CFRunLoopRef rl, CFStringRef mode);
static void RunLoopSourcePerformRoutine (void *info);
static void RunLoopSourceCancleRoutine(void *info, CFRunLoopRef rl, CFStringRef mode);

@implementation GTRunLoopSource

@synthesize delegate;

- (id)initWithDelegate:(id)aDelegate withDisposableSource:(BOOL)aIsDisposableSource
{
	self = [super init];
	
	CFRunLoopSourceContext context = {0, self, NULL, NULL, NULL, NULL, NULL,
		&RunLoopSourceScheduleRoutine,
		RunLoopSourceCancleRoutine,
		RunLoopSourcePerformRoutine};	
	
	runLoopSource = CFRunLoopSourceCreate(NULL, 0, &context);
	commandBuffer = [[NSMutableArray alloc] init];
	lock = [[NSRecursiveLock alloc] init];
	delegate = aDelegate;
	isDisposableSource = aIsDisposableSource;
	
	return self;	
}


- (void)dealloc
{	
	[self invalidate];
	
	CFRelease(runLoopSource);
	
	[commandBuffer release];
	[lock release];
	
	[super dealloc];
}

- (void)addToRunLoop:(CFRunLoopRef)runloop
{
	CFRunLoopAddSource(runloop, runLoopSource, kCFRunLoopDefaultMode);
}


- (void)addToCurrentRunLoop
{
	CFRunLoopRef runLoop = CFRunLoopGetCurrent();
	[self addToRunLoop:runLoop];
}


- (void)addToMainRunLoop
{
	CFRunLoopRef runLoop = CFRunLoopGetMain();
	[self addToRunLoop:runLoop];	
}

- (void)invalidate
{
	CFRunLoopSourceInvalidate(runLoopSource);
}

// Handler method
- (void)sourceFired
{
	[lock lock];

	while([commandBuffer count] > 0)
	{
		id obj = [commandBuffer objectAtIndex:0];
		if ([delegate respondsToSelector:@selector(onGTRunLoop:didSourceFiredWithCommand:)])
		{
			[delegate onGTRunLoop:self didSourceFiredWithCommand:obj];
			
			[commandBuffer removeObjectAtIndex:0];
		}
	}
	
	[lock unlock];
	
	if(isDisposableSource)
		[self invalidate];
}

- (void)addCommand:(id)command
{	
	[lock lock];
	
	[commandBuffer addObject:command];
	
	[lock unlock];
}

- (void)fireAllCommandsOnRunLoop:(CFRunLoopRef)runloop
{
	CFRunLoopSourceSignal(runLoopSource);
	CFRunLoopWakeUp(runloop);
}

@end
///////////////////////////////////////////////////////////////////////////////////////////


@implementation GTRunLoopContext


@synthesize runLoop;
@synthesize source;


- (id)initWithSource:(GTRunLoopSource*)src andLoop:(CFRunLoopRef)loop
{
	self = [super init];
	runLoop = loop;
	source = [src retain];
	
	return self;
}


- (void)dealloc
{
	[source release];
	
	[super dealloc];
}


- (BOOL)isEqual:(id)anObject
{
	if ( [anObject isKindOfClass:[GTRunLoopContext class]] == NO )
		return NO;

	GTRunLoopContext *context = (id)anObject;
	
	if( runLoop == context.runLoop
	 && source == context.source
	  )
		return YES;
	return NO;
}

@end
///////////////////////////////////////////////////////////////////////////////////////////


static void RunLoopSourceScheduleRoutine (void *info, CFRunLoopRef rl, CFStringRef mode)
{
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

	GTRunLoopSource* obj = [(GTRunLoopSource*)info retain];

	GTRunLoopContext *context = [[[GTRunLoopContext alloc] initWithSource:obj andLoop:rl] autorelease];

	if ([obj.delegate respondsToSelector:@selector(onGTRunLoop_RegisterSource:)])
	{
		[obj.delegate onGTRunLoop_RegisterSource:context];
	}

    [obj release];
	[pool release];
}

	
static void RunLoopSourcePerformRoutine (void *info)
{
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

	GTRunLoopSource* obj = [(GTRunLoopSource*)info retain];
	
	[obj sourceFired];
    
    [obj release];
	
	[pool release];
}


static void RunLoopSourceCancleRoutine(void *info, CFRunLoopRef rl, CFStringRef mode)
{
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

	GTRunLoopSource* obj = [(GTRunLoopSource*)info retain];
	GTRunLoopContext *context = [[[GTRunLoopContext alloc] initWithSource:obj andLoop:rl] autorelease];
		
	if ([obj.delegate respondsToSelector:@selector(onGTRunLoop_RegisterSource:)])
	{
		[obj.delegate onGTRunLoop_RemoveSource:context];
	}
    
    [obj release];
	
	[pool release];
}


