/* ParameterWeb - implements the following classes:
**		BParameterWeb, BParameterGroup, BParameter, BNullParameter,
**		BContinuousParameter, BDiscreteParameter
**
** Author: Zousar Shaker
**         Axel Dörfler, axeld@pinc-software.de
**         Marcus Overhagen
**
** This file may be used under the terms of the OpenBeOS License.
*/


#include <ParameterWeb.h>
#include <MediaNode.h>
#include <string.h>
#include "DataExchange.h"
#include "MediaMisc.h"


#include "debug.h"


/*
	The following is documentation on the flattened format
	of structures/classes in this module:
	
	//--------BEGIN-CORE-BPARAMETER-STRUCT---------------------
	?? (0x02040607): 4 bytes
	BParameter Struct Size (in bytes): 4 bytes
	ID: 4 bytes
	Name String Length: 1 byte (??)
		Name String: 'Name String Length' bytes
	Kind String Length: 1 byte (??)
		Kind String: 'Kind String Length' bytes
	Unit String Length: 1 byte (??)
		Unit String: 'Unit String Length' bytes
	Inputs Count: 4 bytes
		Inputs (pointers): ('Inputs Count')*4 bytes
	Outputs Count: 4 bytes
		Outputs (pointers): ('Outputs Count')*4 bytes
	Media Type: 4 bytes
	ChannelCount: 4 bytes
	Flags: 4 bytes
	//---------END-CORE-BPARAMETER-STRUCT-----------------------
	//--------BEGIN-BCONTINUOUSPARAMETER-STRUCT---------
	Min: 4 bytes (as float)
	Max: 4 bytes (as float)
	Stepping: 4 bytes (as float)
	Response: 4 bytes (as int or enum)
	Factor: 4 bytes (as float)
	Offset: 4 bytes (as float)
	//--------END-BCONTINUOUSPARAMETER-STRUCT-------------
	//--------BEGIN-BDISCRETEPARAMETER-STRUCT----------------
	NumItems: 4 bytes (as int)
		//for each item BEGIN
		Item Name String Length: 1 byte
			Item Name String: 'Item Name String Length' bytes
		Item Value: 4 bytes (as int)
		//for each item END
	//--------END-BDISCRETEPARAMETER-STRUCT-------------------
	
	//--------BEGIN-CORE-BPARAMETERGROUP-STRUCT-----------
	?? (0x03040507 OR 0x03040509 depending if the flags field is included or not???): 4 bytes
	(possible) Flags: 4 bytes
	Name String Length: 1 byte (??)
		Name String: 'Name String Length' bytes
	Param Count: 4 bytes
		//for each Param BEGIN
		Pointer: 4 bytes
		Parameter Type: 4 bytes
		Flattened Parameter Size: 4 bytes
		Flattened Parameter: 'Flattened Parameter Size' bytes
		//for each Param END
	Subgroup Count: 4 bytes
		//for each SubGroup BEGIN
		Pointer: 4 bytes
		MEDIA PARAMETER GROUP TYPE('BMCG' (opposite byte order in file)): 4 bytes
		Flattened Group Size: 4 bytes
		Flattened Group: 'Flattened Group Size' bytes
		//for each SubGroup END
	
	//---------END-CORE-BPARAMETERGROUP-STRUCT--------------
	
	//--------BEGIN-CORE-BPARAMETERWEB-STRUCT-----------
	?? 0x01030506: 4 bytes
	??: 4 bytes (is always 1)
	Group Count: 4 bytes
	Node (as media_node): 0x18 bytes (decimal 24 bytes)
		//for each Group BEGIN
		Flattened Group Size: 4 bytes
		Flattened Group: 'Flattened Group Size' bytes
		//for each Group END
		//for each Group BEGIN
		??: 4 bytes (never get written to (holds uninitialized value))
		//for each Group END
	//---------END-CORE-BPARAMETERWEB-STRUCT--------------
	
*/


const char * const B_GENERIC			 = "";
const char * const B_MASTER_GAIN		 = "Master";
const char * const B_GAIN				 = "Gain";
const char * const B_BALANCE			 = "Balance";
const char * const B_FREQUENCY			 = "Frequency";
const char * const B_LEVEL				 = "Level";
const char * const B_SHUTTLE_SPEED		 = "Speed";
const char * const B_CROSSFADE			 = "XFade";
const char * const B_EQUALIZATION		 = "EQ";
const char * const B_COMPRESSION		 = "Compression";
const char * const B_QUALITY			 = "Quality";
const char * const B_BITRATE			 = "Bitrate";
const char * const B_GOP_SIZE			 = "GOPSize";
const char * const B_MUTE				 = "Mute";
const char * const B_ENABLE				 = "Enable";
const char * const B_INPUT_MUX			 = "Input";
const char * const B_OUTPUT_MUX			 = "Output";
const char * const B_TUNER_CHANNEL		 = "Channel";
const char * const B_TRACK				 = "Track";
const char * const B_RECSTATE			 = "RecState";
const char * const B_SHUTTLE_MODE		 = "Shuttle";
const char * const B_RESOLUTION			 = "Resolution";
const char * const B_COLOR_SPACE		 = "Colorspace";
const char * const B_FRAME_RATE			 = "FrameRate";
const char * const B_VIDEO_FORMAT		 = "VideoFormat";
const char * const B_WEB_PHYSICAL_INPUT	 = "PhysInput";
const char * const B_WEB_PHYSICAL_OUTPUT = "PhysOutput";
const char * const B_WEB_ADC_CONVERTER	 = "ADC";
const char * const B_WEB_DAC_CONVERTER	 = "DAC";
const char * const B_WEB_LOGICAL_INPUT	 = "LogInput";
const char * const B_WEB_LOGICAL_OUTPUT	 = "LogOutput";
const char * const B_WEB_LOGICAL_BUS	 = "LogBus";
const char * const B_WEB_BUFFER_INPUT	 = "DataInput";
const char * const B_WEB_BUFFER_OUTPUT	 = "DataOutput";
const char * const B_SIMPLE_TRANSPORT	 = "SimpleTransport";

// Flattened data

static const int32 kCurrentParameterWebVersion = 1;
static const uint32 kParameterWebMagic = 0x01030506;
static const uint32 kBufferGroupMagic = 0x03040509;
static const uint32 kBufferGroupMagicNoFlags = 0x03040507;
static const uint32 kParameterMagic = 0x02040607;

static const ssize_t kAdditionalParameterGroupSize = 12;
static const ssize_t kAdditionalParameterSize = 35;

/* BContinuousParameter - FlattenedSize() fixed part
 *	Min: 4 bytes (as float)
 *	Max: 4 bytes (as float)
 *	Stepping: 4 bytes (as float)
 *	Response: 4 bytes (as int or enum)
 *	Factor: 4 bytes (as float)
 *	Offset: 4 bytes (as float)
 */
static const ssize_t kAdditionalContinuousParameterSize = 24;
static const ssize_t kAdditionalDiscreteParameterSize = sizeof(ssize_t);


// helper functions


template<class Type> Type
read_from_buffer(const void **_buffer)
{
	const Type *typedBuffer = static_cast<const Type *>(*_buffer);
	Type value = *typedBuffer;

	typedBuffer++;
	*_buffer = static_cast<const void *>(typedBuffer);

	return value;
}


static status_t
read_string_from_buffer(const void **_buffer, char **_string, ssize_t size)
{
	if (size < 1)
		return B_BAD_VALUE;
	
	const uint8 *buffer = static_cast<const uint8 *>(*_buffer);
	uint8 length = *buffer++;
	if (length > size - 1)
		return B_BAD_VALUE;

	char *string = (char *)malloc(length + 1);
	if (string == NULL)
		return B_NO_MEMORY;

	memcpy(string, buffer, length);
	string[length] = '\0';

	*_buffer = static_cast<const void *>(buffer + length);
	*_string = string;
	return B_OK;
}


// currently unused
#if 0
template<class Type> Type *
reserve_in_buffer(void **_buffer)
{
	Type *typedBuffer = static_cast<Type *>(*_buffer);

	*typedBuffer = 0;
	typedBuffer++;

	*_buffer = static_cast<void *>(typedBuffer);
}
#endif

template<class Type> void
write_to_buffer(void **_buffer, Type value)
{
	Type *typedBuffer = static_cast<Type *>(*_buffer);

	*typedBuffer = value;
	typedBuffer++;

	*_buffer = static_cast<void *>(typedBuffer);
}


void
write_string_to_buffer(void **_buffer, const char *string)
{
	uint8 *buffer = static_cast<uint8 *>(*_buffer);
	uint32 length = string ? strlen(string) : 0;
	if (length > 255)
		length = 255;

	*buffer++ = static_cast<uint8>(length);
	if (length) {
		memcpy(buffer, string, length);
		buffer += length;
	}

	*_buffer = static_cast<void *>(buffer);
}


