/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_connectivity.hxx"

#include "MacabRecords.hxx"
#include "MacabRecord.hxx"
#include "MacabHeader.hxx"
#include "macabutilities.hxx"

#include <premac.h>
#include <Carbon/Carbon.h>
#include <AddressBook/ABAddressBookC.h>
#include <postmac.h>
#include <com/sun/star/util/DateTime.hpp>

using namespace connectivity::macab;
using namespace com::sun::star::util;

// -------------------------------------------------------------------------
MacabRecords::MacabRecords(const ABAddressBookRef _addressBook, MacabHeader *_header, MacabRecord **_records, sal_Int32 _numRecords)
{
	/* Variables passed in... */
	header = _header;
	recordsSize = _numRecords;
	currentRecord = _numRecords;
	records = _records;
	addressBook = _addressBook;

	/* Default variables... */
	recordType = kABPersonRecordType;

	/* Variables constructed... */
	bootstrap_CF_types();
	bootstrap_requiredProperties();
}

// -------------------------------------------------------------------------
/* Creates a MacabRecords from another: copies the length, name, and
 * address book of the original, but the header or the records themselves.
 * The idea is that the only reason to copy a MacabRecords is to create
 * a filtered version of it, which can have the same length (to avoid
 * resizing) and will work from the same base addressbook, but might have
 * entirey different values and even (possibly in the future) a different
 * header.
 */
MacabRecords::MacabRecords(const MacabRecords *_copy)
{
	/* Variables passed in... */
	recordsSize = _copy->recordsSize;
	addressBook = _copy->addressBook;
	m_sName = _copy->m_sName;

	/* Default variables... */
	currentRecord = 0;
	header = NULL;
	records = new MacabRecord *[recordsSize];
	recordType = kABPersonRecordType;

	/* Variables constructed... */
	bootstrap_CF_types();
	bootstrap_requiredProperties();
}

// -------------------------------------------------------------------------
MacabRecords::MacabRecords(const ABAddressBookRef _addressBook)
{
	/* Variables passed in... */
	addressBook = _addressBook;

	/* Default variables... */
	recordsSize = 0;
	currentRecord = 0;
	records = NULL;
	header = NULL;
	recordType = kABPersonRecordType;

	/* Variables constructed... */
	bootstrap_CF_types();
	bootstrap_requiredProperties();
}

// -------------------------------------------------------------------------
void MacabRecords::initialize()
{

	/* Make sure everything is NULL before initializing. (We usually just
	 * initialize after we use the constructor that takes only a
	 * MacabAddressBook, so these variables will most likely already be
	 * NULL.
	 */
	if(records != NULL)
	{
		sal_Int32 i;

		for(i = 0; i < recordsSize; i++)
			delete records[i];

		delete [] records;
	}

	if(header != NULL)
		delete header;

	/* We can handle both default record Address Book record types in
	 * this method, though only kABPersonRecordType is ever used.
	 */
	CFArrayRef allRecords;
	if(CFStringCompare(recordType, kABPersonRecordType, 0) == kCFCompareEqualTo)
		allRecords = ABCopyArrayOfAllPeople(addressBook);
	else
		allRecords = ABCopyArrayOfAllGroups(addressBook);

	ABRecordRef record;
	sal_Int32 i;
	recordsSize = (sal_Int32) CFArrayGetCount(allRecords);
	records = new MacabRecord *[recordsSize];

	/* First, we create the header... */
	header = createHeaderForRecordType(allRecords, recordType);

	/* Then, we create each of the records... */
	for(i = 0; i < recordsSize; i++)
	{
		record = (ABRecordRef) CFArrayGetValueAtIndex(allRecords, i);
		records[i] = createMacabRecord(record, header, recordType);
	}
	currentRecord = recordsSize;
	
	CFRelease(allRecords);
}

// -------------------------------------------------------------------------
MacabRecords::~MacabRecords()
{
}

// -------------------------------------------------------------------------
void MacabRecords::setHeader(MacabHeader *_header)
{
	if(header != NULL)
		delete header;
	header = _header;
}

// -------------------------------------------------------------------------
MacabHeader *MacabRecords::getHeader() const
{
	return header;
}

// -------------------------------------------------------------------------
/* Inserts a MacabRecord at a given location. If there is already a
 * MacabRecord at that location, return it.
 */
MacabRecord *MacabRecords::insertRecord(MacabRecord *_newRecord, const sal_Int32 _location)
{
	MacabRecord *oldRecord;

	/* If the location is greater than the current allocated size of this
	 * MacabRecords, allocate more space.
	 */
	if(_location >= recordsSize)
	{
		sal_Int32 i;
		MacabRecord **newRecordsArray = new MacabRecord *[_location+1];
		for(i = 0; i < recordsSize; i++)
		{
			newRecordsArray[i] = records[i];
		}
		delete [] records;
		records = newRecordsArray;
	}

	/* Remember: currentRecord refers to one above the highest existing
	 * record (i.e., it refers to where to place the next record if a
	 * location is not given).
	 */
	if(_location >= currentRecord)
		currentRecord = _location+1;
	
	oldRecord = records[_location];
	records[_location] = _newRecord;
	return oldRecord;
}

