//
//  TwitterAPI.m
//  Afficheur
//
//  Created by kichi on 08/04/05.
//  Copyright 2008 Katsuhiko Ichinose. All rights reserved.
//

#import "TwitterAPI.h"
#import "NSStringOgreKit.h"
#import <OgreKit/OgreKit.h>

NSString *TwitterUser	= @"TwitterUser";
NSString *TwitterPass	= @"TwitterPass";
NSString *TwitterHost	= @"TwitterHost";
NSString *TwitterURL	= @"TwitterURL";
NSString *TwitterSource	= @"TwitterSource";
NSString *TwitterUpdate	= @"TwitterUpdate";
NSString *TwitterKey	= @"TwitterKey";
NSString *TwitterSecret	= @"TwitterSecret";

@implementation TwitterAPI

- (id)init
{
	self = [super init];
	if (self)
	{
		_keys = [[NSDictionary dictionaryWithObjectsAndKeys:
				  @"", TwitterUser,
				  @"", TwitterPass,
				  @"", TwitterKey,
				  @"", TwitterSecret,
				  @"", TwitterHost,
				  @"", TwitterURL,
				  @"", TwitterSource,
				  @"", TwitterUpdate,
				  nil] retain];
		_dic = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
				@"status", TwitterUpdate,
				@"", TwitterUser,
				@"", TwitterPass,
				@"", TwitterKey,
				@"", TwitterSecret,
				@"twitter.com", TwitterHost,
				@"http://api.twitter.com/1", TwitterURL,
				nil];
	}
	return self;
}

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

- (id)initWithDictionary:(NSDictionary *)dic
{
	self = [self init];
	if (self)
	{
		NSEnumerator *enumerator = [dic keyEnumerator];
		id key;
		while ((key = [enumerator nextObject])) {
			id val = [_keys valueForKey:key];
			if (val == nil)
			{
				EXPLOG(@"[%@ initWithDictionary] NoKey: %@", [self className], key);
			}
			else
			{
				id value = [dic valueForKey:key];
				if (![value isKindOfClass:[NSNull class]])
				{
					[_dic setValue:value forKey:key];
					if ([key isEqualToString:TwitterKey])
					{
						[self setAccessToken:value];
					}
					else if ([key isEqualToString:TwitterSecret])
					{
						[self setAccessTokenSecret:value];
					}
				}
				else
				{
					[_dic removeObjectForKey:key];
					if ([key isEqualToString:TwitterKey])
					{
						[self setAccessToken:nil];
					}
					else if ([key isEqualToString:TwitterSecret])
					{
						[self setAccessTokenSecret:nil];
					}
				}
			}
		}
	}
	return self;
}

- (NSString *)user
{
	return [_dic valueForKey:TwitterUser];
}

- (id)postWithAuth:(NSString *)path
			header:(NSMutableDictionary *)header
			  body:(NSString *)body
{
	NSString* src = @"";
	if ([_dic valueForKey:TwitterSource])
	{
		src = [NSString stringWithFormat:@"&source=%@", [_dic valueForKey:TwitterSource]];
	}
	NSString *auth = [HTTP base64String:
					  [NSString stringWithFormat:@"%@:%@",
					   [_dic valueForKey:TwitterUser],
					   [_dic valueForKey:TwitterPass]]];
	[header setValue:[_dic valueForKey:TwitterHost] forKey:@"Host"];
	[header setValue:[NSString stringWithFormat:@"Basic %@", auth] forKey:@"Authorization"];
	return [self post:path
			   header:header
				 body:[NSString stringWithFormat:@"%@%@", body, src]];
}

- (id)getWithAuth:(NSString *)path
		   header:(NSMutableDictionary *)header
			 body:(NSString *)body
{
	NSString *auth = [HTTP base64String:
					  [NSString stringWithFormat:@"%@:%@",
					   [_dic valueForKey:TwitterUser],
					   [_dic valueForKey:TwitterPass]]];
	[header setValue:[_dic valueForKey:TwitterHost] forKey:@"Host"];
	[header setValue:[NSString stringWithFormat:@"Basic %@", auth] forKey:@"Authorization"];
	return [self get:path header:header body:body];
}

- (id)headWithAuth:(NSString *)path
			header:(NSMutableDictionary *)header
			  body:(NSString *)body
{
	NSString *auth = [HTTP base64String:
					  [NSString stringWithFormat:@"%@:%@",
					   [_dic valueForKey:TwitterUser],
					   [_dic valueForKey:TwitterPass]]];
	[header setValue:[_dic valueForKey:TwitterHost] forKey:@"Host"];
	[header setValue:[NSString stringWithFormat:@"Basic %@", auth] forKey:@"Authorization"];
	return [self head:path header:header body:body];
}

- (id)update:(NSString *)status
{
	return [self update:status withReply:@""];
}

