//
//  MGMGoogleContacts.m
//  VoiceBase
//
//  Created by Mr. Gecko on 8/17/10.
//  Copyright (c) 2011 Mr. Gecko's Media (James Coleman). http://mrgeckosmedia.com/
//
//  Permission to use, copy, modify, and/or distribute this software for any purpose
//  with or without fee is hereby granted, provided that the above copyright notice
//  and this permission notice appear in all copies.
//
//  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
//  REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
//  FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
//  OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
//  DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
//  ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//

#import "MGMGoogleContacts.h"
#import "MGMContactsProtocol.h"
#import "MGMInstance.h"
#import "MGMAddons.h"
#import "MGMXML.h"
#import <MGMUsers/MGMUsers.h>

NSString * const MGMGCAuthenticationURL = @"https://www.google.com/accounts/ClientLogin";
NSString * const MGMGCAuthenticationBody = @"Email=%@&Passwd=%@&source=MrGeckosMedia-VoiceBase-0.1&service=cp&accountType=HOSTED_OR_GOOGLE";
NSString * const MGMGCUseragent = @"VoiceBase/0.1";

NSString * const MGMGCContactsURL = @"https://www.google.com/m8/feeds/contacts/default/full?max-results=10000";
NSString * const MGMGCGroupsURL = @"https://www.google.com/m8/feeds/groups/default/full?max-results=10000";
NSString * const MGMGCAuthorization = @"Authorization";

const BOOL MGMGoogleContactsInvisible = YES;

@implementation MGMGoogleContacts
- (id)initWithDelegate:(id)theDelegate {
	if ((self = [super init])) {
		gettingContacts = NO;
		delegate = theDelegate;
		user = [[MGMUser userWithID:[[delegate user] settingForKey:MGMCGoogleContactsUser]] retain];
		if (user==nil)
			user = [[delegate user] retain];
		NSString *username = [user settingForKey:MGMUserName];
		if (![username containsString:@"@"])
			username = [username stringByAppendingString:@"@gmail.com"];
		NSURLCredential *credentials = [NSURLCredential credentialWithUser:username password:[user password] persistence:NSURLCredentialPersistenceForSession];
		connectionManager = [[MGMURLConnectionManager managerWithCookieStorage:[user cookieStorage]] retain];
		[connectionManager setCredentials:credentials];
		[connectionManager setUserAgent:MGMGCUseragent];
		isAuthenticating = YES;
		afterAuthentication = [NSMutableArray new];
		NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:MGMGCAuthenticationURL] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:15.0];
		[request setHTTPMethod:MGMPostMethod];
		[request setValue:MGMURLForm forHTTPHeaderField:MGMContentType];
		[request setHTTPBody:[[NSString stringWithFormat:MGMGCAuthenticationBody, [username addPercentEscapes], [[user password] addPercentEscapes]] dataUsingEncoding:NSUTF8StringEncoding]];
		MGMURLBasicHandler *handler = [MGMURLBasicHandler handlerWithRequest:request delegate:self];
		[handler setFailWithError:@selector(authentication:didFailWithError:)];
		[handler setFinish:@selector(authenticationDidFinish:)];
		[handler setInvisible:MGMGoogleContactsInvisible];
		[connectionManager addHandler:handler];
	}
	return self;
}
- (void)dealloc {
	[user release];
	[connectionManager cancelAll];
	[connectionManager release];
	[authenticationString release];
	[afterAuthentication release];
	[releaseTimer fire];
	[contactEntries release];
	[contactPhoto release];
	[super dealloc];
}
- (void)authentication:(MGMURLBasicHandler *)theHandler didFailWithError:(NSError *)theError {
	NSLog(@"MGMGoogleContacts Error: %@", theError);
}
+ (NSDictionary *)dictionaryWithString:(NSString *)theString {
	NSArray *values = [theString componentsSeparatedByString:@"\n"];
	NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
	for (int i=0; i<[values count]; i++) {
		if (![[values objectAtIndex:i] isEqual:@""]) {
			NSMutableArray *info = [NSMutableArray arrayWithArray:[[values objectAtIndex:i] componentsSeparatedByString:@"="]];
			NSString *key = [info objectAtIndex:0];
			[info removeObjectAtIndex:0];
			NSString *value = [info componentsJoinedByString:@"="];
			[dictionary setObject:value forKey:key];
		}
	}
	return dictionary;
}
- (void)authenticationDidFinish:(MGMURLBasicHandler *)theHandler {
	NSDictionary *info = [MGMGoogleContacts dictionaryWithString:[theHandler string]];
	[authenticationString release];
	authenticationString = [[NSString stringWithFormat:@"GoogleLogin auth=%@", [info objectForKey:@"Auth"]] retain];
	isAuthenticating = NO;
	while ([afterAuthentication count]!=0) {
		[[afterAuthentication objectAtIndex:0] invoke];
		[afterAuthentication removeObjectAtIndex:0];
	}
}
- (MGMUser *)user {
	return user;
}