static void
skip_in_buffer(const void **_buffer, uint32 bytes)
{
	const uint8 *buffer = static_cast<const uint8 *>(*_buffer);

	buffer += bytes;

	*_buffer = static_cast<const void *>(buffer);
}


static void inline
skip_in_buffer(void **_buffer, uint32 bytes)
{
	// This actually shouldn't be necessary, but I believe it's a
	// bug in Be's gcc - it complains about "adds cv-quals without intervening `const'"
	// when passing a "const void **" pointer as the first argument.
	// Maybe I am just stupid, though -- axeld.

	skip_in_buffer((const void **)_buffer, bytes);
}


template<class Type> Type
swap32(Type value, bool doSwap)
{
	STATIC_ASSERT(sizeof(Type) == 4);

	if (doSwap)
		return (Type)B_SWAP_INT32((int32)value);

	return value;
}


template<class Type> Type
read_from_buffer_swap32(const void **_buffer, bool doSwap)
{
	return swap32<Type>(read_from_buffer<Type>(_buffer), doSwap);
}


static inline ssize_t
size_left(ssize_t size, const void *bufferStart, const void *buffer)
{
	return size - static_cast<ssize_t>((const uint8 *)buffer - (const uint8 *)bufferStart);
}


/** Copies the source string into a new buffer with the specified
 *	maximum size. This always includes a trailing zero byte.
 *	Returns NULL if the source string is either NULL or empty, or
 *	if there wasn't enough memory to allocate the buffer.
 *	The buffer returned is to be freed by the caller using free().
 */

static char *
strndup(const char *source, size_t maxBufferSize)
{
	if (source == NULL || source[0] == '\0')
		return NULL;

	uint32 size = strlen(source) + 1;
	if (size > maxBufferSize)
		size = maxBufferSize;

	char *target = (char *)malloc(size);
	if (target == NULL)
		return NULL;
	
	memcpy(target, source, size - 1);
	target[size - 1] = '\0';

	return target;
}


//	#pragma mark -

/*************************************************************
 * public BParameterWeb
 *************************************************************/


BParameterWeb::BParameterWeb()
	:	mNode(media_node::null) // mNode is set in BControllable::SetParameterWeb()
{
	CALLED();

	mGroups = new BList();

	mOldRefs = new BList();
	mNewRefs = new BList();

	ASSERT(mGroups != NULL && mOldRefs != NULL && mNewRefs != NULL);
}


BParameterWeb::~BParameterWeb()
{
	CALLED();

	if (mGroups != NULL) {
		for(int32 i = mGroups->CountItems(); i-- > 0;) {
			delete static_cast<BParameterGroup *>(mGroups->ItemAt(i));
		}

		delete mGroups;
	}

	delete mOldRefs;
	delete mNewRefs;
}


media_node
BParameterWeb::Node()
{
	return mNode;
}


BParameterGroup *
BParameterWeb::MakeGroup(const char *name)
{
	CALLED();
	ASSERT(mGroups != NULL);

	BParameterGroup *group = new BParameterGroup(this, name);
	mGroups->AddItem(group);

	return group;
}


int32
BParameterWeb::CountGroups()
{
	ASSERT(mGroups != NULL);

	return mGroups->CountItems();
}


BParameterGroup *
BParameterWeb::GroupAt(int32 index)
{
	ASSERT(mGroups != NULL);

	return static_cast<BParameterGroup *>(mGroups->ItemAt(index));
}


int32
BParameterWeb::CountParameters()
{
	CALLED();
	ASSERT(mGroups != NULL);

	// Counts over all groups (and sub-groups) in the web.
	// The "groups" list is used as count stack

	BList groups(*mGroups);
	int32 count = 0;

	for (int32 i = 0; i < groups.CountItems(); i++) {
		BParameterGroup *group = static_cast<BParameterGroup *>(groups.ItemAt(i));

		count += group->CountParameters();

		if (group->mGroups != NULL)
			groups.AddList(group->mGroups);
	}

	return count;
}


BParameter *
BParameterWeb::ParameterAt(int32 index)
{
	CALLED();
	ASSERT(mGroups != NULL);
	
	// Iterates over all groups (and sub-groups) in the web.
	// The "groups" list is used as iteration stack (breadth search style)
	// Maintains the same order as the Be implementation

	BList groups(*mGroups);

	for (int32 i = 0; i < groups.CountItems(); i++) {
		BParameterGroup *group = static_cast<BParameterGroup *>(groups.ItemAt(i));
		int32 count = group->CountParameters();
		if (index < count)
			return group->ParameterAt(index);

		index -= count;
			// the index is always relative to the start of the current group

		if (group->mGroups != NULL)
			groups.AddList(group->mGroups);
	}

	TRACE("*** could not find parameter at %ld (count = %ld)\n", index, CountParameters());
	return NULL;
}


bool
BParameterWeb::IsFixedSize() const
{
	return false;
}


type_code
BParameterWeb::TypeCode() const
{
	return B_MEDIA_PARAMETER_WEB_TYPE;
}


ssize_t
BParameterWeb::FlattenedSize() const
{
	CALLED();

/*
	//--------BEGIN-CORE-BPARAMETERWEB-STRUCT-----------
	?? 0x01030506: 4 bytes
	??: 4 bytes (is always 1)
	Group Count: 4 bytes
	Node (as media_node): 0x18 bytes (decimal 24 bytes)
		//for each Group BEGIN
		Flattened Group Size: 4 bytes
		Flattened Group: 'Flattened Group Size' bytes
		//for each Group END
		//for each Group BEGIN
		??: 4 bytes (never get written to (holds uninitialized value))
		//for each Group END
	//---------END-CORE-BPARAMETERWEB-STRUCT--------------
*/
	//36 guaranteed bytes, variable after that.
	ssize_t size = sizeof(int32) + 2*sizeof(int32) + sizeof(media_node);

	for (int32 i = mGroups->CountItems(); i-- > 0;) {
		BParameterGroup *group = static_cast<BParameterGroup *>(mGroups->ItemAt(i));
		if (group != NULL) {
			size += 4 + group->FlattenedSize();
				// 4 bytes for the flattened size
		}
	}

	return size;
}


status_t
BParameterWeb::Flatten(void *buffer, ssize_t size) const
{
	CALLED();

	if (buffer == NULL)
		return B_NO_INIT;

	ssize_t actualSize = BParameterWeb::FlattenedSize();
	if (size < actualSize)
		return B_NO_MEMORY;

	void *bufferStart = buffer;

	write_to_buffer<int32>(&buffer, kParameterWebMagic);
	write_to_buffer<int32>(&buffer, kCurrentParameterWebVersion);

	// flatten all groups into this buffer

	int32 count = mGroups ? mGroups->CountItems() : 0;
	write_to_buffer<int32>(&buffer, count);

	write_to_buffer<media_node>(&buffer, mNode);

	for (int32 i = 0; i < count; i++) {
		BParameterGroup *group = static_cast<BParameterGroup *>(mGroups->ItemAt(i));
		if (group == NULL) {
			ERROR("BParameterWeb::Flatten(): group is NULL\n");
			continue;
		}

		ssize_t groupSize = group->FlattenedSize();
		if (groupSize > size_left(size, bufferStart, buffer)) {
			ERROR("BParameterWeb::Flatten(): buffer too small\n");
			return B_BAD_VALUE;
		}

		write_to_buffer<ssize_t>(&buffer, groupSize);

		// write the flattened sub group
		status_t status = group->Flatten(buffer, groupSize);
		if (status < B_OK)
			return status;

		skip_in_buffer(&buffer, groupSize);
	}

	return B_OK;
}


bool
BParameterWeb::AllowsTypeCode(type_code code) const
{
	return code == TypeCode();
}


