//
//  AfficheurGrowl.m
//  Afficheur
//
//  Created by kichi on 08/10/06.
//  Copyright 2008,2009,2010 Katsuhiko Ichinose. All rights reserved.
//

#import "AfficheurGrowl.h"
#import "GrowlNotifier.h"


@implementation AfficheurGrowl

NSString*	GrowlAfficheurSuccessNotify				= @"Afficheur post was successful";
NSString*	GrowlAfficheurFailureNotify				= @"Afficheur post was failure";
NSString*	GrowlAfficheurKeywordsNotify			= @"Afficheur keywords";
NSString*	GrowlAfficheurTwitterNotify				= @"Twitter";
NSString*	GrowlAfficheurTwitterReplyNotify		= @"Twitter Reply";
NSString*	GrowlAfficheurTwitterDirectNotify		= @"Twitter Direct Message";
NSString*	GrowlAfficheurTwitterErrorNotify		= @"Twitter Error";
NSString*	GrowlAfficheurJaikuNotify				= @"Jaiku";
NSString*	GrowlAfficheurJaikuChannelNotify		= @"Jaiku Channel";
NSString*	GrowlAfficheurJaikuCommentNotify		= @"Jaiku Comment";
NSString*	GrowlAfficheurJaikuReplyNotify			= @"Jaiku Reply";
NSString*	GrowlAfficheurJaikuErrorNotify			= @"Jaiku Error";
NSString*	GrowlAfficheurWassrNotify				= @"Wassr";
NSString*	GrowlAfficheurWassrChannelNotify		= @"Wassr Channel";
NSString*	GrowlAfficheurWassrReplyNotify			= @"Wassr Reply";
NSString*	GrowlAfficheurWassrErrorNotify			= @"Wassr Error";
//NSString*	GrowlAfficheurNowaNotify				= @"nowa";
//NSString*	GrowlAfficheurNowaReplyNotify			= @"nowa Reply";
//NSString*	GrowlAfficheurNowaErrorNotify			= @"nowa Error";
NSString*	GrowlAfficheurIdenticaNotify			= @"identi.ca";
NSString*	GrowlAfficheurIdenticaGroupNotify		= @"identi.ca Group";
NSString*	GrowlAfficheurIdenticaReplyNotify		= @"identi.ca Reply";
NSString*	GrowlAfficheurIdenticaDirectNotify		= @"identi.ca Direct Message";
NSString*	GrowlAfficheurIdenticaErrorNotify		= @"identi.ca Error";
//NSString*	GrowlAfficheurJiskoNotify				= @"Jisko";
//NSString*	GrowlAfficheurJiskoReplyNotify			= @"Jisko Reply";
//NSString*	GrowlAfficheurJiskoPrivateNotify		= @"Jisko Private Note";
//NSString*	GrowlAfficheurJiskoErrorNotify			= @"Jisko Error";
//NSString*	GrowlAfficheurChuitterNotify			= @"chuitter";
//NSString*	GrowlAfficheurChuitterGroupNotify		= @"chuitter Group";
//NSString*	GrowlAfficheurChuitterReplyNotify		= @"chuitter Reply";
//NSString*	GrowlAfficheurChuitterDirectNotify		= @"chuitter Direct Message";
//NSString*	GrowlAfficheurChuitterErrorNotify		= @"chuitter Error";
NSString*	GrowlAfficheurTumblrNotify				= @"Tumblr";
NSString*	GrowlAfficheurTumblrReplyNotify			= @"Tumblr Reply";
NSString*	GrowlAfficheurTumblrDirectNotify		= @"Tumblr Direct Message";
NSString*	GrowlAfficheurTumblrErrorNotify			= @"Tumblr Error";
NSString*	GrowlAfficheurFriendFeedNotify			= @"FriendFeed";
NSString*	GrowlAfficheurFriendFeedGroupNotify		= @"FriendFeed Group";
NSString*	GrowlAfficheurFriendFeedCommentNotify	= @"FriendFeed Comment";
NSString*	GrowlAfficheurFriendFeedReplyNotify		= @"FriendFeed Reply";
NSString*	GrowlAfficheurFriendFeedErrorNotify		= @"FriendFeed Error";
NSString*	GrowlAfficheurFaceBookNotify			= @"FaceBook";
NSString*	GrowlAfficheurFaceBookGroupNotify		= @"FaceBook Group";
NSString*	GrowlAfficheurFaceBookCommentNotify		= @"FaceBook Comment";
NSString*	GrowlAfficheurFaceBookReplyNotify		= @"FaceBook Reply";
NSString*	GrowlAfficheurFaceBookErrorNotify		= @"FaceBook Error";

NSString*	KeyGrowlTitle				= @"title";
NSString*	KeyGrowlDescription			= @"description";
NSString*	KeyGrowlNotificationName	= @"notificationName";
NSString*	KeyGrowlIconData			= @"iconData";
NSString*	KeyGrowlClickContext		= @"clickContext";