- (id)update:(NSString *)status
   withReply:(NSString *)reply
{
	id result = nil;
	if (_access_token && _access_token_secret)
	{
		result = [self postWithOAuth:[NSString stringWithFormat:@"%@/statuses/update.json", [_dic valueForKey:TwitterURL]]
							  header:[NSMutableDictionary dictionaryWithObjectsAndKeys:
									  @"application/x-www-form-urlencoded", @"Content-Type",
									  nil]
								body:[NSString stringWithFormat:@"%@=%@%@", 
									  [_dic valueForKey:TwitterUpdate],
									  [HTTP urlEncode:status
											unescaped:@"-._~"
											  escaped:@" !@#$^&*()`+=[]{}\\|;:'\"<>?,/"/*@"&+=;?"*/],
									  reply]];
		LOG(@"[%@ update]\n%@", [self className], [[[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding] autorelease]);
	}
	else
	{
		result = [self postWithAuth:[NSString stringWithFormat:@"%@/statuses/update.json", [_dic valueForKey:TwitterURL]]
							 header:[NSMutableDictionary dictionaryWithObjectsAndKeys:
									 @"application/x-www-form-urlencoded",
									 @"Content-Type",
									 nil]
							   body:[NSString stringWithFormat:@"%@=%@%@", 
									 [_dic valueForKey:TwitterUpdate],
									 [HTTP urlEncode:status
										   unescaped:nil
											 escaped:@"&+=;"],
									 reply]];
	}
	return [self determineError:result WithJSON:YES];
}

- (id)directMessageNew:(NSString *)user
				  text:(NSString *)text
{
	id result = nil;
	if (_access_token && _access_token_secret)
	{
		result = [self postWithOAuth:[NSString stringWithFormat:@"%@/direct_messages/new.json", [_dic valueForKey:TwitterURL]]
							  header:[NSMutableDictionary dictionaryWithObjectsAndKeys:
									  @"application/x-www-form-urlencoded", @"Content-Type",
									  nil]
								body:[NSString stringWithFormat:@"user=%@&text=%@", 
									  user,
									  [HTTP urlEncode:text
											unescaped:@"-._~"
											  escaped:@" !@#$^&*()`+=[]{}\\|;:'\"<>?,/"/*@"&+=;?"*/]]];
		LOG(@"[%@ directMessageNew]\n%@", [self className], [[[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding] autorelease]);
	}
	else
	{
		result = [self postWithAuth:[NSString stringWithFormat:@"%@/direct_messages/new.json", [_dic valueForKey:TwitterURL]]
							 header:[NSMutableDictionary dictionaryWithObjectsAndKeys:
									 @"application/x-www-form-urlencoded",
									 @"Content-Type",
									 nil]
							   body:[NSString stringWithFormat:@"user=%@&text=%@", 
									 user,
									 [HTTP urlEncode:text
										   unescaped:nil
											 escaped:@"&+=;"]]];
	}
	return [self determineError:result WithJSON:YES];
}

#if 0
- (id)updateNowa:(NSString *)status
	   withReply:(NSString *)reply
{
	id result = [self postWithAuth:[NSString stringWithFormat:@"/statuses/update.json", [_dic valueForKey:TwitterURL]]
							header:[NSMutableDictionary dictionaryWithObjectsAndKeys:
									@"application/x-www-form-urlencoded",
									@"Content-Type",
									nil]
							  body:[NSString stringWithFormat:@"%@=%@%@", 
									[_dic valueForKey:TwitterUpdate],
									[HTTP urlEncode:status
										  unescaped:nil
											escaped:@"&+;"],
									reply]];
	if ([result isKindOfClass:[NSData class]])
	{
		NSString* data = [[[NSString alloc] initWithData:result
												encoding:NSUTF8StringEncoding] autorelease];
		//LOG(@"[%@ update]\n%@", [self className], result);
		//LOG(@"[%@ update]\n%@", [self className], data);
		unichar unicode = [data characterAtIndex:0];
		if (![[NSString stringWithCharacters:&unicode
									  length:1] isEqualToString:@"{"])
		{
			data = [data replaceWithExpression:@"\r\n"
									   replace:@"\n"];
			//options:OgreMultilineOption];
			data = [data replaceWithExpression:@"^.*?\n\n(.*)$"
									   replace:@"\\1"
									   options:OgreMultilineOption];
			const char* utf8 = [data UTF8String];
			result = [NSData dataWithBytes:utf8 length:strlen(utf8)];
			//LOG(@"[%@ update]\n%@", [self className], data);
			//LOG(@"[%@ update]\n%@", [self className], result);
		}
	}
	return [self determineError:result WithJSON:YES];
}
#endif

- (id)performRetrieve:(NSString *)path
			withSince:(NSString *)since
			   tweets:(int)tweets
{
	//LOG(@"[%@ performRetrieve]\n%@\n%@", [self className], path, _dic);
	NSMutableDictionary *header = [NSMutableDictionary dictionaryWithObjectsAndKeys:
								   //	@"text/javascript", //@"application/x-www-form-urlencoded",
								   //	@"Content-Type",
								   //	@"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; ja-JP-mac; rv:1.9.0.3) Gecko/2008092414 Firefox/3.0.3",
								   /*	@"",
									@"User-Agent"
									@"text/html,application/xhtml+xml,application/xml,text/javascript,application/json",
									@"Accept",
									@"ja,en-us",
									@"Accept-Language",
									@"deflate",
									@"Accept-Encoding",
									@"utf-8",
									@"Accept-Charset",*/
								   nil];
	if (tweets > 20)
	{
		NSString *join = @"?";
		if ([path lengthOfRegularExpression:@"\\?"] > 0)
		{
			join = @"&";
		}
		path = [NSString stringWithFormat:@"%@%@count=%d",
				path,
				join,
				tweets];
		LOG(@"[%@ performRetrieve]\n%@", [self className], path);
	}
	id result = nil;
	if (_access_token && _access_token_secret)
	{
		result = [self getWithOAuth:path header:header body:@""];
	}
	else
	{
		result = [self getWithAuth:path header:header body:@""];
	}
	//LOG(@"[%@ performRetrieve] %d\n%@\n%@\n%@", [self className], _statusCode, path, header, result);
	return [self determineError:result WithJSON:YES];
}

- (id)performRetrieve:(NSString *)path
			withSince:(NSString *)since
{
	return [self performRetrieve:path
					   withSince:since
						  tweets:0];
}

- (id)performRetrieveWithoutAuth:(NSString *)path
{
	//LOG(@"[%@ performRetrieve]\n%@\n%@", [self className], path, _dic);
	NSMutableDictionary *header = [NSMutableDictionary dictionaryWithObjectsAndKeys:
								   nil];
	id result = [self get:path header:header body:@""];
	//LOG(@"[%@ performRetrieve] %d\n%@\n%@\n%@", [self className], _statusCode, path, header, result);
	return [self determineError:result WithJSON:YES];
}

- (id)performHeadRetrieve:(NSString *)path
{
	//LOG(@"[%@ performRetrieve]\n%@\n%@", [self className], path, _dic);
	NSMutableDictionary *header = [NSMutableDictionary dictionaryWithObjectsAndKeys:
								   nil];
	id result = [self head:path header:header body:@""];
	//LOG(@"[%@ performHeadRetrieve] %d\n%@\n%@\n%@", [self className], _statusCode, path, header, result);
	return result; //[self determineError:result WithJSON:NO];
}

- (id)headHost
{
	//LOG(@"[%@ headHost]", [self className]);
	return [self performHeadRetrieve:[NSString stringWithFormat:@"http://%@/",
									  [_dic valueForKey:TwitterHost]]];
}

- (id)friendsTimeline
{
//	return [self friendsTimelineWithSince:nil];
	id result = [self performRetrieve:[NSString stringWithFormat:@"%@%@",
									   [_dic valueForKey:TwitterURL],
									   @"/statuses/friends_timeline.json"]
							withSince:nil
							   tweets:0];
	return result;
}

- (id)friendsTimelineWithSince:(NSString *)since
{
	//	return [self friendsTimelineWithSince:since
	//								   tweets:0];
	id result = [self performRetrieve:[NSString stringWithFormat:@"%@%@",
									   [_dic valueForKey:TwitterURL],
									   @"/statuses/friends_timeline.json"]
							withSince:since
							   tweets:0];
	return result;
}

- (id)friendsTimelineWithSince:(NSString *)since
						tweets:(int)tweets
{
	//LOG(@"[%@ friendsTimelineWithSince]", [self className]);
	id result = [self performRetrieve:[NSString stringWithFormat:@"%@%@",
									   [_dic valueForKey:TwitterURL],
									   @"/statuses/friends_timeline.json"]
							withSince:since
							   tweets:tweets];
	//LOG(@"[%@ friendsTimelineWithSince:tweets:]\n%@", [self className], [self headers]);
	return result;
}

- (id)homeTimeline
{
	//	return [self friendsTimelineWithSince:nil];
	id result = [self performRetrieve:[NSString stringWithFormat:@"%@%@",
									   [_dic valueForKey:TwitterURL],
									   @"/statuses/home_timeline.json"]
							withSince:nil
							   tweets:0];
	return result;
}

- (id)homeTimelineWithSince:(NSString *)since
{
	//	return [self friendsTimelineWithSince:since
	//								   tweets:0];
	id result = [self performRetrieve:[NSString stringWithFormat:@"%@%@",
									   [_dic valueForKey:TwitterURL],
									   @"/statuses/home_timeline.json"]
							withSince:since
							   tweets:0];
	return result;
}

- (id)homeTimelineWithSince:(NSString *)since
						tweets:(int)tweets
{
	//LOG(@"[%@ friendsTimelineWithSince]", [self className]);
	id result = [self performRetrieve:[NSString stringWithFormat:@"%@%@",
									   [_dic valueForKey:TwitterURL],
									   @"/statuses/home_timeline.json"]
							withSince:since
							   tweets:tweets];
	//LOG(@"[%@ friendsTimelineWithSince:tweets:]\n%@", [self className], [self headers]);
	return result;
}

- (id)replies
{
	return [self repliesWithSince:nil];
}

- (id)repliesWithSince:(NSString *)since;
{
	//LOG(@"[%@ repliesWithSince]", [self className]);
	return [self performRetrieve:[NSString stringWithFormat:@"%@%@",
								  [_dic valueForKey:TwitterURL],
								  @"/statuses/replies.json"]
					   withSince:since];
}

- (id)directMessage
{
	return [self directMessageWithSince:nil];
}

- (id)directMessageWithSince:(NSString *)since
{
	//LOG(@"[%@ directMessageWithSince]", [self className]);
	id result = [self performRetrieve:[NSString stringWithFormat:@"%@%@",
									   [_dic valueForKey:TwitterURL],
									   @"/direct_messages.json"]
							withSince:since];
	return result;
}

- (id)favorite:(NSString *)item_id
{
	id result = nil;
	if (_access_token && _access_token_secret)
	{
		result = [self postWithOAuth:[NSString stringWithFormat:@"%@/favorites/create/%@.json", [_dic valueForKey:TwitterURL], item_id]
							  header:[NSMutableDictionary dictionaryWithObjectsAndKeys:
									  @"application/x-www-form-urlencoded", @"Content-Type",
									  nil]
								body:@""];
	}
	else
	{
		result = [self postWithAuth:[NSString stringWithFormat:@"%@/favorites/create/%@.json", [_dic valueForKey:TwitterURL], item_id]
							 header:[NSMutableDictionary dictionaryWithObjectsAndKeys:
									 @"application/x-www-form-urlencoded", @"Content-Type",
									 nil]
							   body:@""];
	}
	return [self determineError:result WithJSON:YES];
}

- (id)retweet:(NSString *)item_id
{
	id result = nil;
	if (_access_token && _access_token_secret)
	{
		result = [self postWithOAuth:[NSString stringWithFormat:@"%@/statuses/retweet/%@.json", [_dic valueForKey:TwitterURL], item_id]
							  header:[NSMutableDictionary dictionaryWithObjectsAndKeys:
									  @"application/x-www-form-urlencoded", @"Content-Type",
									  nil]
								body:@""];
	}
	else
	{
		result = [self postWithAuth:[NSString stringWithFormat:@"%@/statuses/retweet/%@.json", [_dic valueForKey:TwitterURL], item_id]
							 header:[NSMutableDictionary dictionaryWithObjectsAndKeys:
									 @"application/x-www-form-urlencoded", @"Content-Type",
									 nil]
							   body:@""];
	}
	return [self determineError:result WithJSON:YES];
}

- (id)show:(NSString *)item_id
{
	//LOG(@"[%@ show]", [self className]);
	id result = [self performRetrieve:[NSString stringWithFormat:@"%@/statuses/show/%@.json", [_dic valueForKey:TwitterURL], item_id]
							withSince:nil];
	return result;
}

- (id)showWithoutAuth:(NSString *)item_id
{
	//LOG(@"[%@ showWithoutAuth]", [self className]);
	id result = [self performRetrieveWithoutAuth:[NSString stringWithFormat:@"%@/statuses/show/%@.json", [_dic valueForKey:TwitterURL], item_id]];
	return result;
}

- (id)rateLimit
{
	//LOG(@"[%@ rateLimit]", [self className]);
	id result = [self performRetrieve:[NSString stringWithFormat:@"%@%@", [_dic valueForKey:TwitterURL], @"/account/rate_limit_status.json"]
							withSince:nil];
//	id result = [self performRetrieve:[NSString stringWithFormat:@"http://api.twitter.com/1/account/rate_limit_status.json"]
//							withSince:nil];
	LOG(@"[%@ rateLimit]\n%@", [self className], [self headers]);
	LOG(@"[%@ rateLimit]\n%@", [self className], result);
	return result;
}


- (id)userStream:(id)delegate
{
	return [self getWithOAuth:[NSString stringWithFormat:@"https://userstream.twitter.com/2/user.json"]
					   header:[NSMutableDictionary dictionaryWithObjectsAndKeys:
							   @"application/x-www-form-urlencoded", @"Content-Type",
							   nil]
						 body:@""
					 delegate:delegate];
}

@end