status_t
BParameterWeb::Unflatten(type_code code, const void *buffer, ssize_t size)
{
	CALLED();

	if (!AllowsTypeCode(code)) {
		ERROR("BParameterWeb::Unflatten(): wrong type code\n");
		return B_BAD_TYPE;
	}

	if (buffer == NULL) {
		ERROR("BParameterWeb::Unflatten(): NULL buffer pointer\n");
		return B_NO_INIT;
	}

	// if the buffer is smaller than the size needed to read the
	// signature field, the mystery field, the group count, and the Node, then there is a problem
	if (size < static_cast<ssize_t>(sizeof(int32) + sizeof(int32) + sizeof(ssize_t) + sizeof(media_node)) )
	{
		ERROR("BParameterWeb::Unflatten(): size to small\n");
		return B_ERROR;
	}

	const void *bufferStart = buffer;

	uint32 magic = read_from_buffer<uint32>(&buffer);
	bool isSwapped = false;

	if (magic == B_SWAP_INT32(kParameterWebMagic)) {
		isSwapped = true;
		magic = B_SWAP_INT32(magic);
	}
	if (magic != kParameterWebMagic)
		return B_BAD_DATA;

	// Note, it's not completely sure that this field is the version
	// information - but it doesn't seem to have another purpose	
	int32 version = read_from_buffer_swap32<int32>(&buffer, isSwapped);
	if (version != kCurrentParameterWebVersion) {
		ERROR("BParameterWeb::Unflatten(): wrong version %ld (%lx)?!\n", version, version);
		return B_ERROR;
	}

#if 0	
	if(mGroups != NULL)
	{
		for (int32 i = 0; i < mGroups->CountItems(); i++)
		{
			BParameterGroup *CurrentItem = static_cast<BParameterGroup *>(mGroups->ItemAt(i));
			if(CurrentItem != NULL)
			{
				delete CurrentItem;
			}
		}
		mGroups->MakeEmpty();
	}
	else
	{
		mGroups = new BList();
	}
#endif

	int32 count = read_from_buffer_swap32<int32>(&buffer, isSwapped);

	mNode = read_from_buffer<media_node>(&buffer);
	if (isSwapped)
		swap_data(B_INT32_TYPE, &mNode, sizeof(media_node), B_SWAP_ALWAYS);

	for (int32 i = 0; i < count; i++) {
		ssize_t groupSize = read_from_buffer_swap32<ssize_t>(&buffer, isSwapped);
		if (groupSize > size_left(size, bufferStart, buffer)) {
			ERROR("BParameterWeb::Unflatten(): buffer too small\n");
			return B_BAD_DATA;
		}

		BParameterGroup *group = new BParameterGroup(this, "unnamed");
		status_t status = group->Unflatten(group->TypeCode(), buffer, groupSize);
		if (status < B_OK) {
			ERROR("BParameterWeb::Unflatten(): unflatten group failed\n");
			delete group;
			return status;
		}

		skip_in_buffer(&buffer, groupSize);

		mGroups->AddItem(group);
	}

	// fix all references (ParameterAt() style)

	if ((mOldRefs != NULL) && (mNewRefs != NULL)) {
		BList groups(*mGroups);
		
		for (int32 i = 0; i < groups.CountItems(); i++) {
			BParameterGroup *group = static_cast<BParameterGroup *>(groups.ItemAt(i));
			
			for (int32 index = group->CountParameters(); index-- > 0;) {
				BParameter *parameter = static_cast<BParameter *>(group->ParameterAt(index));

				parameter->FixRefs(*mOldRefs, *mNewRefs);
			}
			
			if (group->mGroups != NULL)
				groups.AddList(group->mGroups);
		}

		mOldRefs->MakeEmpty();
		mNewRefs->MakeEmpty();
	}

	return B_OK;
}

/*************************************************************
 * private BParameterWeb
 *************************************************************/

/*
unimplemented
BParameterWeb::BParameterWeb(const BParameterWeb &clone)
BParameterWeb &BParameterWeb::operator=(const BParameterWeb &clone)
*/


void
BParameterWeb::AddRefFix(void *oldItem, void *newItem)
{
	ASSERT(mOldRefs != NULL);
	ASSERT(mNewRefs != NULL);

	mOldRefs->AddItem(oldItem);
	mNewRefs->AddItem(newItem);
}


//	#pragma mark -

/*************************************************************
 * private BParameterGroup
 *************************************************************/


BParameterGroup::BParameterGroup(BParameterWeb *web, const char *name)
	:	mWeb(web),
	mFlags(0)
{
	CALLED();
	TRACE("BParameterGroup: web = %p, name = \"%s\"\n", web, name);

	mName = strndup(name, 256);

	mControls = new BList();
	mGroups = new BList();
}


BParameterGroup::~BParameterGroup()
{
	CALLED();

	if (mControls) {
		BParameter **items = reinterpret_cast<BParameter **>(mControls->Items());
		for (int i = mControls->CountItems(); i-- > 0;)
			delete items[i];

		delete mControls;
	}

	if (mGroups) {
		BParameterGroup **items = reinterpret_cast<BParameterGroup **>(mGroups->Items());
		for (int i = mGroups->CountItems(); i-- > 0;)
			delete items[i];

		delete mGroups;
	}

	free(mName);
}

/*************************************************************
 * public BParameterGroup
 *************************************************************/

BParameterWeb *
BParameterGroup::Web() const
{
	return mWeb;
}


const char *
BParameterGroup::Name() const
{
	return mName;
}


void
BParameterGroup::SetFlags(uint32 flags)
{
	mFlags = flags;
}


uint32
BParameterGroup::Flags() const
{
	return mFlags;
}


BNullParameter *
BParameterGroup::MakeNullParameter(int32 id, media_type mediaType, const char *name,
	const char *kind)
{
	CALLED();
	ASSERT(mControls != NULL);

	BNullParameter *parameter = new BNullParameter(id, mediaType, mWeb, name, kind);
	parameter->mGroup = this;
	mControls->AddItem(parameter);

	return parameter;
}


BContinuousParameter *
BParameterGroup::MakeContinuousParameter(int32 id, media_type mediaType,
	const char *name, const char *kind, const char *unit,
	float minimum, float maximum, float stepping)
{
	CALLED();
	ASSERT(mControls != NULL);

	BContinuousParameter *parameter = new BContinuousParameter(id, mediaType, mWeb,
		name, kind, unit, minimum, maximum, stepping);

	parameter->mGroup = this;
	mControls->AddItem(parameter);

	return parameter;
}


BDiscreteParameter *
BParameterGroup::MakeDiscreteParameter(int32 id, media_type mediaType,
	const char *name, const char *kind)
{
	CALLED();
	ASSERT(mControls != NULL);

	BDiscreteParameter *parameter = new BDiscreteParameter(id, mediaType, mWeb, name, kind);
	if (parameter == NULL)
		return NULL;

	parameter->mGroup = this;
	mControls->AddItem(parameter);

	return parameter;
}


BParameterGroup *
BParameterGroup::MakeGroup(const char *name)
{
	CALLED();
	ASSERT(mGroups != NULL);

	BParameterGroup *group = new BParameterGroup(mWeb, name);
	if (group != NULL)
		mGroups->AddItem(group);

	return group;
}


int32
BParameterGroup::CountParameters()
{
	ASSERT(mControls != NULL);

	return mControls->CountItems();
}


BParameter *
BParameterGroup::ParameterAt(int32 index)
{
	ASSERT(mControls != NULL);

	return static_cast<BParameter *>(mControls->ItemAt(index));
}


int32
BParameterGroup::CountGroups()
{
	ASSERT(mGroups != NULL);

	return mGroups->CountItems();
}


BParameterGroup *
BParameterGroup::GroupAt(int32 index)
{
	ASSERT(mGroups != NULL);

	return static_cast<BParameterGroup *>(mGroups->ItemAt(index));
}


bool
BParameterGroup::IsFixedSize() const
{
	return false;
}


type_code
BParameterGroup::TypeCode() const
{
	return B_MEDIA_PARAMETER_GROUP_TYPE;
}


ssize_t
BParameterGroup::FlattenedSize() const
{
	CALLED();
	ASSERT(mControls != NULL);
	ASSERT(mGroups != NULL);

	/*
		//--------BEGIN-CORE-BPARAMETERGROUP-STRUCT-----------
		?? (0x03040507 OR 0x03040509 depending if the flags field is included or not???): 4 bytes
		(possible) Flags: 4 bytes
		Name String Length: 1 byte (??)
			Name String: 'Name String Length' bytes
		Param Count: 4 bytes
			//for each Param BEGIN
			Pointer: 4 bytes
			Parameter Type: 4 bytes
			Flattened Parameter Size: 4 bytes
			Flattened Parameter: 'Flattened Parameter Size' bytes
			//for each Param END
		Subgroup Count: 4 bytes
			//for each SubGroup BEGIN
			Pointer: 4 bytes
			MEDIA PARAMETER GROUP TYPE('BMCG' (opposite byte order in file)): 4 bytes
			Flattened Group Size: 4 bytes
			Flattened Group: 'Flattened Group Size' bytes
			//for each SubGroup END
		
		//---------END-CORE-BPARAMETERGROUP-STRUCT--------------
	*/
	//13 guaranteed bytes, variable after that.
	ssize_t size = 13;

	if(mFlags != 0)
	{
		size += 4;
	}
	
	if(mName != NULL)
	{
		size += min_c(strlen(mName),255);
	}
	
	int i;
	int limit;
	
	limit = mControls->CountItems();
	for(i = 0; i < limit; i++)
	{
		BParameter *CurrentParameter = static_cast<BParameter *>(mControls->ItemAt(i));
		if(CurrentParameter != NULL)
		{
			//overhead for each parameter flattened
			size += 16 + CurrentParameter->FlattenedSize();
		}
	}

	limit = mGroups->CountItems();
	for(i = 0; i < limit; i++)
	{
		BParameterGroup *CurrentGroup = static_cast<BParameterGroup *>(mGroups->ItemAt(i));
		if(CurrentGroup != NULL)
		{
			//overhead for each group flattened
			size += 16 + CurrentGroup->FlattenedSize();
		}
	}

	return size;
}