// -------------------------------------------------------------------------
/* Insert a record at the next available place. */
void MacabRecords::insertRecord(MacabRecord *_newRecord)
{
	insertRecord(_newRecord, currentRecord);
}

// -------------------------------------------------------------------------
MacabRecord *MacabRecords::getRecord(const sal_Int32 _location) const
{
	if(_location >= recordsSize)
		return NULL;
	return records[_location];
}

// -------------------------------------------------------------------------
macabfield *MacabRecords::getField(const sal_Int32 _recordNumber, const sal_Int32 _columnNumber) const
{
	if(_recordNumber >= recordsSize)
		return NULL;

	MacabRecord *record = records[_recordNumber];

	if(_columnNumber < 0 || _columnNumber >= record->getSize())
		return NULL;

	return record->get(_columnNumber);
}

// -------------------------------------------------------------------------
macabfield *MacabRecords::getField(const sal_Int32 _recordNumber, const ::rtl::OUString _columnName) const
{
	if(header != NULL)
	{
		sal_Int32 columnNumber = header->getColumnNumber(_columnName);
		if(columnNumber == -1)
			return NULL;

		return getField(_recordNumber, columnNumber);
	}
	else
	{
		// error: shouldn't access field with null header!
		return NULL;
	}
}

// -------------------------------------------------------------------------
sal_Int32 MacabRecords::getFieldNumber(const ::rtl::OUString _columnName) const
{
	if(header != NULL)
		return header->getColumnNumber(_columnName);
	else
		// error: shouldn't access field with null header!
		return -1;
}

// -------------------------------------------------------------------------
/* Create the lcl_CFTypes array -- we need this because there is no
 * way to get the ABType of an object from the object itself, and the
 * function ABTypeOfProperty can't handle multiple levels of data
 * (e.g., it can tell us that "address" is of type
 * kABDictionaryProperty, but it cannot tell us that all of the keys
 * and values in the dictionary have type kABStringProperty. On the
 * other hand, we _can_ get the CFType out of any object.
 * Unfortunately, all information about CFTypeIDs comes with the
 * warning that they change between releases, so we build them
 * ourselves here. (The one that we can't build is for multivalues,
 * e.g., kABMultiStringProperty. All of these appear to have the
 * same type: 1, but there is no function that I've found to give
 * us that dynamically in case that number ever changes.
 */
void MacabRecords::bootstrap_CF_types()
{
	lcl_CFTypesLength = 6;
	lcl_CFTypes = new lcl_CFType[lcl_CFTypesLength];

	lcl_CFTypes[0].cf = CFNumberGetTypeID();
	lcl_CFTypes[0].ab = kABIntegerProperty;

	lcl_CFTypes[1].cf = CFStringGetTypeID();
	lcl_CFTypes[1].ab = kABStringProperty;

	lcl_CFTypes[2].cf = CFDateGetTypeID();
	lcl_CFTypes[2].ab = kABDateProperty;

	lcl_CFTypes[3].cf = CFArrayGetTypeID();
	lcl_CFTypes[3].ab = kABArrayProperty;

	lcl_CFTypes[4].cf = CFDictionaryGetTypeID();
	lcl_CFTypes[4].ab = kABDictionaryProperty;

	lcl_CFTypes[5].cf = CFDataGetTypeID();
	lcl_CFTypes[5].ab = kABDataProperty;
}

// -------------------------------------------------------------------------
/* This is based on the possible fields required in the mail merge template
 * in sw. If the fields possible there change, it would be optimal to
 * change these fields as well.
 */
void MacabRecords::bootstrap_requiredProperties()
{
	numRequiredProperties = 7;
	requiredProperties = new CFStringRef[numRequiredProperties];
	requiredProperties[0] = kABTitleProperty;
	requiredProperties[1] = kABFirstNameProperty;
	requiredProperties[2] = kABLastNameProperty;
	requiredProperties[3] = kABOrganizationProperty;
	requiredProperties[4] = kABAddressProperty;
	requiredProperties[5] = kABPhoneProperty;
	requiredProperties[6] = kABEmailProperty;
}

// -------------------------------------------------------------------------
/* Create the header for a given record type and a given array of records.
 * Because the array of records and the record type are given, if you want
 * to, you can run this method on the members of a group, or on any other
 * filtered list of people and get a header relevant to them (e.g., if
 * they only have home addresses, the work address fields won't show up).
 */
