//
//  ServiceTumblr.m
//  Afficheur
//
//  Created by kichi on 08/09/02.
//  Copyright 2008 Katsuhiko Ichinose. All rights reserved.
//

#import "ServiceTumblrKey.h"
#import "ServiceTumblr.h"
#import "AfficheurController.h"
#import "AfficheurPreferences.h"
#import "AfficheurGrowl.h"
#import "AfficheurTimeline.h"
#import "TumblrAPI.h"
#import "NSStringOgreKit.h"
#import <OgreKit/OgreKit.h>


@implementation ServiceTumblr

static NSString* REQUEST_TOKEN_PATH = @"http://tumblr.com/oauth/request_token";
static NSString* AUTHORIZE_PATH = @"http://tumblr.com/oauth/authorize";
static NSString* ACCESS_TOKEN_PATH = @"http://tumblr.com/oauth/access_token";

- (NSString *)authorize
{
	LOG(@"[%@ authorize]", [self className]);
	NSString *authorize = nil;
	@try
	{
		OAuth *oAuth = [[OAuth alloc] init];
		[oAuth setConsumerKey:CONSUMER_KEY];
		[oAuth setConsumerSecret:CONSUMER_SECRET];
		[oAuth setRequestTokenPath:REQUEST_TOKEN_PATH];
		[oAuth setAuthorizePath:AUTHORIZE_PATH];
		[oAuth setAccessTokenPath:ACCESS_TOKEN_PATH];
		if ([oAuth request_token])
		{
			[self setRequestToken:[oAuth requestToken]];
			[self setRequestTokenSecret:[oAuth requestTokenSecret]];
			authorize = [NSString stringWithFormat:
						 @"%@?oauth_token=%@&perms=delete",
						 [oAuth authorizePath], [oAuth requestToken]];
		}
	}
	@catch (NSException *exception)
	{
		EXPLOG(@"[%@ authorize] EXCEPTION\n%@: %@", [self className], [exception name], [exception reason]);
	}
	return authorize;
}

- (BOOL)access_token:(NSString *)pin
{
	LOG(@"[%@ access_token]", [self className]);
	BOOL access_token = NO;
	@try
	{
		OAuth *oAuth = [[OAuth alloc] init];
		[oAuth setConsumerKey:CONSUMER_KEY];
		[oAuth setConsumerSecret:CONSUMER_SECRET];
		[oAuth setRequestToken:_request_token];
		[oAuth setRequestTokenSecret:_request_token_secret];
		[oAuth setRequestTokenPath:REQUEST_TOKEN_PATH];
		[oAuth setAuthorizePath:AUTHORIZE_PATH];
		[oAuth setAccessTokenPath:ACCESS_TOKEN_PATH];
		if ([oAuth access_tokenWithPIN:pin])
		{
			[self setAccessToken:[oAuth accessToken]];
			[self setAccessTokenSecret:[oAuth accessTokenSecret]];
			access_token = YES;
		}
	}
	@catch (NSException *exception)
	{
		EXPLOG(@"[%@ access_token] EXCEPTION\n%@: %@", [self className], [exception name], [exception reason]);
	}
	return access_token;
}

- (BOOL)checkRetrieveTimeline:(NSDate *)now
{
	BOOL check = NO;
	@synchronized(self)
	{
		check = [self checkRetrieve:now
						   withDate:_dateTimeline
						   interval:[_preferences accountTumblrIntervalTimeline]];
	}
	return check;
}

- (BOOL)checkRetrieveReplies:(NSDate *)now
{
	return NO;
}

- (BOOL)checkRetrieveDM:(NSDate *)now
{
	return NO;
}

- (NSDictionary *)apiParam:(NSString *)api
{
	NSString *user = [_preferences accountTumblrUser];
	NSString *pass = [_preferences accountTumblrPass];
	return [NSDictionary dictionaryWithObjectsAndKeys:
			@"www.tumblr.com", TwitterHost,
			[NSString stringWithFormat:@"http://www.tumblr.com%@", api], TwitterURL,
			user, TwitterUser,
			pass, TwitterPass,
			Afficheur, TwitterSource,
			nil];
}

- (NSDictionary *)apiParam
{
	return [self apiParam:@""];
}

