/*=============================================================================
	CAPersistence.cpp
	CAAudioEngine

	$Log: CAPersistence.cpp,v $
	Revision 1.28  2005/05/03 00:39:46  asynth
	once more
	
	Revision 1.27  2005/05/03 00:25:13  asynth
	must be static
	
	Revision 1.26  2005/05/02 23:53:08  asynth
	define CFStringRefs locally
	
	Revision 1.25  2004/11/13 03:44:28  bills
	save a full "preset" of midi-param maps
	
	Revision 1.24  2004/11/12 03:35:06  bills
	some tweaks to save/restore mappings
	
	Revision 1.23  2004/11/12 02:43:56  bills
	const save method
	
	Revision 1.22  2004/11/10 19:04:51  bills
	fix include
	
	Revision 1.21  2004/11/04 23:25:18  cbruyns
	added MIDIMap save and restore methods
	
	Revision 1.20  2004/07/13 00:55:28  bills
	cleanup up (and Save/Restore for AudioUnitParameter)
	
	Revision 1.19  2003/10/18 22:37:06  bills
	add a human readable name string to the comp dictionary
	
	Revision 1.18  2003/10/17 00:27:55  bills
	tweaks to persistence formats
	
	Revision 1.17  2003/10/11 22:28:49  bills
	tweak save/restore methodology
	
	Revision 1.16  2003/10/02 22:30:53  bills
	remove unused keys
	
	Revision 1.15  2003/07/16 00:59:12  bills
	fix usage of CACFDict
	
	Revision 1.14  2003/07/07 21:50:38  dwyatt
	more refactoring
	
	Revision 1.13  2003/07/07 18:36:30  bills
	CAAU doesn't Save/Restore
	
	Revision 1.12  2003/05/25 22:13:39  bills
	changes to CACFDictionary
	
	Revision 1.11  2003/04/03 19:50:45  bills
	tweaks
	
	Revision 1.10  2003/04/02 18:20:02  bills
	changes to make persistence work
	
	Revision 1.9  2003/03/30 23:10:45  bills
	ChannelMap to ChannelLayout
	
	Revision 1.8  2003/03/23 02:53:12  bills
	some rewrite and add methods
	
	Revision 1.7  2003/03/21 06:53:36  bills
	prelim impl of CA-AU persistence
	
	Revision 1.6  2003/03/19 21:16:34  bills
	changes to how CAAudioUnit manages its internal state
	
	Revision 1.5  2003/03/17 03:11:49  bills
	fix warning
	
	Revision 1.4  2003/03/17 02:56:12  bills
	AU can now keep track of its active busses
	
	Revision 1.3  2003/03/12 23:38:21  bills
	CAAudioUnit::CanDo
	
	Revision 1.2  2003/03/12 05:08:57  bills
	some tweaks
	
	Revision 1.1  2003/03/12 05:05:05  bills
	refactor persistence
	

	Created by William Stewart on Mon Mar 10 2003.
	Copyright (c) 2003 Apple Computer. All rights reserved.

=============================================================================*/

#include "CACFArray.h"
#include "CACFDictionary.h"

#include "CAAudioUnit.h"
#include "CACFString.h"
#include "CAAudioChannelLayout.h"
#include "CAAUParameter.h"
#include "CAAUMIDIMap.h"

#pragma mark __CAStreamBasicDescription

static const CFStringRef kSampleRate = CFSTR("sample rate");
static const CFStringRef kFormat = CFSTR("format");
static const CFStringRef kFormatFlags = CFSTR("format flags");
static const CFStringRef kPacketBytes = CFSTR("packet bytes");
static const CFStringRef kFramePackets = CFSTR("frame packets");
static const CFStringRef kFrameBytes = CFSTR("frame bytes");
static const CFStringRef kFrameChannels = CFSTR("frame channels");
static const CFStringRef kChannelBits = CFSTR("channel bits");

		// This will return a value that should be used as the key for this struct
		// and a CFData object that contains the current state of this object
