/*
 * PhoneGap is available under *either* the terms of the modified BSD license *or* the
 * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
 * 
 * Copyright (c) 2005-2010, Nitobi Software Inc.
 */


#import "Contacts.h"
#import <UIKit/UIKit.h>
#import "PhoneGapDelegate.h"
#import "Categories.h"
#import "Notification.h"
#import "OCCFObject.h"
#import "OCABRecord.h"
#import "OCABMutableMultiValue.h"

@implementation ContactsPicker

@synthesize allowsEditing;
@synthesize jsCallback;

@end


@implementation Contacts

void addressBookChanged(ABAddressBookRef addressBook, CFDictionaryRef info, void* context)
{
	// note that this function is only called when another AddressBook instance modifies 
	// the address book, not the current one. For example, through an OTA MobileMe sync
	Contacts* contacts = (Contacts*)context;
	[contacts addressBookDirty];
}

-(PhoneGapCommand*) initWithWebView:(UIWebView*)theWebView
{
    self = (Contacts*)[super initWithWebView:(UIWebView*)theWebView];
    if (self) {
        addressBook = ABAddressBookCreate();
		allPeople = nil;
		ABAddressBookRegisterExternalChangeCallback(addressBook, addressBookChanged, self);
    }
	return self;
}

- (void) contactsCount:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
{
	NSUInteger argc = [arguments count];
	NSString* jsCallback = nil;
	
	if (argc > 0) {
		jsCallback = [arguments objectAtIndex:0];
	} else {
		NSLog(@"Contacts.contactsCount: Missing 1st parameter.");
		return;
	}
	
	CFIndex numberOfPeople = ABAddressBookGetPersonCount(addressBook);
	NSString* jsString = [[NSString alloc] initWithFormat:@"%@(%d);", jsCallback, numberOfPeople];
	
    [webView stringByEvaluatingJavaScriptFromString:jsString];
	[jsString release];
}

- (void) allContacts:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
{
	NSUInteger argc = [arguments count];
	NSString* jsCallback = nil;
	
	if (argc > 0) {
		jsCallback = [arguments objectAtIndex:0];
	} else {
		NSLog(@"Contacts.allContacts: Missing 1st parameter.");
		return;
	}
		
	NSMutableString* jsArray = [[NSMutableString alloc] init];
	[jsArray appendString:@"["];
	
	NSString* filter = [options valueForKey:@"nameFilter"];
	CFArrayRef records = NULL;
	CFIndex numberOfPeople = 0;

	if (filter) {
		records =  ABAddressBookCopyPeopleWithName(addressBook, (CFStringRef)filter);
		numberOfPeople = CFArrayGetCount(records);
	} else {
		if (allPeople == nil) { // lazy loading
			allPeople = (NSArray*)ABAddressBookCopyArrayOfAllPeople(addressBook);
		}
		records = (CFArrayRef)allPeople;
		numberOfPeople = ABAddressBookGetPersonCount(addressBook);
	}
	
	CFIndex pageSize = [options integerValueForKey:@"pageSize" defaultValue:numberOfPeople withRange:NSMakeRange(1, numberOfPeople)];
	
	NSUInteger maxPages = ceil((double)numberOfPeople / (double)pageSize);
	CFIndex pageNumber = [options integerValueForKey:@"pageNumber" defaultValue:1 withRange:NSMakeRange(1, maxPages)];
	
	CFIndex skipAmount = (pageNumber - 1) * pageSize;
	CFIndex maxIndex = (skipAmount + pageSize);
	CFIndex i;

	NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; 
	for (i = skipAmount; i < maxIndex; i++) 
	{ 
		OCABRecord* rec = [[OCABRecord alloc] initWithCFTypeRef:CFArrayGetValueAtIndex(records, i)];
		[jsArray appendString:[rec JSONValue]];
		[rec release];
		
		if (i+1 != maxIndex) {
			[jsArray appendString:@","];
		}
	}
	[pool release];
	[jsArray appendString:@"]"];
	
	NSString* jsString = [[NSString alloc] initWithFormat:@"%@(%@);", jsCallback, jsArray];
    NSLog(@"%@", jsString);
	
    [webView stringByEvaluatingJavaScriptFromString:jsString];
	
	if (filter) {
		CFRelease(records);
	}
	
	[jsArray release];
	[jsString release];
}