MacabHeader *MacabRecords::createHeaderForRecordType(const CFArrayRef _records, const CFStringRef _recordType) const
{
	/* We have two types of properties for a given record type, nonrequired
	 * and required. Required properties are ones that will show up whether
	 * or not they are empty. Nonrequired properties will only show up if
	 * at least one record in the set has that property filled. The reason
	 * is that some properties, like the kABTitleProperty are required by
	 * the mail merge wizard (in module sw) but are by default not shown in
	 * the Mac OS X address book, so they would be weeded out at this stage
	 * and not shown if they were not required.
	 *
	 * Note: with the addition of required properties, I am not sure that
	 * this method still works for kABGroupRecordType (since the required
	 * properites are all for kABPersonRecordType).
	 *
	 * Note: required properties are constructed in the method
	 * bootstrap_requiredProperties() (above).
	 */
	CFArrayRef allProperties = ABCopyArrayOfPropertiesForRecordType(addressBook, _recordType);
	CFStringRef *nonRequiredProperties;
	sal_Int32 numRecords = (sal_Int32) CFArrayGetCount(_records);
	sal_Int32 numProperties = (sal_Int32) CFArrayGetCount(allProperties); 
	sal_Int32 numNonRequiredProperties = numProperties - numRequiredProperties;

	/* While searching through the properties for required properties, these
	 * sal_Bools will keep track of what we have found.
	 */
	sal_Bool bFoundProperty;
	sal_Bool bFoundRequiredProperties[numRequiredProperties];


	/* We have three MacabHeaders: headerDataForProperty is where we
	 * store the result of createHeaderForProperty(), which return a
	 * MacabHeader for a single property. lcl_header is where we store
	 * the MacabHeader that we are constructing. And, nonRequiredHeader
	 * is where we construct the MacabHeader for non-required properties,
	 * so that we can sort them before adding them to lcl_header.
	 */
	MacabHeader *headerDataForProperty;
	MacabHeader *lcl_header = new MacabHeader();
	MacabHeader *nonRequiredHeader = new MacabHeader();

	/* Other variables... */
	sal_Int32 i, j, k;
	ABRecordRef record;
	CFStringRef property;


	/* Allocate and initialize... */
	nonRequiredProperties = new CFStringRef[numNonRequiredProperties];
	k = 0;
	for(i = 0; i < numRequiredProperties; i++)
		bFoundRequiredProperties[i] = sal_False;

	/* Determine the non-required properties... */
	for(i = 0; i < numProperties; i++)
	{
		property = (CFStringRef) CFArrayGetValueAtIndex(allProperties, i);
		bFoundProperty = sal_False;
		for(j = 0; j < numRequiredProperties; j++)
		{
			if(CFEqual(property, requiredProperties[j]))
			{
				bFoundProperty = sal_True;
				bFoundRequiredProperties[j] = sal_True;
				break;
			}
		}

		if(bFoundProperty == sal_False)
		{
			/* If we have found too many non-required properties */
			if(k == numNonRequiredProperties)
			{
				k++; // so that the OSL_ENSURE below fails
				break;
			}
			nonRequiredProperties[k] = property;
			k++;
		}
	}

	// Somehow, we got too many or too few non-requird properties...
	// Most likely, one of the required properties no longer exists, which
	// we also test later.
	OSL_ENSURE(k == numNonRequiredProperties, "MacabRecords::createHeaderForRecordType: Found an unexpected number of non-required properties");

	/* Fill the header with required properties first... */
	for(i = 0; i < numRequiredProperties; i++)
	{
		if(bFoundRequiredProperties[i] == sal_True)
		{
			/* The order of these matters (we want all address properties
			 * before any phone properties, or else things will look weird),
			 * so we get all possibilitities for each property, going through
			 * each record, and then go onto the next property.
			 * (Note: the reason that we have to go through all records
			 * in the first place is that properties like address, phone, and
			 * e-mail are multi-value properties with an unknown number of
			 * values. A user could specify thirteen different kinds of
			 * e-mail addresses for one of her or his contacts, and we need to
			 * get all of them.
			 */
			for(j = 0; j < numRecords; j++)
			{
				record = (ABRecordRef) CFArrayGetValueAtIndex(_records, j);
				headerDataForProperty = createHeaderForProperty(record,requiredProperties[i],_recordType,sal_True);
				if(headerDataForProperty != NULL)
				{
					(*lcl_header) += headerDataForProperty;
					delete headerDataForProperty;
				}
			}
		}
		else
		{
			// Couldn't find a required property...
			OSL_ENSURE(false, ::rtl::OString("MacabRecords::createHeaderForRecordType: could not find required property: ") +
						::rtl::OUStringToOString(CFStringToOUString(requiredProperties[i]), RTL_TEXTENCODING_ASCII_US));
		}
	}

	/* And now, non-required properties... */
	for(i = 0; i < numRecords; i++)
	{
		record = (ABRecordRef) CFArrayGetValueAtIndex(_records, i);

		for(j = 0; j < numNonRequiredProperties; j++)
		{
			property = nonRequiredProperties[j];
			headerDataForProperty = createHeaderForProperty(record,property,_recordType,sal_False);
			if(headerDataForProperty != NULL)
			{
				(*nonRequiredHeader) += headerDataForProperty;
				delete headerDataForProperty;
			}
		}

	}
	nonRequiredHeader->sortRecord();

	(*lcl_header) += nonRequiredHeader;
	delete nonRequiredHeader;

	CFRelease(allProperties);
	delete [] nonRequiredProperties;

	return lcl_header;
}

// -------------------------------------------------------------------------
/* Create a header for a single property. Basically, this method gets
 * the property's value and type and then calls another method of
 * the same name to do the dirty work.
 */
MacabHeader *MacabRecords::createHeaderForProperty(const ABRecordRef _record, const CFStringRef _propertyName, const CFStringRef _recordType, const sal_Bool _isPropertyRequired) const
{
	// local variables
	CFStringRef localizedPropertyName;
	CFTypeRef propertyValue;
	ABPropertyType propertyType;
	MacabHeader *result;

	/* Get the property's value */
	propertyValue = ABRecordCopyValue(_record,_propertyName);
	if(propertyValue == NULL && _isPropertyRequired == sal_False)
		return NULL;

	propertyType = ABTypeOfProperty(addressBook, _recordType, _propertyName);
	localizedPropertyName = ABCopyLocalizedPropertyOrLabel(_propertyName);

	result = createHeaderForProperty(propertyType, propertyValue, localizedPropertyName);

	if(propertyValue != NULL)
		CFRelease(propertyValue);

	return result;
}