status_t
BParameterGroup::Flatten(void *buffer, ssize_t size) const
{
	CALLED();

	if (buffer == NULL) {
		ERROR("BParameterGroup::Flatten buffer is NULL\n");
		return B_NO_INIT;
	}

	// NOTICE: It is important that this value is the size returned by
	// BParameterGroup::FlattenedSize, not by a descendent's override of this method.
	ssize_t actualSize = BParameterGroup::FlattenedSize();
	if (size < actualSize) {
		ERROR("BParameterGroup::Flatten size to small\n");
		return B_NO_MEMORY;
	}

	if (mFlags != 0) {
		write_to_buffer<int32>(&buffer, kBufferGroupMagic);
		write_to_buffer<uint32>(&buffer, mFlags);
	} else
		write_to_buffer<int32>(&buffer, kBufferGroupMagicNoFlags);

	write_string_to_buffer(&buffer, mName);

	int32 count = mControls ? mControls->CountItems() : 0;
	write_to_buffer<int32>(&buffer, count);

	for (int32 i = 0; i < count; i++) {
		BParameter *parameter = static_cast<BParameter *>(mControls->ItemAt(i));
		if (parameter == NULL) {
			ERROR("BParameterGroup::Flatten(): NULL parameter\n");
			continue;
		}

		write_to_buffer<BParameter *>(&buffer, parameter);
		write_to_buffer<BParameter::media_parameter_type>(&buffer, parameter->Type());

		// flatten parameter into this buffer

		ssize_t parameterSize = parameter->FlattenedSize();
		write_to_buffer<ssize_t>(&buffer, parameterSize);

		status_t status = parameter->Flatten(buffer, parameterSize);
			// we have only that much bytes left to write in this buffer
		if (status < B_OK)
			return status;

		skip_in_buffer(&buffer, parameterSize);
	}

	count = mGroups ? mGroups->CountItems() : 0;
	write_to_buffer<int32>(&buffer, count);

	for (int32 i = 0; i < count; i++) {
		BParameterGroup *group = static_cast<BParameterGroup *>(mGroups->ItemAt(i));
		if (group == NULL) {
			ERROR("BParameterGroup::Flatten(): NULL group\n");
			continue;
		}

		write_to_buffer<BParameterGroup *>(&buffer, group);
		write_to_buffer<type_code>(&buffer, group->TypeCode());

		// flatten sub group into this buffer

		ssize_t groupSize = group->FlattenedSize();
		write_to_buffer<ssize_t>(&buffer, groupSize);

		status_t status = group->Flatten(buffer, groupSize);
			// we have only that much bytes left to write in this buffer
		if (status < B_OK)
			return status;

		skip_in_buffer(&buffer, groupSize);
	}

	return B_OK;
}


bool
BParameterGroup::AllowsTypeCode(type_code code) const
{
	return code == TypeCode();
}


status_t
BParameterGroup::Unflatten(type_code code, const void *buffer, ssize_t size)
{
	CALLED();

	if (!AllowsTypeCode(code)) {
		ERROR("BParameterGroup::Unflatten() wrong type code\n");
		return B_BAD_TYPE;
	}

	if (buffer == NULL) {
		ERROR("BParameterGroup::Unflatten() buffer is NULL\n");
		return B_NO_INIT;
	}

	// if the buffer is smaller than the size needed to read the
	// signature field, then there is a problem
	if (size < static_cast<ssize_t>(sizeof(int32))) {
		ERROR("BParameterGroup::Unflatten() size to small\n");
		return B_ERROR;
	}

	const void *bufferStart = buffer;
		// used to compute the rest length of the buffer when needed

	uint32 magic = read_from_buffer<uint32>(&buffer);
	bool isSwapped = false;

	if (magic == B_SWAP_INT32(kBufferGroupMagic)
		|| magic == B_SWAP_INT32(kBufferGroupMagicNoFlags)) {
		isSwapped = true;
		magic = B_SWAP_INT32(magic);
	}

	if (magic == kBufferGroupMagic)
		mFlags = read_from_buffer_swap32<int32>(&buffer, isSwapped);
	else if (magic == kBufferGroupMagicNoFlags)
		mFlags = 0;
	else
		return B_BAD_TYPE;

	if (read_string_from_buffer(&buffer, &mName,
			size - static_cast<ssize_t>((uint8 *)buffer - (uint8 *)bufferStart)) < B_OK)
		return B_BAD_VALUE;

	// Currently disabled since it's not really used -- axeld.
#if 0
	//Clear all existing parameters/subgroups
	int i;
	if(mControls != NULL)
	{
		for(i = 0; i < mControls->CountItems(); i++)
		{
			BParameter *CurrentItem = static_cast<BParameter *>(mControls->ItemAt(i));
			if(CurrentItem != NULL)
			{
				delete CurrentItem;
			}
		}
		mControls->MakeEmpty();
	}
	else
	{
		mControls = new BList();
	}
	
	if(mGroups != NULL)
	{
		for(i = 0; i < mGroups->CountItems(); i++)
		{
			BParameterGroup *CurrentItem = static_cast<BParameterGroup *>(mGroups->ItemAt(i));
			if(CurrentItem != NULL)
			{
				delete CurrentItem;
			}
		}
		mGroups->MakeEmpty();
	}
	else
	{
		mGroups = new BList();
	}
#endif

	// unflatten parameter list

	int32 count = read_from_buffer_swap32<int32>(&buffer, isSwapped);
	if (count < 0 || count * kAdditionalParameterSize > size_left(size, bufferStart, buffer))
		return B_BAD_VALUE;

	for (int32 i = 0; i < count; i++) {
		// make sure we can read as many bytes
		if (size_left(size, bufferStart, buffer) < 12)
			return B_BAD_VALUE;

		BParameter *oldPointer = read_from_buffer_swap32<BParameter *>(&buffer, isSwapped);
		BParameter::media_parameter_type mediaType =
			read_from_buffer_swap32<BParameter::media_parameter_type>(&buffer, isSwapped);

		ssize_t parameterSize = read_from_buffer_swap32<ssize_t>(&buffer, isSwapped);
		if (parameterSize > size_left(size, bufferStart, buffer))
			return B_BAD_VALUE;

		BParameter *parameter = MakeControl(mediaType);
		if (parameter == NULL) {
			ERROR("BParameterGroup::Unflatten(): MakeControl() failed\n");
			return B_ERROR;
		}

		status_t status = parameter->Unflatten(parameter->TypeCode(), buffer, parameterSize);
		if (status < B_OK) {
			ERROR("BParameterGroup::Unflatten(): parameter->Unflatten() failed\n");
			delete parameter;
			return status;
		}

		skip_in_buffer(&buffer, parameterSize);

		// add the item to the list
		parameter->mGroup = this;
		parameter->mWeb = mWeb;
		mControls->AddItem(parameter);

		// add it's old pointer value to the RefFix list kept by the owner web
		if (mWeb != NULL)
			mWeb->AddRefFix(oldPointer, parameter);
	}

	// unflatten sub groups

	count = read_from_buffer_swap32<int32>(&buffer, isSwapped);
	if (count < 0 || count * kAdditionalParameterGroupSize > size_left(size, bufferStart, buffer))
		return B_BAD_VALUE;

	for (int32 i = 0; i < count; i++) {
		// make sure we can read as many bytes
		if (size_left(size, bufferStart, buffer) < 12)
			return B_BAD_VALUE;

		BParameterGroup *oldPointer = read_from_buffer_swap32<BParameterGroup *>(&buffer, isSwapped);
		type_code type = read_from_buffer_swap32<type_code>(&buffer, isSwapped);

		ssize_t groupSize = read_from_buffer_swap32<ssize_t>(&buffer, isSwapped);
		if (groupSize > size_left(size, bufferStart, buffer))
			return B_BAD_VALUE;

		BParameterGroup *group = new BParameterGroup(mWeb, "sub-unnamed");
		if (group == NULL) {
			ERROR("BParameterGroup::Unflatten(): MakeGroup() failed\n");
			return B_ERROR;
		}

		status_t status = group->Unflatten(type, buffer, groupSize);
		if (status != B_OK) {
			ERROR("BParameterGroup::Unflatten(): group->Unflatten() failed\n");
			delete group;
			return status;
		}

		skip_in_buffer(&buffer, groupSize);

		mGroups->AddItem(group);

		// add it's old pointer value to the RefFix list kept by the owner web
		if (mWeb != NULL)
			mWeb->AddRefFix(oldPointer, group);
	}

	return B_OK;
}