- (void) newContact:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
{	
	NSUInteger argc = [arguments count];
	NSString* firstName = nil, *lastName = nil, *phoneNumber = nil;
	
	if (argc > 0) firstName = [arguments objectAtIndex:0];
	if (argc > 1) lastName = [arguments objectAtIndex:1];
	if (argc > 2) phoneNumber = [arguments objectAtIndex:2];
	
	ABRecordRef ref = ABPersonCreate();
	OCABRecord* rec = [[OCABRecord alloc] initWithCFTypeRef:ref];
	CFRelease(ref);
	
	[rec setFirstName: firstName];
	[rec setLastName: lastName];
	
	if (phoneNumber) {
		[rec setPhoneNumber:phoneNumber withLabel:(NSString*)kABPersonPhoneMainLabel];
	}
	
	//TODO: add more items to set here, from arguments
	
	if ([options existsValue:@"true" forKey:@"gui"]) {
		ABNewPersonViewController* npController = [[[ABNewPersonViewController alloc] init] autorelease];
		
		npController.displayedPerson = [rec ABRecordRef];
		npController.addressBook = addressBook;
		npController.newPersonViewDelegate = self;

		UINavigationController *navController = [[[UINavigationController alloc] initWithRootViewController:npController] autorelease];
		[[super appViewController] presentModalViewController:navController animated: YES];
	} 
	else {
		CFErrorRef errorRef;
		bool addOk = false, saveOk = false;

		addOk = ABAddressBookAddRecord(addressBook, [rec ABRecordRef], &errorRef);
		if (addOk) {
			saveOk = ABAddressBookSave(addressBook, &errorRef);
		}
		if (saveOk) {
			[self addressBookDirty];
		}
		
		NSString* jsCallback = [options valueForKey:@"successCallback"];
		NSString* firstParam = (addOk && saveOk)? [rec JSONValue] : @"";
		
		if (jsCallback) {
			NSString* jsString = [[NSString alloc] initWithFormat:@"%@(%@);", jsCallback, firstParam];
			[webView stringByEvaluatingJavaScriptFromString:jsString];
			
			[jsString release];
		}
	}
	
	[rec release];
}

- (void) newPersonViewController:(ABNewPersonViewController*)newPersonViewController didCompleteWithNewPerson:(ABRecordRef)person
{
	if (person != NULL) {
		[self addressBookDirty];
	}
	[newPersonViewController dismissModalViewControllerAnimated:YES]; 
}

- (void) displayContact:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
{
	NSUInteger argc = [arguments count];
	ABRecordID recordID = kABRecordInvalidID;
	NSString* errorCallback = nil;
	
	//TODO: need better argument handling system
	if (argc > 0) {
		recordID = [[arguments objectAtIndex:0] intValue];
	} else {
		NSLog(@"Contacts.displayContact: Missing 1st parameter.");
		return;
	}
	
	if (argc > 1) {
		errorCallback = [arguments objectAtIndex:1];
	}
	
	bool allowsEditing = [options existsValue:@"true" forKey:@"allowsEditing"];
	
	OCABRecord* rec = [OCABRecord newFromRecordID:recordID andAddressBook:addressBook];
	if (rec) {
		ABPersonViewController* personController = [[[ABPersonViewController alloc] init] autorelease];
		personController.displayedPerson = [rec ABRecordRef];
		personController.personViewDelegate = self;
		personController.allowsEditing = allowsEditing;
		
		UIBarButtonItem* doneButton = [[UIBarButtonItem alloc]
										  initWithBarButtonSystemItem:UIBarButtonSystemItemDone
										  target: self
										  action: @selector(dismissModalView:)];
		
		UINavigationController *navController = [[[UINavigationController alloc] initWithRootViewController:personController] autorelease];
		[[super appViewController] presentModalViewController:navController animated: YES];

		// this needs to be AFTER presentModal, if not it does not show up (iOS 4 regression: workaround)
		personController.navigationItem.rightBarButtonItem = doneButton;
		
		[doneButton release];												
		[rec release];
	} 
	else 
	{
		if (errorCallback) {
			NSString* jsString = [[NSString alloc] initWithFormat:@"%@('Contacts.displayContact: Record id %d not found.');", 
								  errorCallback, recordID];
			NSLog(@"%@", jsString);
			
			[webView stringByEvaluatingJavaScriptFromString:jsString];
			[jsString release];
		}
	}
}