// -------------------------------------------------------------------------
/* Create a header for a single property. This method is recursive
 * because a single property might contain several sub-properties that
 * we also want to treat singly.
 */
MacabHeader *MacabRecords::createHeaderForProperty(const ABPropertyType _propertyType, const CFTypeRef _propertyValue, const CFStringRef _propertyName) const
{
	macabfield **headerNames = NULL;
	sal_Int32 length = 0;

	switch(_propertyType)
	{
		/* Scalars */
		case kABStringProperty:
		case kABRealProperty:
		case kABIntegerProperty:
		case kABDateProperty:
			length = 1;
			headerNames = new macabfield *[1];
			headerNames[0] = new macabfield;
			headerNames[0]->value = _propertyName;
			headerNames[0]->type = _propertyType;
			break;

		/* Multi-scalars */
		case kABMultiIntegerProperty:
		case kABMultiDateProperty:
		case kABMultiStringProperty:
		case kABMultiRealProperty:
		case kABMultiDataProperty:
			/* For non-scalars, we can only get more information if the property
			 * actually exists.
			 */
			if(_propertyValue != NULL)
			{
			sal_Int32 i;
			
			sal_Int32 multiLength = ABMultiValueCount((ABMutableMultiValueRef) _propertyValue);
			CFStringRef multiLabel, localizedMultiLabel;
			::rtl::OUString multiLabelString;
			::rtl::OUString multiPropertyString;
			::rtl::OUString headerNameString;
			ABPropertyType multiType = (ABPropertyType) (ABMultiValuePropertyType((ABMutableMultiValueRef) _propertyValue) - 0x100);

			length = multiLength;
			headerNames = new macabfield *[multiLength];
			multiPropertyString = CFStringToOUString(_propertyName);

			/* Go through each element, and - since each element is a scalar -
			 * just create a new macabfield for it.
			 */
			for(i = 0; i < multiLength; i++)
			{
				multiLabel = ABMultiValueCopyLabelAtIndex((ABMutableMultiValueRef) _propertyValue, i);
				localizedMultiLabel = ABCopyLocalizedPropertyOrLabel(multiLabel);
				multiLabelString = CFStringToOUString(localizedMultiLabel);
				CFRelease(multiLabel);
				CFRelease(localizedMultiLabel);
				headerNameString = multiPropertyString + ::rtl::OUString::createFromAscii(": ") + fixLabel(multiLabelString);
				headerNames[i] = new macabfield;
				headerNames[i]->value = OUStringToCFString(headerNameString);
				headerNames[i]->type = multiType;
			}
			}
			break;

		/* Multi-array or dictionary */
		case kABMultiArrayProperty:
		case kABMultiDictionaryProperty:
			/* For non-scalars, we can only get more information if the property
			 * actually exists.
			 */
			if(_propertyValue != NULL)
			{
				sal_Int32 i,j,k;

				// Total number of multi-array or multi-dictionary elements.
				sal_Int32 multiLengthFirstLevel = ABMultiValueCount((ABMutableMultiValueRef) _propertyValue);

				/* Total length, including the length of each element (e.g., if
				 * this multi-dictionary contains three dictionaries, and each
				 * dictionary has four elements, this variable will be twelve,
				 * whereas multiLengthFirstLevel will be three.
				 */
				sal_Int32 multiLengthSecondLevel = 0;

				CFStringRef multiLabel, localizedMultiLabel;
				CFTypeRef multiValue;
				::rtl::OUString multiLabelString;
				::rtl::OUString multiPropertyString;
				MacabHeader **multiHeaders = new MacabHeader *[multiLengthFirstLevel];
				ABPropertyType multiType = (ABPropertyType) (ABMultiValuePropertyType((ABMutableMultiValueRef) _propertyValue) - 0x100);

				multiPropertyString = CFStringToOUString(_propertyName);

				/* Go through each element - since each element can really
				 * contain anything, we run this method again on each element
				 * and store the resulting MacabHeader (in the multiHeaders
				 * array). Then, all we'll have to do is combine the MacabHeaders
				 * into a single one.
				 */
				for(i = 0; i < multiLengthFirstLevel; i++)
				{
					/* label */
					multiLabel = ABMultiValueCopyLabelAtIndex((ABMutableMultiValueRef) _propertyValue, i);
					multiValue = ABMultiValueCopyValueAtIndex((ABMutableMultiValueRef) _propertyValue, i);
					if(multiValue && multiLabel)
					{
						localizedMultiLabel = ABCopyLocalizedPropertyOrLabel(multiLabel);
						multiLabelString = multiPropertyString + ::rtl::OUString::createFromAscii(": ") + fixLabel(CFStringToOUString(localizedMultiLabel));
						CFRelease(multiLabel);
						CFRelease(localizedMultiLabel);
						multiLabel = OUStringToCFString(multiLabelString);
						multiHeaders[i] = createHeaderForProperty(multiType, multiValue, multiLabel);
						if (!multiHeaders[i])
							multiHeaders[i] = new MacabHeader();
						multiLengthSecondLevel += multiHeaders[i]->getSize();
					}
					else
					{
						multiHeaders[i] = new MacabHeader();
					}
					if(multiValue)
						CFRelease(multiValue);
				}

				/* We now have enough information to create our final MacabHeader.
				 * We go through each field of each header and add it to the
				 * headerNames array (which is what is used below to construct
				 * the MacabHeader we return).
				 */
				length = multiLengthSecondLevel;
				headerNames = new macabfield *[multiLengthSecondLevel];

				for(i = 0, j = 0, k = 0; i < multiLengthSecondLevel; i++,k++)
				{
					while(multiHeaders[j]->getSize() == k)
					{
						j++;
						k = 0;
					}
					
					headerNames[i] = multiHeaders[j]->copy(k);
				}
				for(i = 0; i < multiLengthFirstLevel; i++)
					delete multiHeaders[i];

				delete [] multiHeaders;
			}
			break;
			
		/* Dictionary */
		case kABDictionaryProperty:
			/* For non-scalars, we can only get more information if the property
			 * actually exists.
			 */
			if(_propertyValue != NULL)
			{
			/* Assume all keys are strings */
			sal_Int32 numRecords = (sal_Int32) CFDictionaryGetCount((CFDictionaryRef) _propertyValue);

			/* The only method for getting info out of a CFDictionary, of both
			 * keys and values, is to all of them all at once, so these
			 * variables will hold them.
			 */
			CFStringRef *dictKeys;
			CFTypeRef *dictValues;

			sal_Int32 i,j,k;
			::rtl::OUString dictKeyString, propertyNameString;
			ABPropertyType dictType;
			MacabHeader **dictHeaders = new MacabHeader *[numRecords];
			::rtl::OUString dictLabelString;
			CFStringRef dictLabel, localizedDictKey;

			/* Get the keys and values */
			dictKeys = (CFStringRef *) malloc(sizeof(CFStringRef)*numRecords);
			dictValues = (CFTypeRef *) malloc(sizeof(CFTypeRef)*numRecords);
			CFDictionaryGetKeysAndValues((CFDictionaryRef) _propertyValue, (const void **) dictKeys, (const void **) dictValues);

			propertyNameString = CFStringToOUString(_propertyName);

			length = 0;
			/* Go through each element - assuming that the key is a string but
			 * that the value could be anything. Since the value could be
			 * anything, we can't assume that it is scalar (it could even be
			 * another dictionary), so we attempt to get its type using
			 * the method getABTypeFromCFType and then run this method
			 * recursively on that element, storing the MacabHeader that
			 * results. Then, we just combine all of the MacabHeaders into
			 * one.
			 */
			for(i = 0; i < numRecords; i++)
			{
				dictType = (ABPropertyType) getABTypeFromCFType( CFGetTypeID(dictValues[i]) );
				localizedDictKey = ABCopyLocalizedPropertyOrLabel(dictKeys[i]);
				dictKeyString = CFStringToOUString(localizedDictKey);
				dictLabelString = propertyNameString + ::rtl::OUString::createFromAscii(": ") + fixLabel(dictKeyString);
				dictLabel = OUStringToCFString(dictLabelString);
				dictHeaders[i] = createHeaderForProperty(dictType, dictValues[i], dictLabel);
				if (!dictHeaders[i])
					dictHeaders[i] = new MacabHeader();
				length += dictHeaders[i]->getSize();
				CFRelease(dictLabel);
				CFRelease(localizedDictKey);
			}

			/* Combine all of the macabfields in each MacabHeader into the
			 * headerNames array, which (at the end of this method) is used
			 * to create the MacabHeader that is returned.
			 */
			headerNames = new macabfield *[length];
			for(i = 0, j = 0, k = 0; i < length; i++,k++)
			{
				while(dictHeaders[j]->getSize() == k)
				{
					j++;
					k = 0;
				}
				
				headerNames[i] = dictHeaders[j]->copy(k);
			}

			for(i = 0; i < numRecords; i++)
				delete dictHeaders[i];

			delete [] dictHeaders;
			free(dictKeys);
			free(dictValues);
			}
			break;

		/* Array */
		case kABArrayProperty:
			/* For non-scalars, we can only get more information if the property
			 * actually exists.
			 */
			if(_propertyValue != NULL)
			{
				sal_Int32 arrLength = (sal_Int32) CFArrayGetCount( (CFArrayRef) _propertyValue);
				sal_Int32 i,j,k;
				CFTypeRef arrValue;
				ABPropertyType arrType;
				MacabHeader **arrHeaders = new MacabHeader *[arrLength];
				::rtl::OUString propertyNameString = CFStringToOUString(_propertyName);
				::rtl::OUString arrLabelString;
				CFStringRef arrLabel;

				length = 0;
				/* Go through each element - since the elements here do not have
				 * unique keys like the ones in dictionaries, we create a unique
				 * key out of the id of the element in the array (the first
				 * element gets a 0 plopped onto the end of it, the second a 1...
				 * As with dictionaries, the elements could be anything, including
				 * another array, so we have to run this method recursively on
				 * each element, storing the resulting MacabHeader into an array,
				 * which we then combine into one MacabHeader that is returned.
				 */
				for(i = 0; i < arrLength; i++)
				{
					arrValue = (CFTypeRef) CFArrayGetValueAtIndex( (CFArrayRef) _propertyValue, i);
					arrType = (ABPropertyType) getABTypeFromCFType( CFGetTypeID(arrValue) );
					arrLabelString = propertyNameString + ::rtl::OUString::valueOf(i);
					arrLabel = OUStringToCFString(arrLabelString);
					arrHeaders[i] = createHeaderForProperty(arrType, arrValue, arrLabel);
					if (!arrHeaders[i])
						arrHeaders[i] = new MacabHeader();
					length += arrHeaders[i]->getSize();
					CFRelease(arrLabel);
				}

				headerNames = new macabfield *[length];
				for(i = 0, j = 0, k = 0; i < length; i++,k++)
				{
					while(arrHeaders[j]->getSize() == k)
					{
						j++;
						k = 0;
					}
					
					headerNames[i] = arrHeaders[j]->copy(k);
				}
				for(i = 0; i < arrLength; i++)
					delete arrHeaders[i];

				delete [] arrHeaders;
			}
			break;

			default:
				break;

	}

	/* If we succeeded at adding elements to the headerNames array, then
	 * length will no longer be 0. If it is, create a new MacabHeader
	 * out of the headerNames (after weeding out duplicate headers), and
	 * then return the result. If the length is still 0, return NULL: we
	 * failed to create a MacabHeader out of this property.
	 */
	if(length != 0)
	{
		manageDuplicateHeaders(headerNames, length);
		MacabHeader *headerResult = new MacabHeader(length, headerNames);
		delete [] headerNames;
		return headerResult;
	}
	else
		return NULL;
}