/*************************************************************
 * private BParameterGroup
 *************************************************************/

/*
// unimplemented
BParameterGroup::BParameterGroup()
BParameterGroup::BParameterGroup(const BParameterGroup &clone)
BParameterGroup &BParameterGroup::operator=(const BParameterGroup &clone)
*/


/** Creates an uninitialized parameter of the specified type.
 *	Unlike the BParameterGroup::MakeXXXParameter() type of methods, this
 *	method does not add the parameter to this group automatically.
 */

BParameter *
BParameterGroup::MakeControl(int32 type)
{
	CALLED();

	switch (type) {
		case BParameter::B_NULL_PARAMETER:
			return new BNullParameter(-1, B_MEDIA_NO_TYPE, NULL, NULL, NULL);

		case BParameter::B_DISCRETE_PARAMETER:
			return new BDiscreteParameter(-1, B_MEDIA_NO_TYPE, NULL, NULL, NULL);

		case BParameter::B_CONTINUOUS_PARAMETER:
			return new BContinuousParameter(-1, B_MEDIA_NO_TYPE, NULL, NULL, NULL, NULL, 0, 0, 0);

		default:
			ERROR("BParameterGroup::MakeControl unknown type %ld\n", type);
			return NULL;
	}
}


//	#pragma mark -

/*************************************************************
 * public BParameter
 *************************************************************/


BParameter::media_parameter_type
BParameter::Type() const
{
	return mType;
}


BParameterWeb *
BParameter::Web() const
{
	return mWeb;
}


BParameterGroup *
BParameter::Group() const
{
	return mGroup;
}


const char *
BParameter::Name() const
{
	return mName;
}


const char *
BParameter::Kind() const
{
	return mKind;
}


const char *
BParameter::Unit() const
{
	return mUnit;
}


int32
BParameter::ID() const
{
	return mID;
}


void
BParameter::SetFlags(uint32 flags)
{
	mFlags = flags;
}


uint32
BParameter::Flags() const
{
	return mFlags;
}