- (void)stop {
	shouldStop = YES;
	[connectionManager cancelAll];
	if (gettingContacts || gettingGroups)
		[connectionManager cancelAll];
	shouldStop = NO;
}
- (void)getContacts:(id)sender {
	if (isAuthenticating) {
		SEL selector = @selector(getContacts:);
		NSMethodSignature *signature = [self methodSignatureForSelector:selector];
		if (signature!=nil) {
			NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
			[invocation setSelector:selector];
			[invocation setTarget:self];
			[invocation setArgument:&sender atIndex:2];
			[afterAuthentication addObject:invocation];
		}
		return;
	}
	if (gettingContacts) {
		NSDictionary *info = [NSDictionary dictionaryWithObject:contactsSender forKey:MGMCRecallSender];
		NSError *error = [NSError errorWithDomain:MGMCRecallError code:1 userInfo:info];
		if ([sender respondsToSelector:@selector(contactsError:)]) [sender contactsError:error];
		return;
	}
	gettingContacts = YES;
	contactsSender = sender;
	NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:MGMGCContactsURL] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:60.0];
	[request setValue:authenticationString forHTTPHeaderField:MGMGCAuthorization];
	MGMURLBasicHandler *handler = [MGMURLBasicHandler handlerWithRequest:request delegate:self];
	[handler setFailWithError:@selector(contacts:didFailWithError:)];
	[handler setFinish:@selector(contactsDidFinish:)];
	[handler setInvisible:MGMGoogleContactsInvisible];
	[connectionManager addHandler:handler];
}
- (void)contacts:(MGMURLBasicHandler *)theHandler didFailWithError:(NSError *)theError {
	gettingContacts = NO;
	NSLog(@"MGMGoogleContacts Error: %@", theError);
	if ([contactsSender respondsToSelector:@selector(contactsError:)]) [contactsSender contactsError:theError];
}
- (void)contactsDidFinish:(MGMURLBasicHandler *)theHandler {
	[releaseTimer invalidate];
	[releaseTimer release];
	releaseTimer = nil;
	[contacts release];
	contacts = [NSMutableArray new];
	MGMXMLElement *XML = [(MGMXMLDocument *)[[[MGMXMLDocument alloc] initWithData:[theHandler data] options:MGMXMLDocumentTidyXML error:nil] autorelease] rootElement];
	contactEntries = [[XML elementsForName:@"entry"] retain];
	contactsIndex=0;
	[self continueContacts];
}
- (void)photo:(MGMURLBasicHandler *)theHandler didFailWithError:(NSError *)theError {
	NSLog(@"MGMGoogleContacts Photo Error: %@", theError);
	[self parseContact];
	contactsIndex++;
	[self continueContacts];
}
- (void)photoDidFinish:(MGMURLBasicHandler *)theHandler {
	contactPhoto = [[theHandler data] retain];
	[self parseContact];
	contactsIndex++;
	[self continueContacts];
}
- (void)parseContact {
	if (shouldStop) return;
	MGMXMLElement *entry = [contactEntries objectAtIndex:contactsIndex];
	NSArray *titles = [entry elementsForName:@"title"];
	NSString *name = @"";
	if ([titles count]!=0)
		name = [[titles objectAtIndex:0] stringValue];
	NSArray *organizations = [entry elementsForName:@"gd:organization"];
	NSString *company = @"";
	if ([organizations count]!=0) {
		NSArray *organizationName = [[organizations objectAtIndex:0] elementsForName:@"gd:orgName"];
		if ([organizationName count]!=0)
			company = [[organizationName objectAtIndex:0] stringValue];
	}
	NSArray *phones = [entry elementsForName:@"gd:phoneNumber"];
	NSData *image = nil;
	if ([phones count]>0) {
		if (contactPhoto!=nil) {
			image = [contactPhoto resizeTo:MGMABPhotoSize];
			[contactPhoto release];
			contactPhoto = nil;
		}
	}
	for (int p=0; p<[phones count]; p++) {
		if (shouldStop) break;
		MGMXMLElement *phone = [phones objectAtIndex:p];
		NSMutableDictionary *contact = [NSMutableDictionary dictionary];
		[contact setObject:name forKey:MGMCName];
		[contact setObject:company forKey:MGMCCompany];
		if (delegate!=nil)
			[contact setObject:[[phone stringValue] phoneFormatWithAreaCode:[delegate areaCode]] forKey:MGMCNumber];
		else
			[contact setObject:[[phone stringValue] phoneFormat] forKey:MGMCNumber];
		NSString *label = @"";
		MGMXMLNode *labelXML = [phone attributeForName:@"label"];
		if (labelXML==nil) {
			MGMXMLNode *rel = [phone attributeForName:@"rel"];
			if (rel!=nil) {
				NSString *string = [rel stringValue];
				NSRange range = [string rangeOfString:@"#"];
				if (range.location!=NSNotFound) {
					label = [string substringFromIndex:range.location+range.length];
				}
			}
		} else {
			label = [labelXML stringValue];
		}
		[contact setObject:[label capitalizedString] forKey:MGMCLabel];
		[contacts addObject:[[contact copy] autorelease]];
		if (image!=nil)
			[contact setObject:image forKey:MGMCPhoto];
		if ([contactsSender respondsToSelector:@selector(gotContact:)]) [contactsSender gotContact:contact];
	}
}
- (void)continueContacts {
	for (; contactsIndex<[contactEntries count]; contactsIndex++) {
		if (shouldStop) break;
		MGMXMLElement *entry = [contactEntries objectAtIndex:contactsIndex];
		NSArray *phones = [entry elementsForName:@"gd:phoneNumber"];
		if ([phones count]!=0) {
			NSArray *links = [entry elementsForName:@"link"];
			BOOL loadingPhoto = NO;
			for (int i=0; i<[links count]; i++) {
				MGMXMLNode *rel = [(MGMXMLElement *)[links objectAtIndex:i] attributeForName:@"rel"];
				if (rel!=nil) {
					if ([[rel stringValue] containsString:@"#photo"]) {
						NSString *url = [[[links objectAtIndex:i] attributeForName:@"href"] stringValue];
						NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:60.0];
						[request setValue:authenticationString forHTTPHeaderField:MGMGCAuthorization];
						MGMURLBasicHandler *handler = [MGMURLBasicHandler handlerWithRequest:request delegate:self];
						[handler setFailWithError:@selector(photo:didFailWithError:)];
						[handler setFinish:@selector(photoDidFinish:)];
						[connectionManager addHandler:handler];
						loadingPhoto = YES;
						break;
					}
				}
			}
			if (!loadingPhoto)
				[self parseContact];
			else
				break;
		}
	}
	if (contactsIndex>=[contactEntries count]) {
		releaseTimer = [[NSTimer scheduledTimerWithTimeInterval:20.0 target:self selector:@selector(releaseContacts) userInfo:nil repeats:NO] retain];
		if ([contactsSender respondsToSelector:@selector(doneGettingContacts)]) [contactsSender doneGettingContacts];
		[contactEntries release];
		contactEntries = nil;
		contactsSender = nil;
		gettingContacts = NO;
	}
}
- (void)releaseContacts {
	[releaseTimer invalidate];
	[releaseTimer release];
	releaseTimer = nil;
	[contacts release];
	contacts = nil;
}