// -------------------------------------------------------------------------
void MacabRecords::manageDuplicateHeaders(macabfield **_headerNames, const sal_Int32 _length) const
{
	/* If we have two cases of, say, phone: home, this makes it:
	 * phone: home (1)
	 * phone: home (2)
	 */
	sal_Int32 i, j;
	sal_Int32 count;
	for(i = _length-1; i >= 0; i--)
	{
		count = 1;
		for( j = i-1; j >= 0; j--)
		{
			if(CFEqual(_headerNames[i]->value, _headerNames[j]->value))
			{
				count++;
			}
		}

		// duplicate!
		if(count != 1)
		{
			// There is probably a better way to do this...
			::rtl::OUString newName = CFStringToOUString((CFStringRef) _headerNames[i]->value);
			CFRelease(_headerNames[i]->value);
			newName += ::rtl::OUString::createFromAscii(" (") + ::rtl::OUString::valueOf(count) + ::rtl::OUString::createFromAscii(")");
			_headerNames[i]->value = OUStringToCFString(newName);
		}
	}
}

// -------------------------------------------------------------------------
/* Create a MacabRecord out of an ABRecord, using a given MacabHeader and
 * the record's type. We go through each property for this record type
 * then process it much like we processed the header (above), with two
 * exceptions: if we come upon something not in the header, we ignore it
 * (it's something we don't want to add), and once we find a corresponding
 * location in the header, we store the property and the property type in
 * a macabfield. (For the header, we stored the property type and the name
 * of the property as a CFString.)
 */