status_t
BParameter::GetValue(void *buffer, size_t *_ioSize, bigtime_t *_when)
{
	CALLED();

	if (buffer == NULL || _ioSize == NULL)
		return B_BAD_VALUE;

	size_t ioSize = *_ioSize;
	if (ioSize <= 0)
		return B_NO_MEMORY;

	if (mWeb == NULL) {
		ERROR("BParameter::GetValue: no parent BParameterWeb\n");
		return B_NO_INIT;
	}

	media_node node = mWeb->Node();
	if (IS_INVALID_NODE(node)) {
		ERROR("BParameter::GetValue: the parent BParameterWeb is not assigned to a BMediaNode\n");
		return B_NO_INIT;
	}

	controllable_get_parameter_data_request request;
	controllable_get_parameter_data_reply reply;

	area_id area;
	void *data;
	if (ioSize > MAX_PARAMETER_DATA) {
		// create an area if large data needs to be transfered
		area = create_area("get parameter data", &data, B_ANY_ADDRESS, ROUND_UP_TO_PAGE(ioSize),
			B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
		if (area < B_OK) {
			ERROR("BParameter::GetValue can't create area of %ld bytes\n", ioSize);
			return B_NO_MEMORY;
		}
	} else {
		area = -1;
		data = reply.rawdata;
	}

	request.parameter_id = mID;
	request.requestsize = ioSize;
	request.area = area;

	status_t status = QueryPort(node.port, CONTROLLABLE_GET_PARAMETER_DATA, &request,
		sizeof(request), &reply, sizeof(reply));
	if (status == B_OK) {
		// we don't want to copy more than the buffer provides
		if (reply.size < ioSize)
			ioSize = reply.size;

		memcpy(buffer, data, ioSize);

		// store reported values

		*_ioSize = reply.size;
		if (_when != NULL)
			*_when = reply.last_change;
	} else
		ERROR("BParameter::GetValue querying node failed: %s\n", strerror(status));

	if (area >= B_OK)
		delete_area(area);

	return status;
}


status_t
BParameter::SetValue(const void *buffer, size_t size, bigtime_t when)
{
	CALLED();

	controllable_set_parameter_data_request request;
	controllable_set_parameter_data_reply reply;
	media_node node;
	area_id area;
	status_t rv;
	void *data;

	if (buffer == 0)
		return B_BAD_VALUE;
	if (size <= 0)
		return B_NO_MEMORY;
		
	if (mWeb == 0) {
		ERROR("BParameter::SetValue: no parent BParameterWeb\n");
		return B_NO_INIT;
	}
	
	node = mWeb->Node();
	if (IS_INVALID_NODE(node)) {
		ERROR("BParameter::SetValue: the parent BParameterWeb is not assigned to a BMediaNode\n");
		return B_NO_INIT;
	}
	
	if (size > MAX_PARAMETER_DATA) {
		// create an area if large data needs to be transfered
		area = create_area("set parameter data", &data, B_ANY_ADDRESS, ROUND_UP_TO_PAGE(size), B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
		if (area < B_OK) {
			ERROR("BParameter::SetValue can't create area of %ld bytes\n", size);
			return B_NO_MEMORY;
		}
	} else {
		area = -1;
		data = request.rawdata;
	}

	memcpy(data, buffer, size);
	request.parameter_id = mID;
	request.when = when;
	request.area = area;
	request.size = size;

	rv = QueryPort(node.port, CONTROLLABLE_SET_PARAMETER_DATA, &request, sizeof(request), &reply, sizeof(reply));
	if (rv != B_OK)
		ERROR("BParameter::SetValue querying node failed\n");

	if (area != -1)
		delete_area(area);
	
	return rv;
}


int32
BParameter::CountChannels()
{
	return mChannels;
}


void
BParameter::SetChannelCount(int32 channel_count)
{
	mChannels = channel_count;
}


media_type
BParameter::MediaType()
{
	return mMediaType;
}


void
BParameter::SetMediaType(media_type m_type)
{
	mMediaType = m_type;
}


int32
BParameter::CountInputs()
{
	ASSERT(mInputs != NULL);

	return mInputs->CountItems();
}


BParameter *
BParameter::InputAt(int32 index)
{
	ASSERT(mInputs != NULL);
	
	return static_cast<BParameter *>(mInputs->ItemAt(index));
}


void
BParameter::AddInput(BParameter *input)
{
	CALLED();

	// BeBook has this method returning a status value,
	// but it should be updated
	if (input == NULL)
		return;

	ASSERT(mInputs != NULL);

	if (mInputs->HasItem(input)) {
		// if already in input list, don't duplicate.
		return;
	}

	mInputs->AddItem(input);
	input->AddOutput(this);
}


int32
BParameter::CountOutputs()
{
	ASSERT(mOutputs != NULL);

	return mOutputs->CountItems();
}


BParameter *
BParameter::OutputAt(int32 index)
{
	ASSERT(mOutputs != NULL);

	return static_cast<BParameter *>(mOutputs->ItemAt(index));
}


void
BParameter::AddOutput(BParameter *output)
{
	CALLED();

	// BeBook has this method returning a status value,
	// but it should be updated
	if (output == NULL)
		return;

	ASSERT(mOutputs != NULL);

	if (mOutputs->HasItem(output)) {
		// if already in output list, don't duplicate.
		return;
	}

	mOutputs->AddItem(output);
	output->AddInput(this);
}


bool
BParameter::IsFixedSize() const
{
	return false;
}


type_code
BParameter::TypeCode() const
{
	return B_MEDIA_PARAMETER_TYPE;
}


ssize_t
BParameter::FlattenedSize() const
{
	CALLED();
	/*
		?? (0x02040607): 4 bytes
		BParameter Struct Size (in bytes): 4 bytes
		ID: 4 bytes
		Name String Length: 1 byte (??)
			Name String: 'Name String Length' bytes
		Kind String Length: 1 byte (??)
			Kind String: 'Kind String Length' bytes
		Unit String Length: 1 byte (??)
			Unit String: 'Unit String Length' bytes
		Inputs Count: 4 bytes
			Inputs (pointers): ('Inputs Count')*4 bytes
		Outputs Count: 4 bytes
			Outputs (pointers): ('Outputs Count')*4 bytes
		Media Type: 4 bytes
		ChannelCount: 4 bytes
		Flags: 4 bytes
	*/
	//35 bytes are guaranteed, after that, add the variable length parts.
	ssize_t size = 35;

	if (mName != NULL)
		size += strlen(mName);
	if (mKind != NULL)
		size += strlen(mKind);
	if (mUnit != NULL)
		size += strlen(mUnit);

	if (mInputs != NULL)
		size += mInputs->CountItems() * sizeof(BParameter *);

	if (mOutputs != NULL)
		size += mOutputs->CountItems() * sizeof(BParameter *);

	return size;
}


status_t
BParameter::Flatten(void *buffer, ssize_t size) const
{
	CALLED();

	if (buffer == NULL) {
		ERROR("BParameter::Flatten buffer is NULL\n");
		return B_NO_INIT;
	}

	// NOTICE: It is important that this value is the size returned by
	// BParameter::FlattenedSize(), not by a descendent's override of this method.
	ssize_t actualSize = BParameter::FlattenedSize();
	if (size < actualSize) {
		ERROR("BParameter::Flatten(): size too small\n");
		return B_NO_MEMORY;
	}

	write_to_buffer<uint32>(&buffer, kParameterMagic);
	write_to_buffer<ssize_t>(&buffer, actualSize);
	write_to_buffer<int32>(&buffer, mID);

	write_string_to_buffer(&buffer, mName);
	write_string_to_buffer(&buffer, mKind);
	write_string_to_buffer(&buffer, mUnit);

	// flatten and write the list of inputs
	ssize_t count = mInputs ? mInputs->CountItems() : 0;
	write_to_buffer<ssize_t>(&buffer, count);

	if (count > 0) {
		memcpy(buffer, mInputs->Items(), sizeof(BParameter *) * count);
		skip_in_buffer(&buffer, sizeof(BParameter *) * count);
	}

	// flatten and write the list of outputs
	count = mOutputs ? mOutputs->CountItems() : 0;
	write_to_buffer<ssize_t>(&buffer, count);

	if (count > 0) {
		memcpy(buffer, mOutputs->Items(), sizeof(BParameter *) * count);
		skip_in_buffer(&buffer, sizeof(BParameter *) * count);
	}

	write_to_buffer<media_type>(&buffer, mMediaType);
	write_to_buffer<int32>(&buffer, mChannels);
	write_to_buffer<uint32>(&buffer, mFlags);

	return B_OK;
}


bool
BParameter::AllowsTypeCode(type_code code) const
{
	return (code == TypeCode());
}


status_t
BParameter::Unflatten(type_code code, const void *buffer, ssize_t size)
{
	CALLED();

	if (!AllowsTypeCode(code)) {
		ERROR("BParameter::Unflatten(): wrong type code\n");
		return B_BAD_TYPE;
	}

	if (buffer == NULL) {
		ERROR("BParameter::Unflatten(): buffer is NULL\n");
		return B_NO_INIT;
	}

	// if the buffer is smaller than the size needed to read the
	// signature and struct size fields, then there is a problem
	if (size < static_cast<ssize_t>(sizeof(int32) + sizeof(ssize_t))) {
		ERROR("BParameter::Unflatten() size too small\n");
		return B_BAD_VALUE;
	}

	const void *bufferStart = buffer;

	// check magic

	uint32 magic = read_from_buffer<uint32>(&buffer);
	if (magic == B_SWAP_INT32(kParameterMagic))
		mSwapDetected = true;
	else if (magic == kParameterMagic)
		mSwapDetected = false;
	else {
		ERROR("BParameter::Unflatten(): bad magic\n");
		return B_BAD_TYPE;
	}

	ssize_t parameterSize = read_from_buffer_swap32<ssize_t>(&buffer, mSwapDetected);
	if (parameterSize > size) {
		ERROR("BParameter::Unflatten(): buffer too small (%ld > %ld)\n", parameterSize, size);
		return B_BAD_VALUE;
	}

	//if the struct doesn't meet the minimum size for
	//a flattened BParameter, then return an error.
	//MinFlattenedParamSize = 
	//ID (4 bytes)
	//Name String Length (1 byte)
	//Kind String Length (1 byte)
	//Unit String Length (1 byte)
	//Inputs Count (4 bytes)
	//Outputs Count (4 bytes)
	//Media Type (4 bytes)
	//Channel Count (4 bytes)
	//Flags (4 bytes)
	//TOTAL: 27 bytes
	const ssize_t MinFlattenedParamSize(27);
	if (parameterSize < MinFlattenedParamSize) {
		ERROR("BParameter::Unflatten out of memory (2)\n");
		return B_ERROR;
	}

	mID = read_from_buffer_swap32<int32>(&buffer, mSwapDetected);

	if (read_string_from_buffer(&buffer, &mName, size_left(size, bufferStart, buffer)) < B_OK
		|| read_string_from_buffer(&buffer, &mKind, size_left(size, bufferStart, buffer)) < B_OK	
		|| read_string_from_buffer(&buffer, &mUnit, size_left(size, bufferStart, buffer)) < B_OK)	
		return B_NO_MEMORY;

	// read the list of inputs

	// it will directly add the pointers in the flattened message to the list;
	// these will be fixed to point to the real inputs/outputs later in FixRefs()

	int32 count = read_from_buffer_swap32<int32>(&buffer, mSwapDetected);	

	if (mInputs == NULL)
		mInputs = new BList();
	else
		mInputs->MakeEmpty();

	for (int32 i = 0; i < count; i++) {
		mInputs->AddItem(read_from_buffer_swap32<BParameter * const>(&buffer, mSwapDetected));
	}

	// read the list of outputs

	count = read_from_buffer_swap32<int32>(&buffer, mSwapDetected);

	if (mOutputs == NULL)
		mOutputs = new BList();
	else
		mOutputs->MakeEmpty();

	for (int32 i = 0; i < count; i++) {
		mOutputs->AddItem(read_from_buffer_swap32<BParameter * const>(&buffer, mSwapDetected));
	}

	mMediaType = read_from_buffer_swap32<media_type>(&buffer, mSwapDetected);
	mChannels = read_from_buffer_swap32<int32>(&buffer, mSwapDetected);
	mFlags = read_from_buffer_swap32<uint32>(&buffer, mSwapDetected);

	return B_OK;
}


/*************************************************************
 * private BParameter
 *************************************************************/


BParameter::BParameter(int32 id, media_type mediaType, media_parameter_type type,
	BParameterWeb *web, const char *name, const char *kind, const char *unit)
	:
	mID(id),
	mType(type),
	mWeb(web),
	mGroup(NULL),
	mSwapDetected(true),
	mMediaType(mediaType),
	mChannels(1),
	mFlags(0)
{
	CALLED();

	mName = strndup(name, 256);
	mKind = strndup(kind, 256);
	mUnit = strndup(unit, 256);

	// create empty input/output lists
	mInputs = new BList();
	mOutputs = new BList();
}


BParameter::~BParameter()
{
	CALLED();

	// don't worry about the mWeb/mGroup properties, you don't need
	// to remove yourself from a web/group since the only way in which
	// a parameter is destroyed is when the owner web/group destroys it

	free(mName);
	free(mKind);
	free(mUnit);

	delete mInputs;
	delete mOutputs;
	
	mName = NULL; mKind = NULL; mUnit = NULL; mInputs = NULL; mOutputs = NULL;
}


/** Replaces references to items in the old list with the corresponding
 *	items in the updated list. The references are replaced in the input
 *	and output lists.
 *	This is called by BParameterWeb::Unflatten().
 */

void
BParameter::FixRefs(BList &old, BList &updated)
{
	CALLED();
	ASSERT(mInputs != NULL);
	ASSERT(mOutputs != NULL);

	// update inputs

	void **items = static_cast<void **>(mInputs->Items());
	int32 count = mInputs->CountItems();

	for (int32 i = 0; i < count; i++) {
		int32 index = old.IndexOf(items[i]);
		if (index >= 0)
			items[i] = updated.ItemAt(index);
		else {
			ERROR("BParameter::FixRefs(): No mapping found for input");
			items[i] = NULL;
		}
	}

	// remove all NULL inputs (those which couldn't be mapped)

	for (int32 i = count; i-- > 0;) {
		if (items[i] == NULL)
			mInputs->RemoveItem(i);
	}

	// update outputs

	items = static_cast<void **>(mOutputs->Items());
	count = mOutputs->CountItems();

	for (int32 i = 0; i < count; i++) {
		int32 index = old.IndexOf(items[i]);
		if (index >= 0)
			items[i] = updated.ItemAt(index);
		else {
			ERROR("BParameter::FixRefs(): No mapping found for output");
			items[i] = NULL;
		}
	}

	// remove all NULL outputs (those which couldn't be mapped)
	
	for (int32 i = count; i-- > 0;) {
		if (items[i] == NULL)
			mOutputs->RemoveItem(i);
	}
}


//	#pragma mark -

/*************************************************************
 * public BContinuousParameter
 *************************************************************/


type_code
BContinuousParameter::ValueType()
{
	return B_FLOAT_TYPE;
}


float
BContinuousParameter::MinValue()
{
	return mMinimum;
}


float
BContinuousParameter::MaxValue()
{
	return mMaximum;
}


float
BContinuousParameter::ValueStep()
{
	return mStepping;
}


void
BContinuousParameter::SetResponse(int resp, float factor, float offset)
{
	mResponse = static_cast<response>(resp);
	mFactor = factor;
	mOffset = offset;
}


void
BContinuousParameter::GetResponse(int *resp, float *factor, float *offset)
{
	if (resp != NULL)
		*resp = mResponse;
	if (factor != NULL)
		*factor = mFactor;
	if (offset != NULL)
		*offset = mOffset;
}


ssize_t
BContinuousParameter::FlattenedSize() const
{
	CALLED();

	// only adds a fixed amount of bytes
	return BParameter::FlattenedSize() + kAdditionalContinuousParameterSize;
}


status_t
BContinuousParameter::Flatten(void *buffer, ssize_t size) const
{
	CALLED();

	if (buffer == NULL) {
		ERROR("BContinuousParameter::Flatten(): buffer is NULL\n");
		return B_NO_INIT;
	}

	ssize_t parameterSize = BParameter::FlattenedSize();
	if (size < (parameterSize + kAdditionalContinuousParameterSize)) {
		ERROR("BContinuousParameter::Flatten(): size to small\n");
		return B_NO_MEMORY;
	}

	status_t status = BParameter::Flatten(buffer, size);
	if (status != B_OK) {
		ERROR("BContinuousParameter::Flatten(): BParameter::Flatten() failed\n");
		return status;
	}

	// add our data to the general flattened BParameter

	skip_in_buffer(&buffer, parameterSize);

	write_to_buffer<float>(&buffer, mMinimum);
	write_to_buffer<float>(&buffer, mMaximum);
	write_to_buffer<float>(&buffer, mStepping);
	write_to_buffer<response>(&buffer, mResponse);
	write_to_buffer<float>(&buffer, mFactor);
	write_to_buffer<float>(&buffer, mOffset);

	return B_OK;
}


status_t
BContinuousParameter::Unflatten(type_code code, const void *buffer, ssize_t size)
{
	CALLED();

	// we try to check if the buffer size is long enough to hold an object
	// as early as possible.

	if (!AllowsTypeCode(code)) {
		ERROR("BContinuousParameter::Unflatten wrong type code\n");
		return B_BAD_TYPE;
	}

	if (buffer == NULL) {
		ERROR("BContinuousParameter::Unflatten buffer is NULL\n");
		return B_NO_INIT;
	}

	// if the buffer is smaller than the size needed to read the
	// signature and struct size fields, then there is a problem
	if (size < static_cast<ssize_t>(sizeof(int32) + sizeof(ssize_t))) {
		ERROR("BContinuousParameter::Unflatten size too small\n");
		return B_ERROR;
	}

	status_t status = BParameter::Unflatten(code, buffer, size);
	if (status != B_OK) {
		ERROR("BContinuousParameter::Unflatten(): BParameter::Unflatten failed\n");
		return status;
	}

	ssize_t parameterSize = BParameter::FlattenedSize();
	skip_in_buffer(&buffer, parameterSize);

	if (size < (parameterSize + kAdditionalContinuousParameterSize)) {
		ERROR("BContinuousParameter::Unflatten(): buffer too small\n");
		return B_BAD_VALUE;
	}

	mMinimum = read_from_buffer_swap32<float>(&buffer, SwapOnUnflatten());
	mMaximum = read_from_buffer_swap32<float>(&buffer, SwapOnUnflatten());
	mStepping = read_from_buffer_swap32<float>(&buffer, SwapOnUnflatten());
	mResponse = read_from_buffer_swap32<response>(&buffer, SwapOnUnflatten());
	mFactor = read_from_buffer_swap32<float>(&buffer, SwapOnUnflatten());
	mOffset = read_from_buffer_swap32<float>(&buffer, SwapOnUnflatten());

	return B_OK;
}


/*************************************************************
 * private BContinuousParameter
 *************************************************************/


BContinuousParameter::BContinuousParameter(int32 id, media_type m_type,
	BParameterWeb *web, const char *name, const char *kind, const char *unit,
	float minimum, float maximum, float stepping)
	:	BParameter(id, m_type, B_CONTINUOUS_PARAMETER, web, name, kind, unit),
	mMinimum(minimum), mMaximum(maximum), mStepping(stepping),
	mResponse(B_LINEAR), mFactor(1.0), mOffset(0.0)
{
	CALLED();
}


BContinuousParameter::~BContinuousParameter()
{
	CALLED();
}


//	#pragma mark -

/*************************************************************
 * public BDiscreteParameter
 *************************************************************/


type_code
BDiscreteParameter::ValueType()
{
	return B_INT32_TYPE;
}


int32
BDiscreteParameter::CountItems()
{
	ASSERT(mValues != NULL);

	return mValues->CountItems();
}


const char *
BDiscreteParameter::ItemNameAt(int32 index)
{
	ASSERT(mSelections != NULL);

	return reinterpret_cast<const char *>(mSelections->ItemAt(index));
}


int32
BDiscreteParameter::ItemValueAt(int32 index)
{
	ASSERT(mValues != NULL);

	int32 *item = static_cast<int32 *>(mValues->ItemAt(index));
	if (item == NULL)
		return 0;

	return *item;
}


status_t
BDiscreteParameter::AddItem(int32 value, const char *name)
{
	CALLED();
	//TRACE("\tthis = %p, value = %ld, name = \"%s\"\n", this, value, name);
	ASSERT(mValues != NULL);
	ASSERT(mSelections != NULL);

	int32 *valueCopy = new int32(value);
	char *nameCopy = strndup(name, 256);
	if (name != NULL && nameCopy == NULL)
		return B_NO_MEMORY;

	if (!mValues->AddItem(valueCopy)
		|| !mSelections->AddItem(nameCopy))
		return B_NO_MEMORY;

	return B_OK;
}


status_t
BDiscreteParameter::MakeItemsFromInputs()
{
	CALLED();
	ASSERT(mValues != NULL);
	ASSERT(mSelections != NULL);
	ASSERT(mInputs != NULL);

	int32 count = mInputs->CountItems();
	for(int32 i = 0; i < count; i++) {
		BParameter *parameter = static_cast<BParameter *>(mInputs->ItemAt(i));
		AddItem(i, parameter->Name());
	}

	return B_OK;
}


status_t
BDiscreteParameter::MakeItemsFromOutputs()
{
	CALLED();
	ASSERT(mValues != NULL);
	ASSERT(mSelections != NULL);
	ASSERT(mOutputs != NULL);

	int32 count = mOutputs->CountItems();
	for(int32 i = 0; i < count; i++) {
		BParameter *parameter = static_cast<BParameter *>(mOutputs->ItemAt(i));
		AddItem(i, parameter->Name());
	}

	return B_OK;
}


void
BDiscreteParameter::MakeEmpty()
{
	CALLED();
	ASSERT(mValues != NULL);
	ASSERT(mSelections != NULL);

	for (int32 i = mValues->CountItems(); i-- > 0;) {
		delete static_cast<int32 *>(mValues->ItemAt(i));
	}
	mValues->MakeEmpty();

	for(int32 i = mSelections->CountItems(); i-- > 0;) {
		free(static_cast<char *>(mSelections->ItemAt(i)));
	}
	mSelections->MakeEmpty();
}


ssize_t
BDiscreteParameter::FlattenedSize() const
{
	CALLED();

	ssize_t size = BParameter::FlattenedSize() + kAdditionalDiscreteParameterSize;

	int32 count = mValues ? mValues->CountItems() : 0;
	for (int32 i = 0; i < count; i++) {
		char *selection = static_cast<char *>(mSelections->ItemAt(i));

		if (selection != NULL)
			size += min_c(strlen(selection), 255);

		size += 5;
			// string length + value
	}

	return size;
}


status_t
BDiscreteParameter::Flatten(void *buffer, ssize_t size) const
{
	CALLED();

	if (buffer == NULL) {
		ERROR("BDiscreteParameter::Flatten(): buffer is NULL\n");
		return B_NO_INIT;
	}

	ssize_t parameterSize = BParameter::FlattenedSize();

	if (size < FlattenedSize()) {
		ERROR("BDiscreteParameter::Flatten(): size too small\n");
		return B_NO_MEMORY;
	}

	status_t status = BParameter::Flatten(buffer, size);
	if (status != B_OK) {
		ERROR("BDiscreteParameter::Flatten(): BParameter::Flatten failed\n");
		return status;
	}

	skip_in_buffer(&buffer, parameterSize);

	int32 count = mValues ? mValues->CountItems() : 0;
	write_to_buffer<int32>(&buffer, count);

	// write out all value/name pairs
	for (int32 i = 0; i < count; i++) {
		const char *selection = static_cast<char *>(mSelections->ItemAt(i));
		const int32 *value = static_cast<int32 *>(mValues->ItemAt(i));

		write_string_to_buffer(&buffer, selection);
		write_to_buffer<int32>(&buffer, value ? *value : 0);
	}

	return B_OK;
}


status_t
BDiscreteParameter::Unflatten(type_code code, const void *buffer, ssize_t size)
{
	CALLED();

	if (!AllowsTypeCode(code)) {
		ERROR("BDiscreteParameter::Unflatten(): bad type code\n");
		return B_BAD_TYPE;
	}

	if (buffer == NULL) {
		ERROR("BDiscreteParameter::Unflatten(): buffer is NULL\n");
		return B_NO_INIT;
	}

	// if the buffer is smaller than the size needed to read the
	// signature and struct size fields, then there is a problem
	if (size < static_cast<ssize_t>(sizeof(int32) + sizeof(ssize_t))) {
		ERROR("BDiscreteParameter::Unflatten(): size too small\n");
		return B_ERROR;
	}

	const void *bufferStart = buffer;

	status_t status = BParameter::Unflatten(code, buffer, size);
	if (status != B_OK) {
		ERROR("BDiscreteParameter::Unflatten(): BParameter::Unflatten failed\n");
		return status;
	}

	ssize_t parameterSize = BParameter::FlattenedSize();
	skip_in_buffer(&buffer, parameterSize);

	if (size < (parameterSize + kAdditionalDiscreteParameterSize)) {
		ERROR("BDiscreteParameter::Unflatten(): buffer too small\n");
		return B_BAD_VALUE;
	}

	int32 count = read_from_buffer_swap32<int32>(&buffer, SwapOnUnflatten());

	// clear any existing name/value pairs
	MakeEmpty();

	for (int32 i = 0; i < count; i++) {
		char *name;
		if (read_string_from_buffer(&buffer, &name, size_left(size, bufferStart, buffer)) < B_OK)
			return B_BAD_DATA;

		if (size_left(size, bufferStart, buffer) < (int)sizeof(int32))
			return B_BAD_DATA;

		int32 value = read_from_buffer_swap32<int32>(&buffer, SwapOnUnflatten());

		AddItem(value, name);
	}

	return B_OK;
}


/*************************************************************
 * private BDiscreteParameter
 *************************************************************/


BDiscreteParameter::BDiscreteParameter(int32 id, media_type mediaType,
	BParameterWeb *web, const char *name, const char *kind)
	:	BParameter(id, mediaType, B_DISCRETE_PARAMETER, web, name, kind, NULL)
{
	CALLED();

	mSelections = new BList();
	mValues = new BList();
}


BDiscreteParameter::~BDiscreteParameter()
{
	CALLED();

	MakeEmpty();

	delete mSelections;
	delete mValues;

	mSelections = NULL; mValues = NULL;
}


//	#pragma mark -

/*************************************************************
 * public BNullParameter
 *************************************************************/


type_code
BNullParameter::ValueType()
{
	// NULL parameters have no value type
	return 0;
}


ssize_t
BNullParameter::FlattenedSize() const
{
	return BParameter::FlattenedSize();
}


status_t
BNullParameter::Flatten(void *buffer, ssize_t size) const
{
	return BParameter::Flatten(buffer, size);
}


status_t
BNullParameter::Unflatten(type_code code, const void *buffer, ssize_t size)
{
	return BParameter::Unflatten(code, buffer, size);
}


/*************************************************************
 * private BNullParameter
 *************************************************************/


BNullParameter::BNullParameter(int32 id, media_type mediaType, BParameterWeb *web,
	const char *name, const char *kind)
	: BParameter(id, mediaType, B_NULL_PARAMETER, web, name, kind, NULL)
{
}


BNullParameter::~BNullParameter()
{
}


//	#pragma mark -
//	reserved functions


status_t BParameterWeb::_Reserved_ControlWeb_0(void *) { return B_ERROR; }
status_t BParameterWeb::_Reserved_ControlWeb_1(void *) { return B_ERROR; }
status_t BParameterWeb::_Reserved_ControlWeb_2(void *) { return B_ERROR; }
status_t BParameterWeb::_Reserved_ControlWeb_3(void *) { return B_ERROR; }
status_t BParameterWeb::_Reserved_ControlWeb_4(void *) { return B_ERROR; }
status_t BParameterWeb::_Reserved_ControlWeb_5(void *) { return B_ERROR; }
status_t BParameterWeb::_Reserved_ControlWeb_6(void *) { return B_ERROR; }
status_t BParameterWeb::_Reserved_ControlWeb_7(void *) { return B_ERROR; }

status_t BParameterGroup::_Reserved_ControlGroup_0(void *) { return B_ERROR; }
status_t BParameterGroup::_Reserved_ControlGroup_1(void *) { return B_ERROR; }
status_t BParameterGroup::_Reserved_ControlGroup_2(void *) { return B_ERROR; }
status_t BParameterGroup::_Reserved_ControlGroup_3(void *) { return B_ERROR; }
status_t BParameterGroup::_Reserved_ControlGroup_4(void *) { return B_ERROR; }
status_t BParameterGroup::_Reserved_ControlGroup_5(void *) { return B_ERROR; }
status_t BParameterGroup::_Reserved_ControlGroup_6(void *) { return B_ERROR; }
status_t BParameterGroup::_Reserved_ControlGroup_7(void *) { return B_ERROR; }

status_t BParameter::_Reserved_Control_0(void *) { return B_ERROR; }
status_t BParameter::_Reserved_Control_1(void *) { return B_ERROR; }
status_t BParameter::_Reserved_Control_2(void *) { return B_ERROR; }
status_t BParameter::_Reserved_Control_3(void *) { return B_ERROR; }
status_t BParameter::_Reserved_Control_4(void *) { return B_ERROR; }
status_t BParameter::_Reserved_Control_5(void *) { return B_ERROR; }
status_t BParameter::_Reserved_Control_6(void *) { return B_ERROR; }
status_t BParameter::_Reserved_Control_7(void *) { return B_ERROR; }

status_t BContinuousParameter::_Reserved_ContinuousParameter_0(void *) { return B_ERROR; }
status_t BContinuousParameter::_Reserved_ContinuousParameter_1(void *) { return B_ERROR; }
status_t BContinuousParameter::_Reserved_ContinuousParameter_2(void *) { return B_ERROR; }
status_t BContinuousParameter::_Reserved_ContinuousParameter_3(void *) { return B_ERROR; }
status_t BContinuousParameter::_Reserved_ContinuousParameter_4(void *) { return B_ERROR; }
status_t BContinuousParameter::_Reserved_ContinuousParameter_5(void *) { return B_ERROR; }
status_t BContinuousParameter::_Reserved_ContinuousParameter_6(void *) { return B_ERROR; }
status_t BContinuousParameter::_Reserved_ContinuousParameter_7(void *) { return B_ERROR; }

status_t BDiscreteParameter::_Reserved_DiscreteParameter_0(void *) { return B_ERROR; }
status_t BDiscreteParameter::_Reserved_DiscreteParameter_1(void *) { return B_ERROR; }
status_t BDiscreteParameter::_Reserved_DiscreteParameter_2(void *) { return B_ERROR; }
status_t BDiscreteParameter::_Reserved_DiscreteParameter_3(void *) { return B_ERROR; }
status_t BDiscreteParameter::_Reserved_DiscreteParameter_4(void *) { return B_ERROR; }
status_t BDiscreteParameter::_Reserved_DiscreteParameter_5(void *) { return B_ERROR; }
status_t BDiscreteParameter::_Reserved_DiscreteParameter_6(void *) { return B_ERROR; }
status_t BDiscreteParameter::_Reserved_DiscreteParameter_7(void *) { return B_ERROR; }

status_t BNullParameter::_Reserved_NullParameter_0(void *) { return B_ERROR; }
status_t BNullParameter::_Reserved_NullParameter_1(void *) { return B_ERROR; }
status_t BNullParameter::_Reserved_NullParameter_2(void *) { return B_ERROR; }
status_t BNullParameter::_Reserved_NullParameter_3(void *) { return B_ERROR; }
status_t BNullParameter::_Reserved_NullParameter_4(void *) { return B_ERROR; }
status_t BNullParameter::_Reserved_NullParameter_5(void *) { return B_ERROR; }
status_t BNullParameter::_Reserved_NullParameter_6(void *) { return B_ERROR; }
status_t BNullParameter::_Reserved_NullParameter_7(void *) { return B_ERROR; }