- (void)getGroups:(id)sender {
	if (isAuthenticating) {
		SEL selector = @selector(getGroups:);
		NSMethodSignature *signature = [self methodSignatureForSelector:selector];
		if (signature!=nil) {
			NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
			[invocation setSelector:selector];
			[invocation setTarget:self];
			[invocation setArgument:&sender atIndex:2];
			[afterAuthentication addObject:invocation];
		}
		return;
	}
	if (gettingGroups) {
		NSDictionary *info = [NSDictionary dictionaryWithObject:groupsSender forKey:MGMCRecallSender];
		NSError *error = [NSError errorWithDomain:MGMCRecallError code:1 userInfo:info];
		if ([sender respondsToSelector:@selector(groupsError:)]) [sender groupsError:error];
		return;
	}
	if ([sender respondsToSelector:@selector(groupsError:)]) [sender groupsError:nil];
	return;
	gettingGroups = YES;
	groupsSender = sender;
	NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:MGMGCGroupsURL]];
	[request setValue:authenticationString forHTTPHeaderField:MGMGCAuthorization];
	MGMURLBasicHandler *handler = [MGMURLBasicHandler handlerWithRequest:request delegate:self];
	[handler setFailWithError:@selector(groups:didFailWithError:)];
	[handler setFinish:@selector(groupsDidFinish:)];
	[handler setInvisible:MGMGoogleContactsInvisible];
	[connectionManager addHandler:handler];
}
- (void)groups:(MGMURLBasicHandler *)theHandler didFailWithError:(NSError *)theError {
	gettingGroups = NO;
	NSLog(@"MGMGoogleContacts Error: %@", theError);
}
- (void)groupsDidFinish:(MGMURLBasicHandler *)theHandler {
	MGMXMLElement *XML = [(MGMXMLDocument *)[[[MGMXMLDocument alloc] initWithData:[theHandler data] options:MGMXMLDocumentTidyXML error:nil] autorelease] rootElement];
	NSLog(@"%@", XML);
	gettingGroups = NO;
}
@end