- (AfficheurGrowl *)init
{
	//LOG(@"[%@ init]", [self className]);
	self = [super init];
	if (self)
	{
		_delegate = nil;
		_mainThread = nil;
		_queueGrowl = [[NSMutableArray alloc] init];
		_lockQueueGrowl = [[NSLock alloc] init];
		_lockedGrowl = NO;
		_lockGrowl = [[NSLock alloc] init];
		_sleepDate = nil;
	}
	return self;
}

- (void)dealloc
{
	[_queueGrowl release];
	[_lockQueueGrowl release];
	[_lockGrowl release];
	if (_sleepDate)
	{
		[_sleepDate release];
	}
	[super dealloc];
}

- (void)performLockGrowl:(id)object
{
	@synchronized(_lockGrowl)
	{
		_lockedGrowl = YES;
		[_lockGrowl lock];
	}
}

- (void)lockGrowl
{
	if ([[NSThread currentThread] isEqual:_mainThread])
	{
		[self performLockGrowl:nil];
	}
	else
	{
		[self performSelectorOnMainThread:@selector(performLockGrowl:)
							   withObject:nil
							waitUntilDone:YES];
	}
}

- (void)performUnlockGrowl:(id)object
{
	@synchronized(_lockGrowl)
	{
		if (_lockedGrowl)
		{
			_lockedGrowl = NO;
			[_lockGrowl unlock];
		}
	}
}

- (void)unlockGrowl
{
	if ([[NSThread currentThread] isEqual:_mainThread])
	{
		[self performUnlockGrowl:nil];
	}
	else
	{
		[self performSelectorOnMainThread:@selector(performUnlockGrowl:)
							   withObject:nil
							waitUntilDone:YES];
	}
}

- (void)getMainThread:(id)object
{
	_mainThread = [NSThread currentThread];
}

- (void)notifyWithTitle:(NSString *)title
			description:(NSString *)description
	   notificationName:(NSString *)notificationName
			   iconData:(NSData *)iconData
		   clickContext:(id)clickContext
{
	LOG(@"[%@ notifyWithTitle] %@", [self className], notificationName);
	[_lockQueueGrowl lock];
	@try
	{
		[_queueGrowl addObject:[NSDictionary dictionaryWithObjectsAndKeys:
								title, KeyGrowlTitle,
								description, KeyGrowlDescription,
								notificationName, KeyGrowlNotificationName,
								[NSData dataWithData:iconData], KeyGrowlIconData,
								clickContext, KeyGrowlClickContext,
								nil]];
	}
	@catch (NSException *exception)
	{
		EXPLOG(@"[%@ notifyWithTitle] EXCEPTION\n%@: %@", [self className], [exception name], [exception reason]);
	}
	[_lockQueueGrowl unlock];
	[self unlockGrowl];
}

- (void)performNotify:(NSDictionary *)notify
{
	@synchronized(_growl)
	{
		//LOG(@"[%@ performNotify]\n%@\n%@", [self className], [notify valueForKey:KeyGrowlTitle], [notify valueForKey:KeyGrowlDescription]);
		[_growl notifyWithTitle:[notify valueForKey:KeyGrowlTitle]
					description:[notify valueForKey:KeyGrowlDescription]
			   notificationName:[notify valueForKey:KeyGrowlNotificationName]
					   iconData:[notify valueForKey:KeyGrowlIconData]
					   priority:0
					   isSticky:NO
				   clickContext:[notify valueForKey:KeyGrowlClickContext]];
	}
}

- (void)threadGrowl:(id)object
{
	//NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	@try
	{
		UInt32	notifyCount = 0;
		for (;;)
		{
			NSAutoreleasePool *internalPool = [[NSAutoreleasePool alloc] init];
			[_lockGrowl lock];
			@try
			{
				[_lockQueueGrowl lock];
				@try
				{
					while ([_queueGrowl count] > 0)
					{
						id obj = [_queueGrowl objectAtIndex:0];
						int count = [_queueGrowl count];
						[_lockQueueGrowl unlock];
						@try
						{
							float timeInterval;
							if (_sleepDate)
							{
								timeInterval = [_sleepDate timeIntervalSinceNow];
								LOG(@"[%@ threadGrowl] %f", [self className], timeInterval);
								if (timeInterval < -10)
								{
									notifyCount = 0;
								}
								else if (timeInterval < -4.5)
								{
								}
								else
								{
									[_lockQueueGrowl lock];
									count = [_queueGrowl count];
									[_lockQueueGrowl unlock];
									BOOL zero = NO;
									if ((count <= 1) && (timeInterval < 0))
									{
										NSDate *sleepDate = _sleepDate;
										_sleepDate = [[_sleepDate addTimeInterval:4.5] retain];
										[sleepDate release];
										zero = YES;
									}
									while ([_sleepDate timeIntervalSinceNow] > 0)
									{
										[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.33]];
										[_lockQueueGrowl lock];
										count = [_queueGrowl count];
										[_lockQueueGrowl unlock];
										if (zero && count > 0)
										{
											break;
										}
									}
								}
								[_sleepDate release];
								_sleepDate = nil;
							}
							LOG(@"[%@ threadNotify] performNotify", [self className]);
							[self performNotify:obj];
							notifyCount++;
							[_lockQueueGrowl lock];
							count = [_queueGrowl count];
							[_lockQueueGrowl unlock];
							if ((count <= 0) && (notifyCount % 6 == 0))
							{
								timeInterval = 4.5;
							}
							else if ((count <= 9) && (notifyCount % 3 == 0))
							{
								timeInterval = 1;
							}
							else
							{
								timeInterval = 0.33;
							}
							_sleepDate = [[NSDate dateWithTimeIntervalSinceNow:timeInterval] retain];
						}
						@catch (NSException *exception)
						{
							EXPLOG(@"[%@ threadNotify] EXCEPTION\n%@: %@", [self className], [exception name], [exception reason]);
						}
						@finally
						{
							[_lockQueueGrowl lock];
							[_queueGrowl removeObjectAtIndex:0];
						}
					}
				}
				@finally
				{
					[_lockQueueGrowl unlock];
				}
			//	[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.67]];
			}
			@catch (NSException *exception)
			{
				EXPLOG(@"[%@ threadNotify] EXCEPTION\n%@: %@", [self className], [exception name], [exception reason]);
			}
			@finally
			{
				[_lockGrowl unlock];
			}
			[self lockGrowl];
			[internalPool release];
		}
	}
	@catch (NSException *exception)
	{
		EXPLOG(@"[%@ threadNotify] EXCEPTION\n%@: %@", [self className], [exception name], [exception reason]);
	}
	//[pool release];
	[NSThread exit];
}