OSStatus	CAStreamBasicDescription::Save (CFPropertyListRef *outData) const
{
	CACFDictionary dict(false);

	if (!dict.AddFloat64 (kSampleRate, mSampleRate)) goto error;
	if (!dict.AddUInt32 (kFormat, mFormatID)) goto error;
	if (!dict.AddUInt32 (kFormatFlags, mFormatFlags)) goto error;
	if (!dict.AddUInt32 (kPacketBytes, mBytesPerPacket)) goto error;
	if (!dict.AddUInt32 (kFramePackets, mFramesPerPacket)) goto error;
	if (!dict.AddUInt32 (kFrameBytes, mBytesPerFrame)) goto error;
	if (!dict.AddUInt32 (kFrameChannels, mChannelsPerFrame)) goto error;
	if (!dict.AddUInt32 (kChannelBits, mBitsPerChannel)) goto error;
	
	*outData = dict.GetDict();

	return noErr;

error:
	dict.ShouldRelease (true);
	return paramErr;
}

	
		// Given a CFData object generated by the save command, this will re-establish
		// the CAStreamBasicDescription
OSStatus	CAStreamBasicDescription::Restore (CFPropertyListRef& inData)
{
	if (CFGetTypeID (inData) != CFDictionaryGetTypeID()) return paramErr;
	CACFDictionary dict(static_cast<CFDictionaryRef>(inData), false);
	
	if (!dict.GetFloat64 (kSampleRate, mSampleRate)) return paramErr;
	if (!dict.GetUInt32 (kFormat, mFormatID)) return paramErr;
	if (!dict.GetUInt32 (kFormatFlags, mFormatFlags)) return paramErr;
	if (!dict.GetUInt32 (kPacketBytes, mBytesPerPacket)) return paramErr;
	if (!dict.GetUInt32 (kFramePackets, mFramesPerPacket)) return paramErr;
	if (!dict.GetUInt32 (kFrameBytes, mBytesPerFrame)) return paramErr;
	if (!dict.GetUInt32 (kFrameChannels, mChannelsPerFrame)) return paramErr;
	if (!dict.GetUInt32 (kChannelBits, mBitsPerChannel)) return paramErr;

	return noErr;
}

#pragma mark __CAComponentDescription

static const CFStringRef kType = CFSTR("type");
static const CFStringRef kSubType = CFSTR("subtype");
static const CFStringRef kManu = CFSTR("manufacturer");

OSStatus		CAComponentDescription::Save (CFPropertyListRef *outData) const
{
	CACFDictionary dict(false);
	if (!dict.AddUInt32 (kType, componentType)) goto error;
	if (!dict.AddUInt32 (kSubType, componentSubType)) goto error;
	if (!dict.AddUInt32 (kManu, componentManufacturer)) goto error;
	
	*outData = dict.GetDict();
	
	return 0;
error:
	dict.ShouldRelease (true);
	return paramErr;
}

OSStatus		CAComponentDescription::Restore (CFPropertyListRef &inData)
{
	if (CFGetTypeID (inData) != CFDictionaryGetTypeID()) return paramErr;
	CACFDictionary dict(static_cast<CFDictionaryRef>(inData), false);
		
	if (!dict.GetUInt32 (kType, componentType)) return paramErr;
	if (!dict.GetUInt32 (kSubType, componentSubType)) return paramErr;
	if (!dict.GetUInt32 (kManu, componentManufacturer)) return paramErr;

	componentFlags = 0;
	componentFlagsMask = 0;
	
	return 0;
}

#pragma mark __CAComponent

OSStatus		CAComponent::Save (CFPropertyListRef *outData) const
{
	OSStatus result = mDesc.Save (outData);
	if (result) return result;
	
	//add the name string of the component for a human readable name...
	// this name string is *not* restored when restoring the component
	CFStringRef	name = GetCompName ();
	if (name && *outData)
		CFDictionarySetValue ((CFMutableDictionaryRef)(*outData), CFSTR("name"), name);
	
	return noErr;
}