- (NSString *)determinePost:(id)object
					withAPI:(TumblrAPI *)api
{
	LOG2(@"[%@(%@) determinPost] %@", [self className], _service, object);
	id description = @"Post was failure.";
	@try
	{
		//LOG(@"[%@(%@) determinPost] %@", [self className], _service, [api className]);
		if ([object isKindOfClass:[NSError class]])
		{
			NSString *desc = [api errorLocalizedDescription:object];
			if (desc)
			{
				description = [NSString stringWithFormat:@"%@\n(%@)", description, desc];
			}
		}
		else
		{
			NSString *result = [NSString stringWithFormat:@"%@", object];
			if ([result lengthOfRegularExpression:@"^[0-9]+$" withOptions:OgreIgnoreCaseOption])
			{
				description = nil;
			}
			else
			{
				NSString *title = [api titleOfHTML:result];
				if (title)
				{
					description = [NSString stringWithFormat:@"%@\n(%@)", description, title];
				}
				else
				{
					description = [NSString stringWithFormat:@"%@\n(%@)", description, object];
				}
			}
		}
	}
	@catch (NSException *exception)
	{
		EXPLOG(@"[%@(%@) determinPost] EXCEPTION\n%@: %@", [self className], _service, [exception name], [exception reason]);
	}
	return description;
}

- (void)performPost:(NSArray *)array
{
	@try
	{
		NSString *status = [array objectAtIndex:0];
		if ([_controller isURL:status])
		{
			NSString *url = nil;
			NSString *title = nil;
			@synchronized([NSApplication sharedApplication])
			{
				OGRegularExpression *regex = [OGRegularExpression
											  regularExpressionWithString:@"((?:http|https)://\\S+)"
											  options:OgreIgnoreCaseOption | OgreMultilineOption];
				// マッチ結果の列挙子の生成
				OGRegularExpressionMatch *match = [regex matchInString:status];
				NSString *substr = [match matchedString];
				if (substr)
				{
					url = substr;
				}
				regex = [OGRegularExpression
						 regularExpressionWithString:@"^(.*)\\s+(?:http|https)://"
						 options:OgreIgnoreCaseOption | OgreMultilineOption];
				// マッチ結果の列挙子の生成
				match = [regex matchInString:status];
				title = [match substringAtIndex:1];
			}
			if (url && title)
			{
				TumblrAPI* api = [[[TumblrAPI alloc] initWithDictionary:[array objectAtIndex:1]] autorelease];
				if (api)
				{
					id result = [api createLink:url title:title];
					[self determinePost:[self determinePost:result withAPI:api]
							  withTitle:_title
							   function:@"Post"];
				}
			}
		}
	}
	@catch (NSException *exception)
	{
		EXPLOG(@"[%@ performPost] EXCEPTION\n%@: %@", [self className], [exception name], [exception reason]);
	}
	@finally
	{
		[array release];
	}
}

- (void)post:(NSString *)status
{
	//@synchronized(self)
	{
		@try
		{
			NSDictionary *dic = [self apiParam];
			[self performPost:[[NSArray alloc] initWithObjects:status, dic, nil]];
		}
		@catch (NSException *exception)
		{
			EXPLOG(@"[%@(%@) post] EXCEPTION\n%@: %@", [self className], _service, [exception name], [exception reason]);
		}
	}
}

#define RETWEETED	NO

