//
//  ICeCoffEEServices.m
//  ICeCoffEE APE
//
//  Created by Nicholas Riley on 5/10/05.
//  Copyright 2005 Nicholas Riley. All rights reserved.
//

#import "ICeCoffEEServices.h"
#import "ICeCoffEEShared.h"

// an approximate clone of HIToolbox's CreateServicesLocalizedDictKey
static CFStringRef preferredLocalization(CFDictionaryRef localizations) {
    if (localizations == NULL)
	return NULL;
    
    CFIndex localizationCount = CFDictionaryGetCount(localizations);
    if (localizationCount == 0)
	return NULL;
    
    const void **locales = malloc(localizationCount * sizeof(void *));
    if (locales == NULL)
	return NULL;
    
    CFDictionaryGetKeysAndValues(localizations, locales, NULL);
    CFArrayRef availableLocales = CFArrayCreate(NULL, locales, localizationCount, NULL);
    if (availableLocales == NULL)
	return NULL;
    
    // XXX need to obtain in-use app localization list, THEN canonicalize everything...
    // XXX THEN use CFBundleCopyLocalizationsForPreferences.  All to fix RealPlayer.  Aieee.
    CFArrayRef preferredLocales = CFBundleCopyPreferredLocalizationsFromArray(availableLocales);
    // NSLog(@"%@ => %@", availableLocales, preferredLocales);
    CFRelease(availableLocales);
    CFStringRef preferredLocalization;
    if (preferredLocales != NULL) {
	if (CFArrayGetCount(preferredLocales) > 0) {
	    CFStringRef preferredLocale = CFArrayGetValueAtIndex(preferredLocales, 0);
	    preferredLocalization = (CFStringRef)CFDictionaryGetValue(localizations, preferredLocale);
	}
	CFRelease(preferredLocales);
    }
    if (preferredLocalization == NULL) {
	preferredLocalization = (CFStringRef)CFDictionaryGetValue(localizations, CFSTR("default"));
	if (preferredLocalization == NULL)
	    preferredLocalization = (CFStringRef)CFDictionaryGetValue(localizations, (CFStringRef)locales[0]);
    }
    
    free(locales);
    // NSLog(@"%@", preferredLocalization);
    return preferredLocalization;
}

NSArray *CFServiceControllerCopyServicesEntries(void);

NSDictionary *ICCF_GetServicesInfo(void) {
    NSArray *services = CFServiceControllerCopyServicesEntries();
    
    NSEnumerator *e = [services objectEnumerator];
    NSDictionary *serviceEntry;
    NSMutableDictionary *serviceDict = [[NSMutableDictionary alloc] init];
    while ( (serviceEntry = (NSDictionary *)[e nextObject]) != nil) {
	// XXX once tested, redo all this with CF, and no autoreleasing
	// XXX items named the same as submenus (figure out how Cocoa itself does it, too)
	NSString *itemPath = (NSString *)preferredLocalization((CFDictionaryRef)[serviceEntry objectForKey: @"NSMenuItem"]);
	if (itemPath == nil) continue;
	
	NSString *bundlePath = [serviceEntry objectForKey: @"NSBundlePath"];
	BOOL bubbledUp = (bundlePath == nil);
	NSArray *itemComponents = [itemPath componentsSeparatedByString: @"/"];
	NSEnumerator *ce = [itemComponents objectEnumerator];
	NSString *itemComponent;
	NSMutableDictionary *levelDict = serviceDict;
	NSMutableDictionary *itemDict = nil;
	while ( (itemComponent = (NSString *)[ce nextObject]) != nil) {
	    // levelDict is nil if just created
	    if (levelDict != nil && !bubbledUp) {
		NSString *oldBundlePath = [itemDict objectForKey: (NSString *)kICServiceBundlePath];
		if ([oldBundlePath isEqualToString: bundlePath])
		    bubbledUp = YES;
		else if (oldBundlePath != nil) {
		    [oldBundlePath retain];
		    [itemDict removeObjectForKey: (NSString *)kICServiceBundlePath];
		    NSEnumerator *be = [levelDict objectEnumerator];
		    while ( (itemDict = (NSMutableDictionary *)[be nextObject]) != nil)
			[itemDict setObject: oldBundlePath forKey: (NSString *)kICServiceBundlePath];
		    [oldBundlePath release];
		}
	    }
	    if (levelDict == nil) {
		levelDict = [[NSMutableDictionary alloc] init];
		[itemDict setObject: levelDict forKey: (NSString *)kICServiceSubmenu];
		[levelDict release];
		itemDict = nil;
	    } else {
		itemDict = [levelDict objectForKey: itemComponent];
	    }
	    if (itemDict == nil) {
		itemDict = [[NSMutableDictionary alloc] init];
		if (!bubbledUp) {
		    [itemDict setObject: bundlePath forKey: (NSString *)kICServiceBundlePath];
		    bubbledUp = YES;
		}
		[levelDict setObject: itemDict forKey: itemComponent];
		levelDict = nil;
		[itemDict release];
	    } else {
		levelDict = [itemDict objectForKey: (NSString *)kICServiceSubmenu];
	    }
	}
	
	if (!bubbledUp)
	    [itemDict setObject: bundlePath forKey: (NSString *)kICServiceBundlePath];

	NSString *keyEquivalent = (NSString *)preferredLocalization((CFDictionaryRef)[serviceEntry objectForKey: @"NSKeyEquivalent"]);
	if (keyEquivalent == nil) continue;

	[itemDict setObject: keyEquivalent forKey: (NSString *)kICServiceShortcut];
    }
    [services release];
    
    return [serviceDict autorelease];
}


void ICCF_SetServicesMenu(NSMenu *servicesMenu) {
    // attaches a delegate (NSServiceMaster in 10.4, NSServiceMenuDelegate in 10.5)
    [[NSApplication sharedApplication] setServicesMenu: servicesMenu];
    id delegate;
    if ( (delegate = [servicesMenu delegate]) == nil)
	return;
    
    // populate menu so we have something to filter; delegate reponds to one of these methods
    if ([delegate respondsToSelector: @selector(menuNeedsUpdate:)]) {
	[delegate menuNeedsUpdate: servicesMenu];
    }
    if ([delegate respondsToSelector: @selector(menu:updateItem:atIndex:shouldCancel:)]) {
	int itemCount = [delegate numberOfItemsInMenu: servicesMenu];
	for (int i = 0 ; i < itemCount ; i++) {
	    [servicesMenu addItemWithTitle: @"" action: NULL keyEquivalent: @""];
	}
	for (int i = 0 ; i < itemCount ; i++) {
	    if (![delegate menu: servicesMenu updateItem: (NSMenuItem *)[servicesMenu itemAtIndex: i] atIndex: i shouldCancel: NO])
		break;
	}
    }
}

IconRef ICCF_CopyIconRefForPath(NSString *path) {
    IconRef icon;
    FSRef fsr;
    SInt16 label;
    OSStatus err = noErr;
    
    err = FSPathMakeRef((const UInt8 *)[path fileSystemRepresentation], &fsr, NULL);
    if (err != noErr) return NULL;
    
    err = GetIconRefFromFileInfo(&fsr, 0, NULL, kFSCatInfoNone, NULL, kIconServicesNormalUsageFlag, &icon, &label);
    if (err != noErr) return NULL;
    
    return icon;
}