OSStatus		CAComponent::Restore (CFPropertyListRef &inData)
{
	if (mDesc.Restore (inData)) return paramErr;

	Clear();

	mComp = FindNextComponent (NULL, &mDesc);
		// this will restore the current flags...
	if (mComp)
		GetComponentInfo (Comp(), &mDesc, NULL, NULL, NULL);

	return noErr;
}


#pragma mark __CAAudioChannelLayout

static const CFStringRef kACLTagKey = CFSTR("acl tag");
static const CFStringRef kACLBitmapKey = CFSTR("chan bitmap");
static const CFStringRef kACLLabelKey = CFSTR("label");
static const CFStringRef kACLFlagsKey = CFSTR("flags");
static const CFStringRef kACLCoords0Key = CFSTR("coords 0");
static const CFStringRef kACLCoords1Key = CFSTR("coords 1");
static const CFStringRef kACLCoords2Key = CFSTR("coords 2");
static const CFStringRef kACLDescsKey = CFSTR("descriptions");
	
OSStatus	CAAudioChannelLayout::Save (CFPropertyListRef *outData) const 
{
	const AudioChannelLayout& layout = Layout();

	CACFDictionary dict (false);
	if (!dict.AddUInt32 (kACLTagKey, layout.mChannelLayoutTag))
		goto badadd;
	if (layout.mChannelBitmap && !dict.AddUInt32 (kACLBitmapKey, layout.mChannelBitmap))
		goto badadd;
	
	if (layout.mNumberChannelDescriptions)
	{	
		CFMutableArrayRef descs = CFArrayCreateMutable (NULL, layout.mNumberChannelDescriptions, &kCFTypeArrayCallBacks);
		
		const AudioChannelDescription *desc = layout.mChannelDescriptions;
		for (unsigned int i = 0; i < layout.mNumberChannelDescriptions; ++i, ++desc) 
		{
			CACFDictionary descDict (true);
			if (!descDict.AddUInt32 (kACLLabelKey, desc->mChannelLabel))
				{ CFRelease (descs); goto badadd; }
			if (!descDict.AddUInt32 (kACLFlagsKey, desc->mChannelFlags))
				{ CFRelease (descs); goto badadd; }
			if (!descDict.AddFloat32 (kACLCoords0Key, desc->mCoordinates[0]))
				{ CFRelease (descs); goto badadd; }
			if (!descDict.AddFloat32 (kACLCoords1Key, desc->mCoordinates[1]))
				{ CFRelease (descs); goto badadd; }
			if (!descDict.AddFloat32 (kACLCoords2Key, desc->mCoordinates[2]))
				{ CFRelease (descs); goto badadd; }
			
			CFArrayAppendValue (descs, descDict.AsPropertyList());
		}
		dict.AddArray (kACLDescsKey, descs);
		
		CFRelease (descs);
	}
	
	*outData = dict.GetDict();	
	
	return noErr;
	
badadd:
	dict.ShouldRelease(true);
	return paramErr;
}