- (void)startWithDelegate:(id)delegate
{
	_delegate = delegate;
	[_growl initWithDelegate:self];
	[_growl startWithApplicationName:@"Afficheur"
					   notifications:[NSArray arrayWithObjects:
									  GrowlAfficheurSuccessNotify,
									  GrowlAfficheurFailureNotify,
									  GrowlAfficheurKeywordsNotify,
									  GrowlAfficheurTwitterNotify,
									  GrowlAfficheurTwitterReplyNotify,
									  GrowlAfficheurTwitterDirectNotify,
									  GrowlAfficheurTwitterErrorNotify,
									  GrowlAfficheurJaikuNotify,
									  GrowlAfficheurJaikuChannelNotify,
									  GrowlAfficheurJaikuCommentNotify,
									  GrowlAfficheurJaikuReplyNotify,
									  GrowlAfficheurJaikuErrorNotify,
									  GrowlAfficheurWassrNotify,
									  GrowlAfficheurWassrChannelNotify,
									  GrowlAfficheurWassrReplyNotify,
									  GrowlAfficheurWassrErrorNotify,
//									  GrowlAfficheurNowaNotify,
//									  GrowlAfficheurNowaReplyNotify,
//									  GrowlAfficheurNowaErrorNotify,
									  GrowlAfficheurIdenticaNotify,
									  GrowlAfficheurIdenticaGroupNotify,
									  GrowlAfficheurIdenticaReplyNotify,
									  GrowlAfficheurIdenticaDirectNotify,
									  GrowlAfficheurIdenticaErrorNotify,
//									  GrowlAfficheurJiskoNotify,
//									  GrowlAfficheurJiskoReplyNotify,
//									  GrowlAfficheurJiskoPrivateNotify,
//									  GrowlAfficheurJiskoErrorNotify,
//									  GrowlAfficheurChuitterNotify,
//									  GrowlAfficheurChuitterGroupNotify,
//									  GrowlAfficheurChuitterReplyNotify,
//									  GrowlAfficheurChuitterDirectNotify,
//									  GrowlAfficheurChuitterErrorNotify,
									  GrowlAfficheurTumblrNotify,
									  GrowlAfficheurTumblrReplyNotify,
									  GrowlAfficheurTumblrDirectNotify,
									  GrowlAfficheurTumblrErrorNotify,
									  GrowlAfficheurFriendFeedNotify,
									  GrowlAfficheurFriendFeedGroupNotify,
									  GrowlAfficheurFriendFeedCommentNotify,
									  GrowlAfficheurFriendFeedReplyNotify,
									  GrowlAfficheurFriendFeedErrorNotify,
									  GrowlAfficheurFaceBookNotify,
									  GrowlAfficheurFaceBookGroupNotify,
									  GrowlAfficheurFaceBookCommentNotify,
									  GrowlAfficheurFaceBookReplyNotify,
									  GrowlAfficheurFaceBookErrorNotify,
									  nil]
				dafaultNotifications:nil
								icon:nil];

	[self performSelectorOnMainThread:@selector(getMainThread:)
						   withObject:nil
						waitUntilDone:YES];
	[NSThread detachNewThreadSelector:@selector(threadGrowl:)
							 toTarget:self
						   withObject:nil];
}

// Growl Delegate

- (void)growlIsReady
{
}

- (void)growlNotificationWasClicked:(id)clickContext
{
	if (_delegate && [_delegate respondsToSelector:@selector(growlNotificationWasClicked:)])
	{
		[_delegate performSelector:@selector(growlNotificationWasClicked:) withObject:clickContext];
	}
}

@end