- (void) dismissModalView:(id)sender 
{
	UIViewController* controller = ([super appViewController]);
	[controller.modalViewController dismissModalViewControllerAnimated:YES]; 
}
								   
- (BOOL) personViewController:(ABPersonViewController *)personViewController shouldPerformDefaultActionForPerson:(ABRecordRef)person 
					 property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifierForValue
{
	return YES;
}

- (void) chooseContact:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
{
	NSUInteger argc = [arguments count];
	NSString* jsCallback = nil;
	
	//TODO: need better argument handling system
	if (argc > 0) {
		jsCallback = [arguments objectAtIndex:0];
	} else {
		NSLog(@"Contacts.chooseContact: Missing 1st parameter.");
		return;
	}
	
	ContactsPicker* pickerController = [[[ContactsPicker alloc] init] autorelease];
	pickerController.peoplePickerDelegate = self;
	pickerController.jsCallback = jsCallback;
	pickerController.allowsEditing = (BOOL)[options existsValue:@"true" forKey:@"allowsEditing"];
	
	[[super appViewController] presentModalViewController:pickerController animated: YES];
}

- (BOOL) peoplePickerNavigationController:(ABPeoplePickerNavigationController*)peoplePicker 
	     shouldContinueAfterSelectingPerson:(ABRecordRef)person
{
	ABPersonViewController* personController = [[[ABPersonViewController alloc] init] autorelease];
	ContactsPicker* picker = (ContactsPicker*)peoplePicker;
	
	personController.displayedPerson = person;
	personController.personViewDelegate = self;
	personController.allowsEditing = picker.allowsEditing;
	
	if (picker.allowsEditing) {
		[peoplePicker pushViewController:personController animated:YES];
	} else {
		OCABRecord* rec = [[OCABRecord alloc] initWithCFTypeRef:person];
		NSString* jsString = [[NSString alloc] initWithFormat:@"%@(%@);", picker.jsCallback, [rec JSONValue]];
		[webView stringByEvaluatingJavaScriptFromString:jsString];
		
		[jsString release];
		[rec release];
		[picker dismissModalViewControllerAnimated:YES];
	}
	return NO;
}

- (BOOL) peoplePickerNavigationController:(ABPeoplePickerNavigationController*)peoplePicker 
	     shouldContinueAfterSelectingPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier
{
	return YES;
}

- (void) peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker
{
	[peoplePicker dismissModalViewControllerAnimated:YES]; 
}

- (void) removeContact:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
{
	NSUInteger argc = [arguments count];
	ABRecordID recordID = kABRecordInvalidID;
	NSString* jsCallback = nil;
	
	if (argc > 0) {
		recordID = [[arguments objectAtIndex:0] intValue];
	} else {
		NSLog(@"Contacts.removeContact: Missing 1st parameter.");
		return;
	}
	
	if (argc > 1)
		jsCallback = [arguments objectAtIndex:1];
	
	OCABRecord* rec = [OCABRecord newFromRecordID:recordID andAddressBook:addressBook];
	NSString* firstParam = @"";
	bool removeOk = false, saveOk = false;
	CFErrorRef errorRef;
	
	if (rec) {
		removeOk = [rec removeFrom:addressBook];
		if (removeOk) {
			saveOk = ABAddressBookSave(addressBook, &errorRef);
		}
		if (saveOk) {
			[self addressBookDirty];
			firstParam = [rec JSONValue];
		}
		[rec release];
	}
	
	if (jsCallback) {
		NSString* jsString = [[NSString alloc] initWithFormat:@"%@(%@);", jsCallback, firstParam];
		[webView stringByEvaluatingJavaScriptFromString:jsString];
		[jsString release];
	}
}

- (void) addressBookDirty
{
	[allPeople release];
	allPeople = nil;
}

- (void)dealloc
{
	ABAddressBookUnregisterExternalChangeCallback(addressBook, addressBookChanged, self);

	if (addressBook) {
		CFRelease(addressBook);
	}
	
	[allPeople release];
    [super dealloc];
}

@end