OSStatus	CAAudioChannelLayout::Restore (CFPropertyListRef &inData) 
{
	if (CFGetTypeID (inData) != CFDictionaryGetTypeID()) return paramErr;
	CACFDictionary dict(static_cast<CFDictionaryRef>(inData), false);

	ACLRefCounter *temp = NULL;
	UInt32 size = 0;
	AudioChannelLayout* layout;
	
	CFArrayRef descs = NULL;
	UInt32 numDescs = 0;

	if (dict.GetArray (kACLDescsKey, descs)) {
		numDescs = CFArrayGetCount (descs);
	}
	
	size = numDescs * sizeof (AudioChannelDescription) + offsetof(AudioChannelLayout, mChannelDescriptions[0]);
	temp = new ACLRefCounter (size);
	layout = temp->GetLayout();
		
	if (!dict.GetUInt32 (kACLTagKey, layout->mChannelLayoutTag))
		goto badget;
	if (dict.HasKey (kACLBitmapKey)) {
		if (!dict.GetUInt32 (kACLBitmapKey, layout->mChannelBitmap))
			goto badget;
	} else
		layout->mChannelBitmap = 0;
		
	layout->mNumberChannelDescriptions = numDescs;
	
	if (numDescs)
	{
		AudioChannelDescription *desc = layout->mChannelDescriptions;
		for (unsigned int i = 0; i < numDescs; ++i, ++desc)
		{
			CFDictionaryRef descDict = (CFDictionaryRef)CFArrayGetValueAtIndex (descs, i);			
			CACFDictionary theDesc (descDict, false);
			
			if (!theDesc.GetUInt32 (kACLLabelKey, desc->mChannelLabel))
				goto badget;
			if (!theDesc.GetUInt32 (kACLFlagsKey, desc->mChannelFlags))
				goto badget;
			if (!theDesc.GetFloat32 (kACLCoords0Key, desc->mCoordinates[0]))
				goto badget;
			if (!theDesc.GetFloat32 (kACLCoords1Key, desc->mCoordinates[1]))
				goto badget;
			if (!theDesc.GetFloat32 (kACLCoords2Key, desc->mCoordinates[2]))
				goto badget;
		}
	}
	if (mLayoutHolder)
		mLayoutHolder->release();

	mLayoutHolder = temp;
	
	return noErr;

badget:
	delete temp;
	return paramErr;
}

#pragma mark __AudioUnitParameter

static const CFStringRef kAUScopeStr = CFSTR("scope");
static const CFStringRef kAUElementIDStr = CFSTR("element ID");
static const CFStringRef kAUParameterIDStr = CFSTR("paramID");

void		CAAUParameter::Save (CFPropertyListRef &outData) const 
{ 
	return CAAUParameter::Save (*this, outData); 
}

// static functions to save/restore AudioUnitParameter
void		CAAUParameter::Save (const AudioUnitParameter &inParam, CFPropertyListRef &outData)
{
	CACFDictionary dict(false);
	dict.AddUInt32 (kAUScopeStr, inParam.mScope);
	dict.AddUInt32 (kAUElementIDStr, inParam.mElement);
	dict.AddUInt32 (kAUParameterIDStr, inParam.mParameterID);

	outData = dict.AsPropertyList();
}

OSStatus	CAAUParameter::Restore	(const CFPropertyListRef inData, AudioUnitParameter &outParam)
{
	if (CFGetTypeID (inData) != CFDictionaryGetTypeID()) return paramErr;
	CACFDictionary dict(static_cast<CFDictionaryRef>(inData), false);

	if (!dict.GetUInt32 (kAUScopeStr, outParam.mScope)) return paramErr;
	if (!dict.GetUInt32 (kAUElementIDStr, outParam.mElement)) return paramErr;
	if (!dict.GetUInt32 (kAUParameterIDStr, outParam.mParameterID)) return paramErr;
	return noErr;
}


#pragma mark __MIDIMap

const CFStringRef kParamMIDIStr = CFSTR("param maps");

const CFStringRef kMIDIFlagsStr = CFSTR("flags");
const CFStringRef kMIDISubMinStr = CFSTR("sub min");
const CFStringRef kMIDISubMaxStr = CFSTR("sub max");
const CFStringRef kMIDIStatusStr = CFSTR("midi status byte");
const CFStringRef kMIDIDataByteStr = CFSTR("midi data1 byte");
const CFStringRef kAUStr = CFSTR("unit");

static const CFStringRef kLocalElementIDStr = CFSTR("element ID");
static const CFStringRef kLocalScopeStr = CFSTR("scope");
static const CFStringRef kLocalParameterIDStr = CFSTR("paramID");

void CAAUMIDIMap::Save(CFPropertyListRef &outData) const
{
	CACFDictionary paramDict(false);

	paramDict.AddUInt32 (kLocalScopeStr, mScope);
	paramDict.AddUInt32 (kLocalElementIDStr, mElement);
	paramDict.AddUInt32 (kLocalParameterIDStr, mParameterID);
	paramDict.AddUInt32 (kMIDIFlagsStr, mFlags);
	paramDict.AddFloat32 (kMIDISubMinStr, mSubRangeMin);
	paramDict.AddFloat32 (kMIDISubMaxStr, mSubRangeMax);

	UInt32 data = mStatus;
	paramDict.AddUInt32 (kMIDIStatusStr, data);

	data = mData1;
	paramDict.AddUInt32 (kMIDIDataByteStr, data);

	outData = paramDict.GetCFDictionary();
}