MacabRecord *MacabRecords::createMacabRecord(const ABRecordRef _abrecord, const MacabHeader *_header, const CFStringRef _recordType) const
{
	/* The new record that we will create... */
	MacabRecord *macabRecord = new MacabRecord(_header->getSize());

	CFArrayRef recordProperties = ABCopyArrayOfPropertiesForRecordType(addressBook, _recordType);
	sal_Int32 numProperties = (sal_Int32) CFArrayGetCount(recordProperties); 

	sal_Int32 i;

	CFTypeRef propertyValue;
	ABPropertyType propertyType;

	CFStringRef propertyName, localizedPropertyName;
	::rtl::OUString propertyNameString;
	for(i = 0; i < numProperties; i++)
	{
		propertyName = (CFStringRef) CFArrayGetValueAtIndex(recordProperties, i);
		localizedPropertyName = ABCopyLocalizedPropertyOrLabel(propertyName);
		propertyNameString = CFStringToOUString(localizedPropertyName);
		CFRelease(localizedPropertyName);

		/* Get the property's value */
		propertyValue = ABRecordCopyValue(_abrecord,propertyName);
		if(propertyValue != NULL)
		{
			propertyType = ABTypeOfProperty(addressBook, _recordType, propertyName);
			if(propertyType != kABErrorInProperty)
				insertPropertyIntoMacabRecord(propertyType, macabRecord, _header, propertyNameString, propertyValue);

			CFRelease(propertyValue);
		}
	}
	CFRelease(recordProperties);
	return macabRecord;
}

// -------------------------------------------------------------------------
/* Inserts a given property into a MacabRecord. This method calls another
 * method by the same name after getting the property type (it only
 * receives the property value). It is called when we aren't given the
 * property's type already.
 */