- (NSDictionary *)parse:(NSDictionary *)obj
			   withUser:(NSString *)user_id
				   kind:(int)kind
				  first:(BOOL)first
{
	if (![obj isKindOfClass:[NSDictionary class]])
	{
		return nil;
	}
	NSDictionary *item = nil;
	@try
	{
		NSDictionary *org = obj;
		NSDictionary *retweet = [obj valueForKey:@"retweeted_status"];
		NSString *retweetedBy = nil;
		if (RETWEETED && retweet)
		{
			NSDictionary *byUser = [obj valueForKey:@"user"];
			retweetedBy = [byUser valueForKey:@"screen_name"];
			obj = retweet;
			LOG(@"[%@(%@) parse:%d]\n%@\nby %@", [self className], _service, kind, obj, retweetedBy);
		}
		NSString *item_id = [NSString stringWithFormat:@"%@", [obj valueForKey:@"id"]];
		//LOG(@"[%@(%@) parse:%d] %@", [self className], _service, kind, item_id);
		NSDictionary *user = [obj valueForKey:@"user"];
		if (!user)
		{
			user = [obj valueForKey:@"sender"];
		}
		int hasKeyword = -1;
		id protected = [user valueForKey:@"protected"];
		NSString *screen_name = [user valueForKey:@"screen_name"];
		NSString *user_name = [user valueForKey:@"name"];
		if (!user_name)
		{
			user_name = @"";
		}
		else if ([user_name isKindOfClass:[NSNull class]])
		{
			user_name = @"";
		}
		user_name = [self convertText:user_name];
		NSString *name = @"";
		if ((![user_name isEqualToString:@""]) &&(![user_name isEqualToString:screen_name]))
		{
			name = [NSString stringWithFormat:@" / %@", user_name];
		}
		NSString *user_profile = [user valueForKey:@"profile_image_url"];
		NSString *date = [obj valueForKey:@"created_at"];
		if (RETWEETED && retweet)
		{
			date = [org valueForKey:@"created_at"];
		}
		NSString *channel = @"";
		NSString *text = [obj valueForKey:@"text"];
		NSString *comment = @"";
		NSString *notify = _notify;
		NSString *kindStr = KindTimeline;
		NSString *category = KindTimeline;
		NSString *inReplyUser = nil;
		if (!text || [text isKindOfClass:[NSNull class]])
		{
			text = @"";
		}
		if (![screen_name isEqualToString:user_id])
		{
			hasKeyword = [self hasKeywords:
						  [NSArray arrayWithObjects:
						   text, screen_name, user_name, channel, nil]];
			if (hasKeyword == 0)
			{
				notify = GrowlAfficheurKeywordsNotify;
				first = NO;
			}
		}
		if (kind == kindReply)
		{
			notify = _notifyReply;
			kindStr = KindReply;
			category = KindReply;
		}
		else if (kind == kindDM)
		{
			notify = _notifyDM;
			kindStr = KindDM;
			category = KindDM;
		}
		id in_reply = [obj valueForKey:@"in_reply_to_status_id"];
		if (!in_reply || [in_reply isKindOfClass:[NSNull class]])
		{
			if ((kind == kindTimeline) || (kind == kindChannel))
			{
				if (![screen_name isEqualToString:user_id] &&
					([text lengthOfRegularExpression:[NSString stringWithFormat:@"@%@$", user_id]] > 0) ||
					([text lengthOfRegularExpression:[NSString stringWithFormat:@"@%@[^!-~]", user_id]] > 0))
				{
					//LOG(@"[%@(%@) parse:%d] Reply\n%@:%@", [self className], _service, kind, screen_name, text);
					notify = _notifyReply;
					category = KindReply;
					first = NO;
				}
			}
		}
		else
		{
			NSDictionary *reply = [self fetch:[NSString stringWithFormat:@"%@", in_reply]
								  withService:_service];
			if (reply)
			{
				inReplyUser = [reply valueForKey:KeyUser];
				comment = [NSString stringWithFormat:@"(on %@: %@)",
						   [reply valueForKey:KeyUser], [reply valueForKey:KeyText]];
				if ((kind == kindTimeline) || (kind == kindChannel))
				{
					if ([[reply valueForKey:KeyUser] isEqualToString:user_id])
					{
						//LOG(@"[%@(%@) parse:%d] Reply\n%@:%@", [self className], _service, kind, screen_name, text);
						notify = _notifyReply;
						category = KindReply;
						first = NO;
					}
				}
			}
			else if ((kind == kindTimeline) || (kind == kindChannel))
			{
				if (![screen_name isEqualToString:user_id] &&
					([text lengthOfRegularExpression:[NSString stringWithFormat:@"@%@$", user_id]] > 0) ||
					([text lengthOfRegularExpression:[NSString stringWithFormat:@"@%@[^!-~]", user_id]] > 0))
				{
					//LOG(@"[%@(%@) parse:%d] Reply\n%@:%@", [self className], _service, kind, screen_name, text);
					notify = _notifyReply;
					category = KindReply;
					first = NO;
				}
			}
		}
	//	if (kind == kindTimeline)
	//	{
	//		int i = 0;
	//		while (i < [_since count])
	//		{
	//			NSString *since = [_since objectAtIndex:i];
	//			if ([since compare:item_id] != NSOrderedDescending)
	//			{
	//				break;
	//			}
	//			i++;
	//		}
	//		[_since insertObject:item_id atIndex:i];
	//	}
		if (kind == kindDM)
		{
			item_id = [NSString stringWithFormat:@"DM%@", item_id];
			user_profile = [NSString stringWithFormat:@"%@ %@", user_profile, Mail];
		}
		NSString *rid = [obj valueForKey:@"rid"];
		item = [self createItem:item_id
					   withUser:screen_name
						   name:name
						channel:channel
							rid:rid
						   text:text
						comment:comment
						 source:[obj valueForKey:@"source"]
					 created_at:[NSDate dateWithNaturalLanguageString:date]
				   user_profile:user_profile
						 notify:notify
						   kind:kindStr
					   category:category
					inReplyUser:inReplyUser
					retweetedBy:retweetedBy
					retweetedId:nil
						keyword:hasKeyword
						protect:[protected intValue]
						  first:first];
	}
	@catch (NSException *exception)
	{
		EXPLOG(@"[%@(%@) parse:%d] EXCEPTION\n%@: %@", [self className], _service, kind, [exception name], [exception reason]);
	}
	return item;
}

