// note: this doesn't work for languages; we have to parse the ltag table

// fortunately features that aren't supported are simply ignored, so we can copy them all in
// LONGTERM FUTURE when we switch to 10.9, the language parameter won't be needed anymore
// LONGTERM FUTURE and on 10.10 we can use OpenType tags directly!
CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uint16_t *types, const uint16_t *selectors, size_t n, const char *language)
{
	CTFontDescriptorRef new;
	CFMutableArrayRef outerArray;
	CFDictionaryRef innerDict;
	CFNumberRef numType, numSelector;
	const void *keys[2], *values[2];
	size_t i;
	CFArrayRef languages;
	CFIndex il, nl;
	CFStringRef curlang;
	char d[2];

	outerArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
	if (outerArray == NULL) {
		// TODO
	}
	keys[0] = kCTFontFeatureTypeIdentifierKey;
	keys[1] = kCTFontFeatureSelectorIdentifierKey;
	for (i = 0; i < n; i++) {
		numType = CFNumberCreate(NULL, kCFNumberSInt16Type,
			(const SInt16 *) (types + i));
		numSelector = CFNumberCreate(NULL, kCFNumberSInt16Type,
			(const SInt16 *) (selectors + i));
		values[0] = numType;
		values[1] = numSelector;
		innerDict = CFDictionaryCreate(NULL,
			keys, values, 2,
			// TODO are these correct?
			&kCFCopyStringDictionaryKeyCallBacks,
			&kCFTypeDictionaryValueCallBacks);
		if (innerDict == NULL) {
			// TODO
		}
		CFArrayAppendValue(outerArray, innerDict);
		CFRelease(innerDict);
		CFRelease(numSelector);
		CFRelease(numType);
	}

	// now we have to take care of the language
	if (language != NULL) {
		languages = CTFontDescriptorCopyAttribute(desc, kCTFontLanguagesAttribute);
		if (languages != NULL) {
			nl = CFArrayGetCount(languages);
			d[0] = language[0];
			if (d[0] >= 'A' && d[0] <= 'Z')
				d[0] += 'a' - 'A';
			d[1] = language[1];
			if (d[1] >= 'A' && d[1] <= 'Z')
				d[1] += 'a' - 'A';
			for (il = 0; il < nl; il++) {
				char c[2];
		
				curlang = (CFStringRef) CFArrayGetValueAtIndex(languages, il);
				// TODO check for failure
				CFStringGetBytes(curlang, CFRangeMake(0, 2),
					kCFStringEncodingUTF8, 0, false,
					(UInt8 *) c, 2, NULL);
				if (c[0] >= 'A' && c[0] <= 'Z')
					c[0] += 'a' - 'A';
				if (c[1] >= 'A' && c[1] <= 'Z')
					c[1] += 'a' - 'A';
				if (c[0] == d[0] && c[1] == d[1])
					break;
			}
			if (il != nl) {
				uint16_t typ;
		
				typ = kLanguageTagType;
				il++;
				numType = CFNumberCreate(NULL, kCFNumberSInt16Type,
					(const SInt16 *) (&typ));
				numSelector = CFNumberCreate(NULL, kCFNumberCFIndexType,
					&il);
				values[0] = numType;
				values[1] = numSelector;
				innerDict = CFDictionaryCreate(NULL,
					keys, values, 2,
					// TODO are these correct?
					&kCFCopyStringDictionaryKeyCallBacks,
					&kCFTypeDictionaryValueCallBacks);
				if (innerDict == NULL) {
					// TODO
				}
				CFArrayAppendValue(outerArray, innerDict);
				CFRelease(innerDict);
				CFRelease(numSelector);
				CFRelease(numType);
			}
			CFRelease(languages);
		}
	}

	keys[0] = kCTFontFeatureSettingsAttribute;
	values[0] = outerArray;
	innerDict = CFDictionaryCreate(NULL,
		keys, values, 1,
		// TODO are these correct?
		&kCFCopyStringDictionaryKeyCallBacks,
		&kCFTypeDictionaryValueCallBacks);
	CFRelease(outerArray);
	new = CTFontDescriptorCreateCopyWithAttributes(desc, innerDict);
	CFRelease(desc);
	CFRelease(innerDict);
	return new;
}