void CAAUMIDIMap::Restore(CFDictionaryRef inData)
{	
	CACFDictionary paramDict (inData, false);

	if (!paramDict.GetUInt32 (kLocalScopeStr, mScope)) return; 
	if (!paramDict.GetUInt32 (kLocalElementIDStr, mElement)) return; 
	if (!paramDict.GetUInt32 (kLocalParameterIDStr, mParameterID)) return; 
	if (!paramDict.GetUInt32 (kMIDIFlagsStr, mFlags)) return;
	if (!paramDict.GetFloat32 (kMIDISubMinStr, mSubRangeMin)) return; 
	if (!paramDict.GetFloat32 (kMIDISubMaxStr, mSubRangeMax)) return; 
	UInt32 data;		
	if (!paramDict.GetUInt32 (kMIDIStatusStr, data)) return;
	mStatus = data;
	if (!paramDict.GetUInt32 (kMIDIDataByteStr, data)) return; 
	mData1 = data;
}

void		CAAUMIDIMap::SaveAsMapPList (AudioUnit inUnit, const AUParameterMIDIMapping* inMappings, UInt32 inNumMappings, CFPropertyListRef &outData, CFStringRef inName)
{

	CACFDictionary mappingDict (false);	
	CACFArray maps (true);
	
	for (UInt32 i = 0; i< inNumMappings; ++i) 
	{
		CFPropertyListRef data;
		CAAUMIDIMap paramMap(inMappings[i]);
		paramMap.Save (data);
		if (data) 
		{
			maps.AppendCFType (data); 
			CFRelease(data); 
		}				
	}

	if (maps.GetNumberItems()) {
		mappingDict.AddCFType (kParamMIDIStr, maps.GetCFArray());
		
		// Add the AU info here - where this map came from
		CAAudioUnit au (inUnit);
		CFPropertyListRef data;
		au.Comp().Save (&data);
		
		mappingDict.AddCFType (kAUStr, data);
		CFRelease(data);
		
		if (!inName) inName = CFSTR("Untitled");
		mappingDict.AddString (CFSTR("name"), inName);
		
		mappingDict.AddUInt32 (CFSTR("version"), 1);
		
		outData = mappingDict.AsPropertyList();
	} else {
		mappingDict.ShouldRelease(true);
		outData = NULL;
	}
}

UInt32		CAAUMIDIMap::NumberOfMaps (const CFDictionaryRef inData)
{
	CACFDictionary dict (inData, false);
	
	if (dict.HasKey (kParamMIDIStr)) 
	{
		CFArrayRef cfArray;
		dict.GetArray (kParamMIDIStr, cfArray); 
		
		CACFArray array (cfArray, false);
		
		return array.GetNumberItems();
	}
	return 0;
}
	
void		CAAUMIDIMap::RestoreFromMapPList (const CFDictionaryRef inData, AUParameterMIDIMapping* outMappings, UInt32 inNumMappings)
{

	CACFDictionary dict (inData, false);
	
	if (dict.HasKey (kParamMIDIStr)) 
	{
		CFArrayRef cfArray;
		dict.GetArray (kParamMIDIStr, cfArray); 
		
		CACFArray array (cfArray, false);
		
		UInt32 count = array.GetNumberItems();
		if (count > inNumMappings)
			count = inNumMappings;
		
		for (unsigned int i = 0; i < count; ++i)
		{
			CFDictionaryRef paramsDictRef;
			if (!array.GetDictionary(i, paramsDictRef)) 
				return;
			
			CAAUMIDIMap parameterMap;
			parameterMap.Restore(paramsDictRef);
			outMappings[i] = parameterMap;
		}
	}
}