void MacabRecords::insertPropertyIntoMacabRecord(MacabRecord *_abrecord, const MacabHeader *_header, const ::rtl::OUString _propertyName, const CFTypeRef _propertyValue) const
{
	CFTypeID cf_type = CFGetTypeID(_propertyValue);
	ABPropertyType ab_type = getABTypeFromCFType( cf_type );

	if(ab_type != kABErrorInProperty)
		insertPropertyIntoMacabRecord(ab_type, _abrecord, _header, _propertyName, _propertyValue);
}

// -------------------------------------------------------------------------
/* Inserts a given property into a MacabRecord. This method is recursive
 * because properties can contain many sub-properties.
 */
void MacabRecords::insertPropertyIntoMacabRecord(const ABPropertyType _propertyType, MacabRecord *_abrecord, const MacabHeader *_header, const ::rtl::OUString _propertyName, const CFTypeRef _propertyValue) const
{
	/* If there is no value, return */
	if(_propertyValue == NULL)
		return;

	/* The main switch statement */
	switch(_propertyType)
	{
		/* Scalars */
		case kABStringProperty:
		case kABRealProperty:
		case kABIntegerProperty:
		case kABDateProperty:
		{
			/* Only scalars actually insert a property into the MacabRecord.
			 * In all other cases, this method is called recursively until a
			 * scalar type, an error, or an unknown type are found.
			 * Because of that, the following checks only occur for this type.
			 * We store whether we have successfully placed this property
			 * into the MacabRecord (or whether an unrecoverable error occured).
			 * Then, we try over and over again to place the property into the
			 * record. There are three possible results:
			 * 1) Success!
			 * 2) There is already a property stored at the column of this name,
			 * in which case we have a duplicate header (see the method
			 * manageDuplicateHeaders()). If that is the case, we add an ID
			 * to the end of the column name in the same format as we do in
			 * manageDuplicateHeaders() and try again.
			 * 3) No column of this name exists in the header. In this case,
			 * there is nothing we can do: we have failed to place this
			 * property into the record.
			 */
			sal_Bool bPlaced = sal_False;
			::rtl::OUString columnName = ::rtl::OUString(_propertyName);
			sal_Int32 i = 1;
			
			// A big safeguard to prevent two fields from having the same name.
			while(bPlaced != sal_True)
			{
				sal_Int32 columnNumber = _header->getColumnNumber(columnName);
				bPlaced = sal_True;
				if(columnNumber != -1)
				{
					// collision! A property already exists here!
					if(_abrecord->get(columnNumber) != NULL)
					{
						bPlaced = sal_False;
						i++;
						columnName = ::rtl::OUString(_propertyName) + ::rtl::OUString::createFromAscii(" (") + ::rtl::OUString::valueOf(i) + ::rtl::OUString::createFromAscii(")");
					}

					// success!
					else
					{
						_abrecord->insertAtColumn(_propertyValue, _propertyType, columnNumber);
					}
				}
			}
		}
		break;

		/* Array */
		case kABArrayProperty:
			{
				/* An array is basically just a list of anything, so all we do
				 * is go through the array, and rerun this method recursively
				 * on each element.
				 */
				sal_Int32 arrLength = (sal_Int32) CFArrayGetCount( (CFArrayRef) _propertyValue);
				sal_Int32 i;
				const void *arrValue;
				::rtl::OUString newPropertyName;

				/* Going through each element... */
				for(i = 0; i < arrLength; i++)
				{
					arrValue = CFArrayGetValueAtIndex( (CFArrayRef) _propertyValue, i);
					newPropertyName = _propertyName + ::rtl::OUString::valueOf(i);
					insertPropertyIntoMacabRecord(_abrecord, _header, newPropertyName, arrValue);
					CFRelease(arrValue);
				}

			}
			break;

		/* Dictionary */
		case kABDictionaryProperty:
			{
				/* A dictionary is basically a hashmap. Technically, it can
				 * hold any object as a key and any object as a value.
				 * For our case, we assume that the key is a string (so that
				 * we can use the key to get the column name and match it against
				 * the header), but we don't assume anything about the value, so
				 * we run this method recursively (or, rather, we run the version
				 * of this method for when we don't know the object's type) until
				 * we hit a scalar value.
				 */

				sal_Int32 numRecords = (sal_Int32) CFDictionaryGetCount((CFDictionaryRef) _propertyValue);
				::rtl::OUString dictKeyString;
				sal_Int32 i;
				::rtl::OUString newPropertyName;

				/* Unfortunately, the only way to get both keys and values out
				 * of a dictionary in Carbon is to get them all at once, so we
				 * do that.
				 */
				CFStringRef *dictKeys;
				CFStringRef localizedDictKey;
				CFTypeRef *dictValues;
				dictKeys = (CFStringRef *) malloc(sizeof(CFStringRef)*numRecords);
				dictValues = (CFTypeRef *) malloc(sizeof(CFTypeRef)*numRecords);
				CFDictionaryGetKeysAndValues((CFDictionaryRef) _propertyValue, (const void **) dictKeys, (const void **) dictValues);

				/* Going through each element... */
				for(i = 0; i < numRecords; i++)
				{
					localizedDictKey = ABCopyLocalizedPropertyOrLabel(dictKeys[i]);
					dictKeyString = CFStringToOUString(localizedDictKey);
					CFRelease(localizedDictKey);
					newPropertyName = _propertyName + ::rtl::OUString::createFromAscii(": ") + fixLabel(dictKeyString);
					insertPropertyIntoMacabRecord(_abrecord, _header, newPropertyName, dictValues[i]);
				}

				free(dictKeys);
				free(dictValues);
			}
			break;

		/* Multivalue */
		case kABMultiIntegerProperty:
		case kABMultiDateProperty:
		case kABMultiStringProperty:
		case kABMultiRealProperty:
		case kABMultiDataProperty:
		case kABMultiDictionaryProperty:
		case kABMultiArrayProperty:
			{
				/* All scalar multivalues are handled in the same way. Each element
				 * is a label and a value. All labels are strings
				 * (kABStringProperty), and all values have the same type
				 * (which is the type of the multivalue minus 255, or as
				 * Carbon's list of property types has it, minus 0x100.
				 * We just get the correct type, then go through each element
				 * and get the label and value and print them in a list.
				 */

				sal_Int32 i;
				sal_Int32 multiLength = ABMultiValueCount((ABMutableMultiValueRef) _propertyValue);
				CFStringRef multiLabel, localizedMultiLabel;
				CFTypeRef multiValue;
				::rtl::OUString multiLabelString, newPropertyName;
				ABPropertyType multiType = (ABPropertyType) (ABMultiValuePropertyType((ABMutableMultiValueRef) _propertyValue) - 0x100);

				/* Go through each element... */
				for(i = 0; i < multiLength; i++)
				{
					/* Label and value */
					multiLabel = ABMultiValueCopyLabelAtIndex((ABMutableMultiValueRef) _propertyValue, i);
					multiValue = ABMultiValueCopyValueAtIndex((ABMutableMultiValueRef) _propertyValue, i);

					localizedMultiLabel = ABCopyLocalizedPropertyOrLabel(multiLabel);
					multiLabelString = CFStringToOUString(localizedMultiLabel);
					newPropertyName = _propertyName + ::rtl::OUString::createFromAscii(": ") + fixLabel(multiLabelString);
					insertPropertyIntoMacabRecord(multiType, _abrecord, _header, newPropertyName, multiValue);

					/* free our variables */
					CFRelease(multiLabel);
					CFRelease(localizedMultiLabel);
					CFRelease(multiValue);
				}
			}
			break;

		/* Unhandled types */
		case kABErrorInProperty:
		case kABDataProperty:
		default:
			/* An error, as far as I have seen, only shows up as a type
			 * returned by a function for dictionaries when the dictionary
			 * holds many types of values. Since we do not use that function,
			 * it shouldn't come up. I have yet to see the kABDataProperty,
			 * and I am not sure how to represent it as a string anyway,
			 * since it appears to just be a bunch of bytes. Assumably, if
			 * these bytes made up a string, the type would be
			 * kABStringProperty. I think that this is used when we are not
			 * sure what the type is (e.g., it could be a string or a number).
			 * That being the case, I still don't know how to represent it.
			 * And, default should never come up, since we've exhausted all
			 * of the possible types for ABPropertyType, but... just in case.
			 */
			break;
	}

}

