// Address Book Dump  
//
// Author:    Matthew Might
// Site:      http://matt.might.net/
// Original:  http://matt.might.net/articles/address-book-hacking/
//
// Dumps the OS X Address Book in S-Expression format.

// Usage:
// % adbump [group name]

// Compile with:
//  gcc -framework AddressBook -framework Foundation abdump.m -o abdump

#include <stdio.h>
#include <stdlib.h>

#import <Foundation/Foundation.h>
#import <AddressBook/AddressBook.h>

#import <objc/runtime.h>

// Note: Was used for debugging.
// Print the class name of an NS object:
const char* className(id object)  {
  return class_getName([object class]) ;
}

// Print out a dictionary object:
void printDictionary(id dict) {
  [dict enumerateKeysAndObjectsUsingBlock: ^(id key, id value, BOOL* stop) {
      printf("   (%-12s \"%s\")\n", [key UTF8String], [value UTF8String]) ;
   }];
}


int main (int argc, const char * argv[])
{
   NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];

   // Group name:
   NSString* groupName = @"Holiday Card List" ;

   // Check for a group name on the command line:
   if (argc >= 2) {
     groupName = [[NSString alloc] initWithUTF8String: argv[1]] ;
   }
  
   // Grab the address book object:
   ABAddressBook* AB = [ABAddressBook sharedAddressBook];   

   // Search for a group with a specific name:
   ABSearchElement* groupMatches = 
     [ABGroup searchElementForProperty: kABGroupNameProperty
              label: nil
              key: nil
              value: groupName  
              comparison: kABEqualCaseInsensitive];

   // Grab all groups with the name groupName:
   NSArray* groupsFound =
     [AB recordsMatchingSearchElement: groupMatches];

 
   // Iterate over all groups with groupName:
   [groupsFound enumerateObjectsUsingBlock:
      ^(id group, NSUInteger groupNum, BOOL* stop) {
  
         // Grab the people from this group:
         NSArray* peopleFound = [group members] ;
   
         // Iterate over all the people in this group:
         [peopleFound enumerateObjectsUsingBlock: 
           ^(id person, NSUInteger personNum, BOOL* stop) {

             int k; 

             // Grab names and titles:
             NSString* firstName = [person valueForProperty: kABFirstNameProperty] ;
             NSString* lastName = [person valueForProperty: kABLastNameProperty] ;
             NSString* title = [person valueForProperty: kABTitleProperty] ;
             NSString* organizationName = [person valueForProperty: kABOrganizationProperty] ;

             // Grab addresses and relatives:
             id addresses = [person valueForProperty: kABAddressProperty] ;
             id relatives = [person valueForProperty: kABRelatedNamesProperty] ;

             // Check if it's a person or a company:
             if (firstName) {
               printf("((first-name  \"%s\")\n", [firstName UTF8String]) ;
               printf(" (last-name   \"%s\")\n", [lastName UTF8String]) ;
             } else {
               // It's a company.
               printf("((first-name  \"%s\")\n", [organizationName UTF8String]) ;
               printf(" (last-name   \"\")\n") ;
             }

             // Print an id for this record:
             printf(" (uid         %lu)\n", personNum) ;


             if (title) 
               printf(" (title   \"%s\")\n", [title UTF8String]) ;


             // Print the home location if there is one,
             // but print the last location if there isn't:
             id lastLocation = NULL ;
             BOOL foundHome = FALSE ;

             for (k = 0; k < [addresses count]; ++k) {
               id location = [addresses valueAtIndex: k] ;
               NSString* label = [addresses labelAtIndex: k];

               lastLocation = location ;

               if (![label isEqualToString: kABHomeLabel])
                 continue ;

              foundHome = TRUE ;

               printf(" (address\n") ;
               printDictionary(location) ;
               printf(")\n") ;
             }

             if (!foundHome && lastLocation) {
               // No home found; print the last address:
               printf(" (address\n") ;
               printDictionary(lastLocation) ;
               printf(")\n") ;
             }

             // Print the spouse or partner, if any:
             for (k = 0; k < [relatives count]; ++k) {
               id relative = [relatives valueAtIndex: k] ;
               NSString* label = [relatives labelAtIndex: k] ;

               if ([label isEqualToString: kABSpouseLabel])
                 printf( "(spouse  \"%s\")\n", [relative UTF8String]) ;

               if ([label isEqualToString: kABPartnerLabel]) {
                 printf(" (partner \"%s\")\n", [relative UTF8String]) ;
               }
             }

             printf(")\n") ;
           }];
      }];


   [pool drain];
   return 0;
}