- (void)retrieveTimeline
{
	//LOG(@"[%@(%@) retrieveTimeline]", [self className], _service);
	BOOL retrieve = NO;
	@synchronized(self)
	{
		if (_dateTimeline)
		{
			retrieve = YES;
			[self setupRetrieveDateTimeline:nil];
		}
	}
	if (retrieve)
	{
		@try
		{
			_xmppCount = 0;
			NSDictionary *dic = [[self apiParam] retain];
			[self performRetrieveHomeTimeline:dic];
		}
		@catch (NSException *exception)
		{
			EXPLOG(@"[%@(%@) retrieveTimeline] EXCEPTION\n%@: %@", [self className], _service, [exception name], [exception reason]);
		}
	}
}

- (void)favorite:(NSString *)item_id
{
	//LOG(@"[%@(%@) favorite]", [self className], _service);
	@synchronized(self)
	{
		@try
		{
			id item = [self fetch:item_id withService:_service];
			if (item && ![[item valueForKey:KeyKind] isEqualToString:KindDM])
			{
				NSDictionary *dic = [self apiParam];
				[self performFavorite:[[NSArray alloc] initWithObjects:item_id, dic, nil]];
			}
		}
		@catch (NSException *exception)
		{
			EXPLOG(@"[%@(%@) favorite] EXCEPTION\n%@: %@", [self className], _service, [exception name], [exception reason]);
		}
	}
}

- (void)doRetweetViaAPI:(NSString *)item_id
{
	LOG2(@"[%@(%@) doRetweetViaAPI]\n%@", [self className], _service, item_id);
	@synchronized(self)
	{
		@try
		{
			id item = [self fetch:item_id withService:_service];
			if (item && ![[item valueForKey:KeyKind] isEqualToString:KindDM])
			{
				NSDictionary *dic = [self apiParam];
				[self performRetweetViaAPI:[[NSArray alloc] initWithObjects:item_id, dic, nil]];
			}
		}
		@catch (NSException *exception)
		{
			EXPLOG(@"[%@(%@) doRetweetViaAPI] EXCEPTION\n%@: %@", [self className], _service, [exception name], [exception reason]);
		}
	}
}

- (id)getWithAuth:(NSString *)url
{
	LOG2(@"[%@(%@) getWithAuth]\n%@", [self className], _service, url);
	id result = nil;
	@synchronized(self)
	{
		@try
		{
			NSString *http = [url replaceWithExpression:@"(http://)(.*?)/(.*)$"
												replace:@"\\1"];
			NSString *host = [url replaceWithExpression:@"(http://)(.*?)/(.*)$"
												replace:@"\\2"];
			NSString *path = [url replaceWithExpression:@"(http://)(.*?)/(.*)$"
												replace:@"\\3"];
			LOG2(@"[%@(%@) getWithAuth]\n%@\n%@\n%@", [self className], _service, http, host, path);
			NSString *user = [_preferences accountTumblrUser];
			NSString *pass = [_preferences accountTumblrPass];
			TumblrAPI *api = [[[TumblrAPI alloc] initWithDictionary:
							   [NSDictionary dictionaryWithObjectsAndKeys:
								host, TwitterHost,
								[NSString stringWithFormat:@"%@%@", http, host], TwitterURL,
								user, TwitterUser,
								pass, TwitterPass,
								nil]] autorelease];
			NSMutableDictionary *header = [NSMutableDictionary dictionary];
			[api setRedirect:YES];
			[api setShouldHandleCookies:NO];
			result = [api getWithAuth:[NSString stringWithFormat:@"%@%@/%@", http, host, path]
							   header:header
								 body:@""];
		}
		@catch (NSException *exception)
		{
			EXPLOG(@"[%@(%@) getWithAuth] EXCEPTION\n%@: %@", [self className], _service, [exception name], [exception reason]);
		}
	}
	return result;
}

@end