// -------------------------------------------------------------------------
ABPropertyType MacabRecords::getABTypeFromCFType(const CFTypeID cf_type ) const
{
	sal_Int32 i;
	for(i = 0; i < lcl_CFTypesLength; i++)
	{
		/* A match! */
		if(lcl_CFTypes[i].cf == (sal_Int32) cf_type)
		{
			return (ABPropertyType) lcl_CFTypes[i].ab;
		}
	}
	return kABErrorInProperty;
}

// -------------------------------------------------------------------------
sal_Int32 MacabRecords::size() const
{
	return currentRecord;
}

// -------------------------------------------------------------------------
MacabRecords *MacabRecords::begin()
{
	return this;
}

// -------------------------------------------------------------------------
MacabRecords::iterator::iterator ()
{
}

// -------------------------------------------------------------------------
MacabRecords::iterator::~iterator ()
{
}

// -------------------------------------------------------------------------
void MacabRecords::iterator::operator= (MacabRecords *_records)
{
	id = 0;
	records = _records;
}

// -------------------------------------------------------------------------
void MacabRecords::iterator::operator++ ()
{
	id++;
}

// -------------------------------------------------------------------------
sal_Bool MacabRecords::iterator::operator!= (const sal_Int32 i) const
{
	return(id != i);
}

// -------------------------------------------------------------------------
sal_Bool MacabRecords::iterator::operator== (const sal_Int32 i) const
{
	return(id == i);
}

// -------------------------------------------------------------------------
MacabRecord *MacabRecords::iterator::operator* () const
{
	return records->getRecord(id);
}

// -------------------------------------------------------------------------
sal_Int32 MacabRecords::end() const
{
	return currentRecord;
}

// -------------------------------------------------------------------------
void MacabRecords::swap(const sal_Int32 _id1, const sal_Int32 _id2)
{
	MacabRecord *swapRecord = records[_id1];

	records[_id1] = records[_id2];
	records[_id2] = swapRecord;
}

// -------------------------------------------------------------------------
void MacabRecords::setName(const ::rtl::OUString _sName)
{
	m_sName = _sName;
}

// -------------------------------------------------------------------------
::rtl::OUString MacabRecords::getName() const
{
	return m_sName;
}