

//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
//_/_/
//_/_/  This file is part of CMSDK version 2.1.1.260726 (26 July 2026)
//_/_/  
//_/_/  developed by
//_/_/  
//_/_/  Communicative Machines Ltd. 
//_/_/    http://cmlabs.com/
//_/_/  Copyright (c) 2026
//_/_/  
//_/_/  in collaboration with 
//_/_/  
//_/_/  Icelandic Institute for Intelligent Machines ses
//_/_/    Menntavegur 1
//_/_/    101 Reykjavik
//_/_/    ICELAND
//_/_/    http://www.iiim.is/
//_/_/
//_/_/  and is released under the following
//_/_/  
//_/_/  EXTENDED BSD License
//_/_/
//_/_/  Redistribution and use in source and binary forms, with or without
//_/_/  modification, is permitted provided that the following conditions 
//_/_/  are met:
//_/_/
//_/_/  - Redistributions of source code must retain the above copyright 
//_/_/    and collaboration notice, this list of conditions and the following 
//_/_/    disclaimer.
//_/_/
//_/_/  - Redistributions in binary form must reproduce the above copyright 
//_/_/    notice, this list of conditions and the following disclaimer in the 
//_/_/    documentation and/or other materials provided with the distribution.
//_/_/
//_/_/  - Neither the name of its copyright holders nor the names of its 
//_/_/    contributors may be used to endorse or promote products derived from 
//_/_/    this software without specific prior written permission.
//_/_/
//_/_/  - CADIA Clause: The license granted in and to the software under this 
//_/_/    agreement is a limited-use license. The software may not be used in 
//_/_/    furtherance of: (i) intentionally causing bodily injury or severe 
//_/_/    emotional harm to any person; (ii) invading the personal privacy or 
//_/_/    violating the human rights of any person; or (iii) committing or 
//_/_/    preparing for any act of war. 
//_/_/
//_/_/  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
//_/_/  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
//_/_/  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
//_/_/  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 
//_/_/  OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
//_/_/  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
//_/_/  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
//_/_/  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
//_/_/  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
//_/_/  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
//_/_/  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//_/_/
//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
/*! @file ComponentData.cpp
    @brief Implementation of the legacy page-based ComponentData record accessor.
    @ingroup Memory */

#include "ComponentData.h"
#include "PsyTime.h"
#include "UnitTestFramework.h"

namespace cmlabs {


// Static
ComponentData* ComponentData::CreateComponent(uint32 id, const char* name, uint32 size, uint16 nodeID, uint16 procID) {

	ComponentData* compData = GetComponentData(id);
	if (compData != NULL) {
		if (_stricmp(name, compData->header->name) == 0) {
			if ((nodeID != compData->header->nodeID) && (compData->header->nodeID)) {
				delete(compData);
				return false;
			}
		}
		compData->header->nodeID = nodeID;
		compData->header->procID = procID;
	}
	else {
		// Create the page
		uint32 pageID = 0;
		char* data = MemoryManager::CreateAndLockNewSystemPage(size, pageID);
		if (data == NULL)
			return NULL;
	
		// Record the pageID into the list
		InsertComponentPageID(id, pageID, nodeID, procID, name);

		struct COMPONENTDATAHEADER* header = (struct COMPONENTDATAHEADER*) data;
		header->cid = id;
		header->createdTime = GetTimeNow();
		header->size = size;
		header->nodeID = nodeID;
		header->procID = procID;
		header->pageID = pageID;
		utils::strcpyavail(header->name, name, MAXKEYNAMELEN-1, false);

		// Initialise the component data
		compData = new ComponentData(data);
	}

	// Do not unlock the data, ComponentData destructor will do this
	return compData;
}

// static
bool ComponentData::RecordRemoteComponent(uint32 id, const char* name, uint16 nodeID, uint64 time) {

	ComponentData* compData = GetComponentData(id);
	if (compData != NULL) {
		if (_stricmp(name, compData->header->name) == 0) {
			if (nodeID == compData->header->nodeID) {
				delete(compData);
				return true;
			}
			else if (compData->header->nodeID == 0) {
				compData->header->nodeID = nodeID;
				delete(compData);
				return true;
			}
		}
		delete(compData);
		return false;
	}

	// Create the page
	uint32 pageID = 0;
	char* data = MemoryManager::CreateAndLockNewSystemPage(sizeof(COMPONENTDATAHEADER), pageID);
	if (data == NULL)
		return NULL;
	
	// Record the pageID into the list
	InsertComponentPageID(id, pageID, nodeID, 0, name);

	struct COMPONENTDATAHEADER* header = (struct COMPONENTDATAHEADER*) data;
	header->cid = id;
	header->createdTime = time;
	header->size = 0;
	header->nodeID = nodeID;
	header->procID = 0;
	header->pageID = pageID;
	utils::strcpyavail(header->name, name, MAXKEYNAMELEN-1, false);
	MemoryManager::UnlockSystemBlock(pageID);
	return true;
}

// static
bool ComponentData::ReserveComponentID(uint32 id, const char* name) {

	ComponentData* compData = GetComponentData(id);
	if (compData != NULL) {
		delete(compData);
		return false;
	}

	// Create the page
	uint32 pageID = 0;
	char* data = MemoryManager::CreateAndLockNewSystemPage(sizeof(COMPONENTDATAHEADER), pageID);
	if (data == NULL)
		return NULL;
	
	// Record the pageID into the list
	InsertComponentPageID(id, pageID, 0, 0, name);

	struct COMPONENTDATAHEADER* header = (struct COMPONENTDATAHEADER*) data;
	header->cid = id;
	header->createdTime = 0;
	header->size = 0;
	header->nodeID = 0;
	header->procID = 0;
	header->pageID = pageID;
	utils::strcpyavail(header->name, name, MAXKEYNAMELEN-1, false);
	MemoryManager::UnlockSystemBlock(pageID);
	return true;
}

// static
char* ComponentData::GetAndRemoveComponent(uint32 cid, uint32& dataSize) {
	uint32 pageID = 0;
	if (!GetComponentPageID(cid, pageID))
		return NULL;
	uint32 size = 0;
	char* data = MemoryManager::GetAndLockSystemBlock(pageID, size);
	if (data == NULL)
		return NULL;

	char* newData = (char*)malloc(size);
	memcpy(newData, data, size);
	
	MemoryManager::UnlockSystemBlock(pageID);
	MemoryManager::DestroySystemPage(pageID);
	dataSize = size;
	return newData;
}

// static
bool ComponentData::AddExistingComponent(uint32 cid, uint16 nodeID, uint16 procID, char* oldData, uint32 dataSize) {
	// Create the page
	uint32 pageID = 0;
	char* data = MemoryManager::CreateAndLockNewSystemPage(dataSize, pageID);
	if (data == NULL)
		return false;

	struct COMPONENTDATAHEADER* header = (struct COMPONENTDATAHEADER*) oldData;

	// Record the pageID into the list
	InsertComponentPageID(cid, pageID, nodeID, procID, header->name);
	memcpy(data, oldData, dataSize);

	header = (struct COMPONENTDATAHEADER*) data;
	header->cid = cid;
	header->nodeID = nodeID;
	header->procID = procID;
	header->pageID = pageID;

	MemoryManager::UnlockSystemBlock(pageID);
	return true;
}


// static
bool ComponentData::UpdateRemoteComponent(uint32 cid, uint16 nodeID, uint16 procID, const char* name) {
	// Record the component into the list
	InsertComponentPageID(cid, 0, nodeID, procID, name);
	return true;
}

// static
bool ComponentData::AddComponentStats(uint32 cid, uint64 userCPU, uint64 kernelCPU, DataMessage* inputMsg, DataMessage* outputMsg, uint32 runCount) {
	uint32 pageID = 0;
	uint32 size = 0;
	COMPONENTDATAHEADER* header;

	if (!GetComponentPageID(cid, pageID))
		return false;
	if ( (!(header = (COMPONENTDATAHEADER*)MemoryManager::GetAndLockSystemBlock(pageID, size))) || (header->procID == 0))
		return false;

	header->stats.time = GetTimeNow();
	header->stats.runCount += runCount;
	header->stats.runUserTime += userCPU;
	header->stats.runKernelTime += kernelCPU;
	if (inputMsg) {
		header->stats.msgInCount++;
		header->stats.msgInBytes += inputMsg->getSize();
		memcpy(((char*)header->stats.recentInMsg)+sizeof(DataMessageHeader), (char*)header->stats.recentInMsg, 9*sizeof(DataMessageHeader));
		memcpy(header->stats.recentInMsg, inputMsg->data, sizeof(DataMessageHeader));
	}
	if (outputMsg) {
		header->stats.msgOutCount++;
		header->stats.msgOutBytes += outputMsg->getSize();
		memcpy(((char*)header->stats.recentOutMsg)+sizeof(DataMessageHeader), (char*)header->stats.recentOutMsg, 9*sizeof(DataMessageHeader));
		memcpy(header->stats.recentOutMsg, outputMsg->data, sizeof(DataMessageHeader));
	}

	MemoryManager::UnlockSystemBlock(pageID);
	return true;
}


// Static
bool ComponentData::DestroyComponent(uint32 cid) {
	ComponentData* comp = GetComponentData(cid);
	if (comp == NULL) {
		// Not a local component, just remove entry
		FreeComponentID(cid);
		return true;
	}
	if (!comp->destroyComponent()) {
		delete(comp);
		return false;
	}
	delete(comp);
	return true;
}

// Static
ComponentData* ComponentData::GetComponentData(uint32 cid) {
	uint32 pageID = 0;
	if (!GetComponentPageID(cid, pageID))
		return NULL;
	uint32 size = 0;
	char* data = MemoryManager::GetAndLockSystemBlock(pageID, size);
	if (data == NULL)
		return NULL;
	// Do not unlock the data, ComponentData destructor will do this
	return new ComponentData(data);
}


// Static
bool ComponentData::GetComponentPageID(uint32 cid, uint32& pageID) {
	ComponentMapEntry* entry = GetAndLockComponentEntry(cid);
	if ((entry != NULL) && (entry->id == cid) && (entry->time != 0)) {
		pageID = entry->pageID;
		UnlockComponentMap();
		return true;
	}
	else {
		if (entry)
			UnlockComponentMap();
		return false;
	}
}

// Static
bool ComponentData::InsertComponentPageID(uint32 cid, uint32 pageID, uint16 nodeID, uint16 procID, const char* name) {
	uint32 dataSize;
	char* data = MemoryManager::GetAndLockSystemBlock(ID_COMPMAPPAGE, dataSize);
	if (data == NULL)
		return false;
	// Insert Component PageID from list
	ComponentMapHeader* cmHeader = (ComponentMapHeader*)data;

	// Calc offset
	uint32 offset = sizeof(ComponentMapHeader) + cmHeader->bitFieldSize +
		cid * sizeof(ComponentMapEntry);
	if (offset > cmHeader->size) {
		MemoryManager::UnlockSystemBlock(ID_COMPMAPPAGE);
		return false;
	}
	ComponentMapEntry* entry = (ComponentMapEntry*) (data+offset);
	if ((entry->id == cid) || (entry->time == 0)) {
		entry->id = cid;
		entry->time = GetTimeNow();
		entry->nodeID = nodeID;
		entry->procID = procID;
		entry->pageID = pageID;
		uint16 nameLen = (uint16)strlen(name);
		if (nameLen > MAXKEYNAMELEN) {
			memcpy(entry->name, name, MAXKEYNAMELEN);
			entry->name[MAXKEYNAMELEN] = 0;
		}
		else
			strcpy(entry->name, name);
		cmHeader->count++;
		utils::SetBit(cid, BITOCCUPIED, (uint32*)((char*)cmHeader + sizeof(ComponentMapHeader)), cmHeader->bitFieldSize);
		MemoryManager::UnlockSystemBlock(ID_COMPMAPPAGE);
		return true;
	}
	else {
		MemoryManager::UnlockSystemBlock(ID_COMPMAPPAGE);
		return false;
	}
}

// Static
bool ComponentData::GetNextAvailableComponentID(uint32& cid) {
	uint32 dataSize;
	char* data = MemoryManager::GetAndLockSystemBlock(ID_COMPMAPPAGE, dataSize);
	if (data == NULL)
		return false;
	// Get next available Component ID from list
	ComponentMapHeader* cmHeader = (ComponentMapHeader*)data;
	uint32 loc;
	if (!utils::GetFirstFreeBitLoc((uint32*)((char*)cmHeader + sizeof(ComponentMapHeader)), cmHeader->bitFieldSize, loc)) {
		MemoryManager::UnlockSystemBlock(ID_COMPMAPPAGE);
		return false;
	}
	// Reserve it
	utils::SetBit(cid, BITOCCUPIED, (uint32*)((char*)cmHeader + sizeof(ComponentMapHeader)), cmHeader->bitFieldSize);
	cid = loc;
	MemoryManager::UnlockSystemBlock(ID_COMPMAPPAGE);
	return true;
}


// Static
bool ComponentData::FreeComponentID(uint32 cid) {
	uint32 dataSize;
	char* data = MemoryManager::GetAndLockSystemBlock(ID_COMPMAPPAGE, dataSize);
	if (data == NULL)
		return false;
	// Get next available Component ID from list
	ComponentMapHeader* cmHeader = (ComponentMapHeader*)data;
	ComponentMapEntry* entry;

	// Calc offset
	uint32 offset = sizeof(ComponentMapHeader) + cmHeader->bitFieldSize +
		cid * sizeof(ComponentMapEntry);
	if (offset > cmHeader->size) {
		MemoryManager::UnlockSystemBlock(ID_COMPMAPPAGE);
		return false;
	}

	// Free it
	entry = (ComponentMapEntry*) (data+offset);
	entry->time = 0;
	entry->name[0] = 0;

	cmHeader->count--;
	utils::SetBit(cid, BITFREE, (uint32*)((char*)cmHeader + sizeof(ComponentMapHeader)), cmHeader->bitFieldSize);
	MemoryManager::UnlockSystemBlock(ID_COMPMAPPAGE);
	// Does not delete the old page from memory!!!
	return true;
}



// static
uint16 ComponentData::GetComponentNodeID(uint32 cid) {
	ComponentMapEntry* entry = GetAndLockComponentEntry(cid);
	if ((entry != NULL) && (entry->id == cid) && (entry->time != 0)) {
		uint16 nodeID = entry->nodeID;
		UnlockComponentMap();
		return nodeID;
	}
	else {
		UnlockComponentMap();
		return 0;
	}
}

// static
uint16 ComponentData::GetComponentProcessID(uint32 cid) {
	ComponentMapEntry* entry = GetAndLockComponentEntry(cid);
	if ((entry != NULL) && (entry->id == cid) && (entry->time != 0) && (entry->pageID != 0)) {
		uint16 procID = entry->procID;
		UnlockComponentMap();
		return procID;
	}
	else {
		UnlockComponentMap();
		return 0;
	}
}


// static
bool ComponentData::IsComponentLocal(uint32 cid) {
	ComponentMapEntry* entry = GetAndLockComponentEntry(cid);
	if ((entry != NULL) && (entry->id == cid) && (entry->time != 0)) {
		uint16 procID = entry->procID;
		UnlockComponentMap();
		return (procID != 0);
	}
	else {
		UnlockComponentMap();
		return false;
	}
}

// static
bool ComponentData::GetComponentName(uint32 cid, char* name, uint32 maxNameLen) {
	ComponentMapEntry* entry = GetAndLockComponentEntry(cid);
	if ((entry != NULL) && (entry->id == cid) && (entry->time != 0)) {
		if (strlen(entry->name) < maxNameLen)
			strcpy(name, entry->name);
		else {
			memcpy(name, entry->name, maxNameLen-1);
			name[maxNameLen-1] = 0;
		}
		UnlockComponentMap();
		return true;
	}
	else {
		UnlockComponentMap();
		return false;
	}
}

// static
ComponentStats ComponentData::GetComponentStats(uint32 cid) {
	ComponentStats stats;
	uint32 pageID = 0;
	uint32 size = 0;
	char* data;
	if (GetComponentPageID(cid, pageID)) {
		if ((data = MemoryManager::GetAndLockSystemBlock(pageID, size)) &&
				(((COMPONENTDATAHEADER*)data)->procID != 0))
			memcpy(&stats, &((COMPONENTDATAHEADER*)data)->stats, sizeof(ComponentStats));
		else
			memset(&stats, 0, sizeof(ComponentStats));
	}
	MemoryManager::UnlockSystemBlock(pageID);
	return stats;
}

// static
bool ComponentData::SetComponentStats(uint32 cid, ComponentStats& stats){
	uint32 pageID = 0;
	uint32 size = 0;
	char* data;
	if (GetComponentPageID(cid, pageID)) {
		if ((data = MemoryManager::GetAndLockSystemBlock(pageID, size)) &&
				(((COMPONENTDATAHEADER*)data)->procID != 0)) {
			memcpy(&((COMPONENTDATAHEADER*)data)->stats, &stats, sizeof(ComponentStats));
			MemoryManager::UnlockSystemBlock(pageID);
			return true;
		}
	}
	MemoryManager::UnlockSystemBlock(pageID);
	return false;
}

// static
bool ComponentData::GetComponentID(const char* name, uint32 &cid) {
	uint32 dataSize;
	char* data = MemoryManager::GetAndLockSystemBlock(ID_COMPMAPPAGE, dataSize);
	if (data == NULL)
		return false;
	ComponentMapHeader* header = (ComponentMapHeader*)data;
	ComponentMapEntry* entry;

	entry = (ComponentMapEntry*) (data + sizeof(ComponentMapHeader) + header->bitFieldSize);
	char* endEntry = data + dataSize;
	while ((char*)entry < endEntry) {
		if (strcmp(name, entry->name) == 0) {
			cid = entry->id;
			MemoryManager::UnlockSystemBlock(ID_COMPMAPPAGE);
			return true;
		}
		entry = entry + 1; // sizeof(ComponentMapEntry);
	}

	MemoryManager::UnlockSystemBlock(ID_COMPMAPPAGE);
	return false;
}












ComponentData::ComponentData(char* data) {
	header = (struct COMPONENTDATAHEADER*) data;
}

ComponentData::~ComponentData() {
	disconnect();
}

bool ComponentData::disconnect() {
	if (header == NULL)
		return true;
	MemoryManager::UnlockSystemBlock(header->pageID);
	header = NULL;
	return true;
}

uint32 ComponentData::getAvailableDataSize() {
	return (header->size - sizeof(COMPONENTDATAHEADER) - header->paramSize - header->dataSize);
}

bool ComponentData::resize(uint32 increase) {
	uint32 newSize = header->size + increase;
	uint32 pageID = 0;
	char* data = MemoryManager::CreateAndLockNewSystemPage(newSize, pageID);
	if (data == NULL)
		return false;

	// Record the pageID into the list, overwrite
	InsertComponentPageID(header->cid, pageID, header->nodeID, header->procID, header->name);

	struct COMPONENTDATAHEADER* newHeader = (struct COMPONENTDATAHEADER*) data;
	memcpy((char*)newHeader, (char*)header, header->size);
	newHeader->size = newSize;
	newHeader->pageID = pageID;

	uint32 oldPageID = header->pageID;
	// Unlock and delete the old page
	MemoryManager::UnlockSystemBlock(oldPageID);
	MemoryManager::DestroySystemPage(oldPageID);
	// Swap the new data into place
	header = newHeader;
	// Do not unlock the new data, ComponentData destructor will do this
	return true;
}

bool ComponentData::destroyComponent() {
	FreeComponentID(header->cid);
	uint32 oldPageID = header->pageID;
	// Unlock and delete the old page
	MemoryManager::UnlockSystemBlock(oldPageID);
	MemoryManager::DestroySystemPage(oldPageID);
	header = NULL;
	return true;
}




char* ComponentData::makeRoomForPrivateData(uint32 newSize) {
	if (getAvailableDataSize() < newSize) {
		if ((!resize(newSize)) || (getAvailableDataSize() < newSize))
			return NULL;
	}
	// space available after private data so just use this
	return (char*)header + sizeof(COMPONENTDATAHEADER) + header->paramSize + header->dataSize;
}

char* ComponentData::makeRoomForParameter(uint32 newSize) {
	if (getAvailableDataSize() < newSize) {
		if ((!resize(newSize)) || (getAvailableDataSize() < newSize))
			return NULL;
	}
	char* paramSrc = (char*)header + sizeof(COMPONENTDATAHEADER);
	char* dataSrc = (char*)paramSrc + header->paramSize;
	// space available after private data, so we need to move the private data down
	// to make a gap btw this and the parameter data
	memcpy(dataSrc+newSize, dataSrc, header->dataSize);
	// fill the gap with 0s
	memset(dataSrc, 0, newSize);
	// return pointer to the new gap
	return dataSrc;
}



// Private Data
bool ComponentData::setPrivateData(const char* name, const char* data, uint32 size) {
	char* src = (char*)header + sizeof(COMPONENTDATAHEADER) + header->paramSize;
	char* srcEnd = (char*)header + sizeof(COMPONENTDATAHEADER) + header->paramSize + header->dataSize;
	
	// Does the data entry exist already?
	uint32 offset = 0;
	while ( src < srcEnd ) {
		if (_stricmp(name, src + sizeof(uint32)) == 0)
			return replacePrivateData(name, data, size, offset);
		offset += *(uint32*)src;
		src += *(uint32*)src;
	}

	// Check for required space
	uint32 nameLen = (uint32)strlen(name);
	uint32 newSize = sizeof(uint32) + nameLen + 1 + size;
	char* newData = makeRoomForPrivateData(newSize);
	if (newData == NULL)
		return false;

	// Copy in new data
	*(uint32*) newData = newSize;
	memcpy(newData + sizeof(uint32), name, nameLen+1); // incl. 0 at the end of the string
	memcpy(newData + sizeof(uint32) + nameLen+1, data, size);

	header->dataCount++;
	header->dataSize += newSize;
	return true;
}

uint32 ComponentData::getPrivateDataSize(const char* name) {
	char* src = (char*)header + sizeof(COMPONENTDATAHEADER) + header->paramSize;
	char* srcEnd = (char*)header + sizeof(COMPONENTDATAHEADER) + header->paramSize + header->dataSize;
	
	// Does the data entry exist already?
	while ( src < srcEnd ) {
		if (_stricmp(name, src + sizeof(uint32)) == 0)
			return *(uint32*)src - sizeof(uint32) - (uint32)strlen(src + sizeof(uint32)) - 1;
		src += *(uint32*)src;
	}
	return 0;
}

bool ComponentData::getPrivateDataCopy(const char* name, char* data, uint32 maxSize) {
	char* src = (char*)header + sizeof(COMPONENTDATAHEADER) + header->paramSize;
	char* srcEnd = (char*)header + sizeof(COMPONENTDATAHEADER) + header->paramSize + header->dataSize;
	
	uint32 size, nameLen;
	// Does the data entry exist already?
	while ( src < srcEnd ) {
		if (_stricmp(name, src + sizeof(uint32)) == 0) {
			nameLen = (uint32)strlen(src + sizeof(uint32));
			size = *(uint32*)src - sizeof(uint32) - nameLen - 1;
			if (size > maxSize)
				return false;
			memcpy((void*)data, src + sizeof(uint32) + nameLen+1, size);
			return true;
		}
		src += *(uint32*)src;
	}
	return false;
}

bool ComponentData::replacePrivateData(const char* name, const char* data, uint32 size) {
	char* src = (char*)header + sizeof(COMPONENTDATAHEADER) + header->paramSize;
	char* srcEnd = (char*)header + sizeof(COMPONENTDATAHEADER) + header->paramSize + header->dataSize;
	
	uint32 offset = 0;
	while ( src < srcEnd ) {
		if (_stricmp(name, src + sizeof(uint32)) == 0)
			return replacePrivateData(name, data, size, offset);
		offset += *(uint32*)src;
		src += *(uint32*)src;
	}
	return false;
}

bool ComponentData::replacePrivateData(const char* name, const char* data, uint32 size, uint32 offset) {

	char* src = (char*)header + sizeof(COMPONENTDATAHEADER) + header->paramSize + offset;
	uint32 nameLen = (uint32)strlen(src + sizeof(uint32));
	uint32 oldSize = *(uint32*)src - sizeof(uint32) - nameLen - 1;
	if (oldSize == size) {
		memcpy(src + sizeof(uint32) + nameLen + 1, data, size);
		return true;
	}
	else {
		deletePrivateData(name);
		return setPrivateData(name, data, size);
	}
}

bool ComponentData::deletePrivateData(const char* name) {
	char* src = (char*)header + sizeof(COMPONENTDATAHEADER) + header->paramSize;
	char* srcEnd = (char*)header + sizeof(COMPONENTDATAHEADER) + header->paramSize + header->dataSize;
	
	uint32 delSize;
	uint32 copySize = header->dataSize;
	while ( src < srcEnd ) {
		copySize -= *(uint32*)src;
		if (_stricmp(name, src + sizeof(uint32)) == 0) {
			delSize = *(uint32*)src;
			memcpy(src, src + *(uint32*)src, copySize);
			header->dataCount--;
			header->dataSize -= delSize;
			srcEnd -= delSize;
			memset(srcEnd, 0, delSize);
			return true;
		}
		src += *(uint32*)src;
	}
	return false;
}










// Parameters
bool ComponentData::createParameter(const char* name, const char* val, const char* defaultValue) {
	char* src = (char*)header + sizeof(COMPONENTDATAHEADER);
	char* srcEnd = (char*)header + sizeof(COMPONENTDATAHEADER) + header->paramSize;
	
	// Does the data entry exist already?
	while ( src < srcEnd ) {
		if (_stricmp(name, src + sizeof(PARAMHEADER)) == 0)
			return false;
		src += *(uint32*)src;
	}

	// Calculate required size
	uint32 nameLen = (uint32)strlen(name);
	uint32 newSize = nameLen + 1 + sizeof(PARAMHEADER) + (uint32)strlen(val) + 1;
	if (defaultValue != NULL)
		newSize += (uint32)strlen(defaultValue) + 1;
	else
		newSize += (uint32)strlen(val) + 1;

	char* newData = makeRoomForParameter(newSize);
	if (newData == NULL)
		return false;

	// Copy in new data
	PARAMHEADER* pHeader = (PARAMHEADER*) newData;
	pHeader->size = newSize;
	pHeader->type = PARAM_STRING;
	memcpy(newData + sizeof(PARAMHEADER), name, nameLen+1); // incl. 0 at the end of the string

	src = newData + sizeof(PARAMHEADER) + nameLen + 1;

	// copy in main value
	strcpy(src, val);
	src += strlen(val) + 1;
	// copy in default value
	if (defaultValue != NULL)
		strcpy(src, defaultValue);
	else
		strcpy(src, val);

	header->paramCount++;
	header->paramSize += newSize;
	return true;
}

bool ComponentData::createParameter(const char* name, const char* val, uint32 count, uint32 defaultIndex) {
	char* src = (char*)header + sizeof(COMPONENTDATAHEADER);
	char* srcEnd = (char*)header + sizeof(COMPONENTDATAHEADER) + header->paramSize;
	
	// Does the data entry exist already?
	while ( src < srcEnd ) {
		if (_stricmp(name, src + sizeof(PARAMHEADER)) == 0)
			return false;
		src += *(uint32*)src;
	}

	// Calculate required size
	uint32 n, len;
	uint32 nameLen = (uint32)strlen(name);
	uint32 newSize = nameLen + 1 + sizeof(PARAMHEADER) + sizeof(PARAMCOLLHEADER);
	uint32 dataSize = 0;
	const char* pVal = val;
	for (n=0; n<count; n++) {
		len = (uint32)strlen(pVal) + 1;
		dataSize += len;
		pVal += len;
	}
	newSize += dataSize;

	char* newData = makeRoomForParameter(newSize);
	if (newData == NULL)
		return false;

	// Copy in new data
	PARAMHEADER* pHeader = (PARAMHEADER*) newData;
	pHeader->size = newSize;
	pHeader->type = PARAM_STRING_COLL;
	memcpy(newData + sizeof(PARAMHEADER), name, nameLen+1); // incl. 0 at the end of the string

	PARAMCOLLHEADER* dHeader = (PARAMCOLLHEADER*) (newData + sizeof(PARAMHEADER) + nameLen + 1);
	dHeader->index = defaultIndex;
	dHeader->defaultIndex = defaultIndex;
	dHeader->count = count;

	// copy in data values
	memcpy((char*)dHeader + sizeof(PARAMCOLLHEADER), val, dataSize);

	header->paramCount++;
	header->paramSize += newSize;
	return true;
}

bool ComponentData::createParameter(const char* name, std::vector<std::string> values, const char* defaultValue) {
	uint32 n, dlen, len = 0, dataSize = 0, defaultIndex = 0;
	uint32 count = (uint32)values.size();
	for (n=0; n<count; n++)
		dataSize += (uint32)values[n].size() + 1;
	char* val = new char[dataSize];
	for (n=0; n<count; n++) {
		dlen = (uint32)values[n].size()+1;
		memcpy(val+len, values[n].c_str(), dlen);
		len += dlen;
		if (values[n] == defaultValue)
			defaultIndex = n;
	}
	bool res = createParameter(name, val, count, defaultIndex);
	delete [] val;
	return res;
}

bool ComponentData::createParameter(const char* name, std::vector<std::string> values, int64 defaultValue) {
	uint32 n, defaultIndex = 0;
	uint32 count = (uint32)values.size();
	int64* val = new int64[count];
	for (n=0; n<count; n++) {
		val[n] = utils::Ascii2Int64(values[n].c_str());
		if (val[n] == defaultValue)
			defaultIndex = n;
	}
	bool res = createParameter(name, val, count, defaultIndex);
	delete [] val;
	return res;
}

bool ComponentData::createParameter(const char* name, std::vector<std::string> values, float64 defaultValue) {
	uint32 n, defaultIndex = 0;
	uint32 count = (uint32)values.size();
	float64* val = new float64[count];
	for (n=0; n<count; n++) {
		val[n] = utils::Ascii2Float64(values[n].c_str());
		if (val[n] == defaultValue)
			defaultIndex = n;
	}
	bool res = createParameter(name, val, count, defaultIndex);
	delete [] val;
	return res;
}

bool ComponentData::createParameter(const char* name, int64* val, uint32 count, uint32 defaultIndex) {
	char* src = (char*)header + sizeof(COMPONENTDATAHEADER);
	char* srcEnd = (char*)header + sizeof(COMPONENTDATAHEADER) + header->paramSize;
	
	// Does the data entry exist already?
	while ( src < srcEnd ) {
		if (_stricmp(name, src + sizeof(PARAMHEADER)) == 0)
			return false;
		src += *(uint32*)src;
	}

	// Calculate required size
	uint32 nameLen = (uint32)strlen(name);
	uint32 newSize = nameLen + 1 + sizeof(PARAMHEADER) + sizeof(PARAMCOLLHEADER) + (count * sizeof(int64));

	char* newData = makeRoomForParameter(newSize);
	if (newData == NULL)
		return false;

	// Copy in new data
	PARAMHEADER* pHeader = (PARAMHEADER*) newData;
	pHeader->size = newSize;
	pHeader->type = PARAM_INTEGER_COLL;
	memcpy(newData + sizeof(PARAMHEADER), name, nameLen+1); // incl. 0 at the end of the string

	PARAMCOLLHEADER* dHeader = (PARAMCOLLHEADER*) (newData + sizeof(PARAMHEADER) + nameLen + 1);
	dHeader->index = defaultIndex;
	dHeader->defaultIndex = defaultIndex;
	dHeader->count = count;

	// copy in data values
	memcpy((char*)dHeader + sizeof(PARAMCOLLHEADER), val, count * sizeof(int64));

	header->paramCount++;
	header->paramSize += newSize;
	return true;
}

bool ComponentData::createParameter(const char* name, float64* val, uint32 count, uint32 defaultIndex) {
	char* src = (char*)header + sizeof(COMPONENTDATAHEADER);
	char* srcEnd = (char*)header + sizeof(COMPONENTDATAHEADER) + header->paramSize;
	
	// Does the data entry exist already?
	while ( src < srcEnd ) {
		if (_stricmp(name, src + sizeof(PARAMHEADER)) == 0)
			return false;
		src += *(uint32*)src;
	}

	// Calculate required size
	uint32 nameLen = (uint32)strlen(name);
	uint32 newSize = nameLen + 1 + sizeof(PARAMHEADER) + sizeof(PARAMCOLLHEADER) + (count * sizeof(float64));

	char* newData = makeRoomForParameter(newSize);
	if (newData == NULL)
		return false;

	// Copy in new data
	PARAMHEADER* pHeader = (PARAMHEADER*) newData;
	pHeader->size = newSize;
	pHeader->type = PARAM_FLOAT_COLL;
	memcpy(newData + sizeof(PARAMHEADER), name, nameLen+1); // incl. 0 at the end of the string

	PARAMCOLLHEADER* dHeader = (PARAMCOLLHEADER*) (newData + sizeof(PARAMHEADER) + nameLen + 1);
	dHeader->index = defaultIndex;
	dHeader->defaultIndex = defaultIndex;
	dHeader->count = count;

	// copy in data values
	memcpy((char*)dHeader + sizeof(PARAMCOLLHEADER), val, count * sizeof(float64));

	header->paramCount++;
	header->paramSize += newSize;
	return true;
}

bool ComponentData::createParameter(const char* name, int64 val, int64 min, int64 max, int64 interval) {
	char* src = (char*)header + sizeof(COMPONENTDATAHEADER);
	char* srcEnd = (char*)header + sizeof(COMPONENTDATAHEADER) + header->paramSize;
	
	// Does the data entry exist already?
	while ( src < srcEnd ) {
		if (_stricmp(name, src + sizeof(PARAMHEADER)) == 0)
			return false;
		src += *(uint32*)src;
	}

	// Calculate required size
	uint32 nameLen = (uint32)strlen(name);
	uint32 newSize = nameLen + 1 + sizeof(PARAMHEADER) + sizeof(PARAMINTEGERHEADER);

	char* newData = makeRoomForParameter(newSize);
	if (newData == NULL)
		return false;

	// Copy in new data
	PARAMHEADER* pHeader = (PARAMHEADER*) newData;
	pHeader->size = newSize;
	pHeader->type = PARAM_INTEGER;
	memcpy(newData + sizeof(PARAMHEADER), name, nameLen+1); // incl. 0 at the end of the string

	PARAMINTEGERHEADER* dHeader = (PARAMINTEGERHEADER*) (newData + sizeof(PARAMHEADER) + nameLen + 1);

	// copy in values
	dHeader->val = val;
	dHeader->defaultVal = val;
	dHeader->minVal = min;
	dHeader->maxVal = max;
	dHeader->interval = interval;

	header->paramCount++;
	header->paramSize += newSize;
	return true;
}

bool ComponentData::createParameter(const char* name, float64 val, float64 min, float64 max, float64 interval) {
	char* src = (char*)header + sizeof(COMPONENTDATAHEADER);
	char* srcEnd = (char*)header + sizeof(COMPONENTDATAHEADER) + header->paramSize;
	
	// Does the data entry exist already?
	while ( src < srcEnd ) {
		if (_stricmp(name, src + sizeof(PARAMHEADER)) == 0)
			return false;
		src += *(uint32*)src;
	}

	// Calculate required size
	uint32 nameLen = (uint32)strlen(name);
	uint32 newSize = nameLen + 1 + sizeof(PARAMHEADER) + sizeof(PARAMFLOATHEADER);

	char* newData = makeRoomForParameter(newSize);
	if (newData == NULL)
		return false;

	// Copy in new data
	PARAMHEADER* pHeader = (PARAMHEADER*) newData;
	pHeader->size = newSize;
	pHeader->type = PARAM_FLOAT;
	memcpy(newData + sizeof(PARAMHEADER), name, nameLen+1); // incl. 0 at the end of the string

	PARAMFLOATHEADER* dHeader = (PARAMFLOATHEADER*) (newData + sizeof(PARAMHEADER) + nameLen + 1);

	// copy in values
	dHeader->val = val;
	dHeader->defaultVal = val;
	dHeader->minVal = min;
	dHeader->maxVal = max;
	dHeader->interval = interval;

	header->paramCount++;
	header->paramSize += newSize;
	return true;
}

bool ComponentData::hasParameter(const char* name) {
	char* src = (char*)header + sizeof(COMPONENTDATAHEADER);
	char* srcEnd = (char*)header + sizeof(COMPONENTDATAHEADER) + header->paramSize;
	
	while ( src < srcEnd ) {
		if (_stricmp(name, src + sizeof(PARAMHEADER)) == 0)
			return true;
		src += *(uint32*)src;
	}
	return false;
}

bool ComponentData::deleteParameter(const char* name) {
	char* src = (char*)header + sizeof(COMPONENTDATAHEADER);
	char* srcEnd = (char*)header + sizeof(COMPONENTDATAHEADER) + header->paramSize;
	
	uint32 delSize;
	uint32 copySize = header->paramSize + header->dataSize;
	while ( src < srcEnd ) {
		copySize -= *(uint32*)src;
		if (_stricmp(name, src + sizeof(PARAMHEADER)) == 0) {
			delSize = *(uint32*)src;
			memcpy(src, src + delSize, copySize);
			header->paramCount--;
			header->paramSize -= delSize;
			// fill data at end with 0s
			srcEnd -= delSize + header->dataSize;
			memset(srcEnd, 0, delSize);
			return true;
		}
		src += *(uint32*)src;
	}
	return false;
}

uint8 ComponentData::getParameterDataType(const char* name) {
	char* src = (char*)header + sizeof(COMPONENTDATAHEADER);
	char* srcEnd = (char*)header + sizeof(COMPONENTDATAHEADER) + header->paramSize;
	
	while ( src < srcEnd ) {
		if (_stricmp(name, src + sizeof(PARAMHEADER)) == 0) {
			PARAMHEADER* pHeader = (PARAMHEADER*) src;
			return pHeader->type;
		}
		src += *(uint32*)src;
	}
	return 0;
}

uint32 ComponentData::getParameterValueSize(const char* name) {
	char* src = (char*)header + sizeof(COMPONENTDATAHEADER);
	char* srcEnd = (char*)header + sizeof(COMPONENTDATAHEADER) + header->paramSize;
	
	char* pSrc;
	while ( src < srcEnd ) {
		if (_stricmp(name, src + sizeof(PARAMHEADER)) == 0) {
			PARAMHEADER* pHeader = (PARAMHEADER*) src;
			pSrc = src + sizeof(PARAMHEADER) + strlen(src + sizeof(PARAMHEADER)) + 1;
			if ( (pHeader->type == PARAM_INTEGER) || (pHeader->type == PARAM_INTEGER_COLL) )
				return sizeof(int64);
			else if ( (pHeader->type == PARAM_FLOAT) || (pHeader->type == PARAM_FLOAT_COLL) )
				return sizeof(float64);
			else if (pHeader->type == PARAM_STRING) {
				return ((uint32)strlen(pSrc) + 1);
			}
			else {
				PARAMCOLLHEADER* cHeader = (PARAMCOLLHEADER*) pSrc;
				for (uint32 n=0; n<cHeader->index; n++)
					pSrc += strlen(pSrc) + 1;
				return (uint32)strlen(pSrc) + 1;
			}
		}
		src += *(uint32*)src;
	}
	return 0;
}

bool ComponentData::getParameter(const char* name, char* val, uint32 maxSize) {
	char* src = (char*)header + sizeof(COMPONENTDATAHEADER);
	char* srcEnd = (char*)header + sizeof(COMPONENTDATAHEADER) + header->paramSize;
	
	char* pSrc;
	while ( src < srcEnd ) {
		if (_stricmp(name, src + sizeof(PARAMHEADER)) == 0) {
			PARAMHEADER* pHeader = (PARAMHEADER*) src;
			pSrc = src + sizeof(PARAMHEADER) + strlen(src + sizeof(PARAMHEADER)) + 1;
			if (pHeader->type == PARAM_STRING) {
				if (maxSize < strlen(pSrc) + 1)
					return false;
				strcpy(val, pSrc);
				return true;
			}
			else if (pHeader->type == PARAM_STRING_COLL) {
				PARAMCOLLHEADER* cHeader = (PARAMCOLLHEADER*) pSrc;
				pSrc += sizeof(PARAMCOLLHEADER);
				for (uint32 n=0; n<cHeader->index; n++)
					pSrc += strlen(pSrc) + 1;
				if (maxSize < strlen(pSrc) + 1)
					return false;
				strcpy(val, pSrc);
				return true;
			}
			else
				return false;
		}
		src += *(uint32*)src;
	}
	return false;
}

bool ComponentData::getParameter(const char* name, int64& val) {
	char* src = (char*)header + sizeof(COMPONENTDATAHEADER);
	char* srcEnd = (char*)header + sizeof(COMPONENTDATAHEADER) + header->paramSize;
	
	char* pSrc;
	while ( src < srcEnd ) {
		if (_stricmp(name, src + sizeof(PARAMHEADER)) == 0) {
			PARAMHEADER* pHeader = (PARAMHEADER*) src;
			pSrc = src + sizeof(PARAMHEADER) + strlen(src + sizeof(PARAMHEADER)) + 1;
			if (pHeader->type == PARAM_INTEGER) {
				val = *(int64*)(pSrc);
				return true;
			}
			else if  (pHeader->type == PARAM_INTEGER_COLL) {
				PARAMCOLLHEADER* cHeader = (PARAMCOLLHEADER*) pSrc;
				pSrc += sizeof(PARAMCOLLHEADER) + (cHeader->index*sizeof(int64));
				val = *(int64*) pSrc;
				return true;
			}
			else
				return false;
		}
		src += *(uint32*)src;
	}
	return false;
}

bool ComponentData::getParameter(const char* name, float64& val) {
	char* src = (char*)header + sizeof(COMPONENTDATAHEADER);
	char* srcEnd = (char*)header + sizeof(COMPONENTDATAHEADER) + header->paramSize;
	
	char* pSrc;
	while ( src < srcEnd ) {
		if (_stricmp(name, src + sizeof(PARAMHEADER)) == 0) {
			PARAMHEADER* pHeader = (PARAMHEADER*) src;
			pSrc = src + sizeof(PARAMHEADER) + strlen(src + sizeof(PARAMHEADER)) + 1;
			if (pHeader->type == PARAM_FLOAT) {
				val = *(float64*)(pSrc);
				return true;
			}
			else if  (pHeader->type == PARAM_FLOAT_COLL) {
				PARAMCOLLHEADER* cHeader = (PARAMCOLLHEADER*) pSrc;
				pSrc += sizeof(PARAMCOLLHEADER) + (cHeader->index*sizeof(float64));
				val = *(float64*) pSrc;
				return true;
			}
			else
				return false;
		}
		src += *(uint32*)src;
	}
	return false;
}


bool ComponentData::setParameter(const char* name, const char* val) {
	char* src = (char*)header + sizeof(COMPONENTDATAHEADER);
	char* srcEnd = (char*)header + sizeof(COMPONENTDATAHEADER) + header->paramSize;
	
	char* pSrc;
	uint32 index, oldSize;
	while ( src < srcEnd ) {
		if (_stricmp(name, src + sizeof(PARAMHEADER)) == 0) {
			PARAMHEADER* pHeader = (PARAMHEADER*) src;
			pSrc = src + sizeof(PARAMHEADER) + strlen(src + sizeof(PARAMHEADER)) + 1;
			if (pHeader->type == PARAM_STRING) {
				oldSize = (uint32)strlen(pSrc);
				if (strlen(val) == oldSize) {
					strcpy(pSrc, val);
					return true;
				}
				// we need to recreate the parameter with the new size
				uint32 defaultSize = (uint32)strlen(pSrc + oldSize + 1) + 1;
				char* defaultValue = new char[defaultSize];
				strcpy(defaultValue, pSrc + oldSize + 1);
				// delete the old parameter
				if (!deleteParameter(name)) {
					delete [] defaultValue;
					return false;
				}
				// create the new one
				if (!createParameter(name, val, defaultValue)) {
					delete [] defaultValue;
					return false;
				}
				// clean up
				delete [] defaultValue;
				return true;
			}
			else if (pHeader->type == PARAM_STRING_COLL) {
				// See if the requested value is in the list
				PARAMCOLLHEADER* cHeader = (PARAMCOLLHEADER*) pSrc;
				index = 0;
				pSrc += sizeof(PARAMCOLLHEADER);
				while (index < cHeader->count) {
					if (_stricmp(val, pSrc) == 0) {
						// if so, set the index to the correct entry
						cHeader->index = index;
						return true;
					}
					index++;
					pSrc += strlen(pSrc) + 1;
				}
				return false;
			}
			else
				return false;
		}
		src += *(uint32*)src;
	}
	return false;
}

bool ComponentData::setParameter(const char* name, int64 val) {
	char* src = (char*)header + sizeof(COMPONENTDATAHEADER);
	char* srcEnd = (char*)header + sizeof(COMPONENTDATAHEADER) + header->paramSize;
	
	char* pSrc;
	uint32 index;
	while ( src < srcEnd ) {
		if (_stricmp(name, src + sizeof(PARAMHEADER)) == 0) {
			PARAMHEADER* pHeader = (PARAMHEADER*) src;
			pSrc = src + sizeof(PARAMHEADER) + strlen(src + sizeof(PARAMHEADER)) + 1;
			if (pHeader->type == PARAM_INTEGER) {
				((PARAMINTEGERHEADER*) pSrc)->val = val;
			}
			else if (*(uint8*)pSrc == PARAM_INTEGER_COLL) {
				// See if the requested value is in the list
				PARAMCOLLHEADER* cHeader = (PARAMCOLLHEADER*) pSrc;
				index = 0;
				pSrc += sizeof(PARAMCOLLHEADER);
				while (index < cHeader->count) {
					if (*(int64*)pSrc == val) {
						// if so, set the index to the correct entry
						cHeader->index = index;
						return true;
					}
					index++;
					pSrc += sizeof(int64);
				}
				return false;
			}
			else
				return false;
		}
		src += *(uint32*)src;
	}
	return false;
}

bool ComponentData::setParameter(const char* name, float64 val) {
	char* src = (char*)header + sizeof(COMPONENTDATAHEADER);
	char* srcEnd = (char*)header + sizeof(COMPONENTDATAHEADER) + header->paramSize;
	
	char* pSrc;
	uint32 index;
	while ( src < srcEnd ) {
		if (_stricmp(name, src + sizeof(PARAMHEADER)) == 0) {
			PARAMHEADER* pHeader = (PARAMHEADER*) src;
			pSrc = src + sizeof(PARAMHEADER) + strlen(src + sizeof(PARAMHEADER)) + 1;
			if (pHeader->type == PARAM_FLOAT) {
				PARAMFLOATHEADER* dHeader = (PARAMFLOATHEADER*) pSrc;
				dHeader->val = val;
			}
			else if (*(uint8*)pSrc == PARAM_INTEGER_COLL) {
				// See if the requested value is in the list
				PARAMCOLLHEADER* cHeader = (PARAMCOLLHEADER*) pSrc;
				index = 0;
				pSrc += sizeof(PARAMCOLLHEADER);
				while (index < cHeader->count) {
					if (*(float64*)pSrc == val) {
						// if so, set the index to the correct entry
						cHeader->index = index;
						return true;
					}
					index++;
					pSrc += sizeof(int64);
				}
				return false;
			}
			else
				return false;
		}
		src += *(uint32*)src;
	}
	return false;
}


bool ComponentData::resetParameter(const char* name) {
	char* src = (char*)header + sizeof(COMPONENTDATAHEADER);
	char* srcEnd = (char*)header + sizeof(COMPONENTDATAHEADER) + header->paramSize;
	
	char* pSrc;
	while ( src < srcEnd ) {
		if (_stricmp(name, src + sizeof(PARAMHEADER)) == 0) {
			PARAMHEADER* pHeader = (PARAMHEADER*) src;
			pSrc = src + sizeof(PARAMHEADER) + strlen(src + sizeof(PARAMHEADER)) + 1;
			if (pHeader->type == PARAM_INTEGER) {
				((PARAMINTEGERHEADER*)pSrc)->val = ((PARAMINTEGERHEADER*)pSrc)->defaultVal;
			}
			else if (*(uint8*)pSrc == PARAM_INTEGER_COLL) {
				// Set index to default index
				((PARAMCOLLHEADER*)pSrc)->index = ((PARAMCOLLHEADER*)pSrc)->defaultIndex;
				return true;
			}
			else if (*(uint8*)pSrc == PARAM_FLOAT) {
				((PARAMFLOATHEADER*)pSrc)->val = ((PARAMFLOATHEADER*)pSrc)->defaultVal;
			}
			else if (*(uint8*)pSrc == PARAM_FLOAT_COLL) {
				// Set index to default index
				((PARAMCOLLHEADER*)pSrc)->index = ((PARAMCOLLHEADER*)pSrc)->defaultIndex;
				return true;
			}
			else if (*(uint8*)pSrc == PARAM_STRING) {
				return setParameter(name, pSrc + strlen(pSrc) + 1);
			}
			else if (*(uint8*)pSrc == PARAM_STRING_COLL) {
				// Set index to default index
				((PARAMCOLLHEADER*)pSrc)->index = ((PARAMCOLLHEADER*)pSrc)->defaultIndex;
				return true;
			}
			else
				return false;
		}
		src += *(uint32*)src;
	}
	return false;
}

bool ComponentData::tweakParameter(const char* name, int32 tweak) {
	char* src = (char*)header + sizeof(COMPONENTDATAHEADER);
	char* srcEnd = (char*)header + sizeof(COMPONENTDATAHEADER) + header->paramSize;

	if (tweak == 0)
		return true;
	
	char* pSrc;
	int64 testInt;
	float64 testFloat;
	while ( src < srcEnd ) {
		if (_stricmp(name, src + sizeof(PARAMHEADER)) == 0) {
			PARAMHEADER* pHeader = (PARAMHEADER*) src;
			pSrc = src + sizeof(PARAMHEADER) + strlen(src + sizeof(PARAMHEADER)) + 1;
			if ( (pHeader->type == PARAM_INTEGER_COLL) || (pHeader->type == PARAM_FLOAT_COLL) || (pHeader->type == PARAM_STRING_COLL) ) {
				PARAMCOLLHEADER* cHeader = (PARAMCOLLHEADER*)pSrc;
				if ( (cHeader->index + tweak >= 0) && (cHeader->index + tweak < cHeader->count) ) {
					cHeader->index += tweak;
					return true;
				}
				else
					return false;
			}
			else if (pHeader->type == PARAM_INTEGER) {
				PARAMINTEGERHEADER* iHeader = (PARAMINTEGERHEADER*)pSrc;
				if (iHeader->interval == 0)
					return false;
				testInt = iHeader->val + (tweak * iHeader->interval);
				if ( (testInt >= iHeader->minVal) && (testInt <= iHeader->maxVal) ) {
					iHeader->val = testInt;
					return true;
				}
			}
			else if (pHeader->type == PARAM_FLOAT) {
				PARAMFLOATHEADER* fHeader = (PARAMFLOATHEADER*)pSrc;
				if (fHeader->interval == 0)
					return false;
				testFloat = fHeader->val + (tweak * fHeader->interval);
				if ( (testFloat >= fHeader->minVal) && (testFloat <= fHeader->maxVal) ) {
					fHeader->val = testFloat;
					return true;
				}
			}
			else
				return false;
		}
		src += *(uint32*)src;
	}
	return false;
}











// static
ComponentMapEntry* ComponentData::GetAndLockComponentEntry(uint32 id) {
	uint32 dataSize;
	char* data = MemoryManager::GetAndLockSystemBlock(ID_COMPMAPPAGE, dataSize);
	if (data == NULL)
		return NULL;
	ComponentMapHeader* header = (ComponentMapHeader*)data;
	ComponentMapEntry* entry;

	// Calc offset
	uint32 offset = sizeof(ComponentMapHeader) + header->bitFieldSize + id * sizeof(ComponentMapEntry);
	if (offset > header->size) {
		MemoryManager::UnlockSystemBlock(ID_COMPMAPPAGE);
		return NULL;
	}
	entry = (ComponentMapEntry*) (data+offset);
	if ((entry->id == id) && (entry->time != 0)) {
		return entry;
	}
	else {
		MemoryManager::UnlockSystemBlock(ID_COMPMAPPAGE);
		return false;
	}
}

// static
bool ComponentData::UnlockComponentMap() {
	MemoryManager::UnlockSystemBlock(ID_COMPMAPPAGE);
	return true;
}




bool ComponentData::UnitTest() {

	// First create and initialise the MemoryManager
	unittest::progress(5, "memory manager init");
	MemoryManager* manager = new MemoryManager();
	uint32 maxPageCount = 100;
	if (!manager->create(0, maxPageCount)) {
		unittest::fail("ComponentData test: MemoryManager init() failed");
		delete(manager);
		return false;
	}

	unittest::progress(15, "create component and lookup");
	uint32 cid = 1;
	char* name = new char[1024];
	strcpy(name, "TestComponent");
	ComponentData* data = CreateComponent(cid, name, 1024, 0, 0);
	if (data == NULL) {
		unittest::fail("ComponentData test: Could not create component data");
		delete(data);
		delete(manager);
		return false;
	}

	char* str = new char[1024];
	if (!GetComponentName(cid, str, 1024)) {
		unittest::fail("ComponentData test: Could not get component name");
		delete(data);
		delete(manager);
		return false;
	}
	if (strcmp(name, str) != 0) {
		unittest::fail("ComponentData test: Component name mismatch ('%s' != '%s')", name, str);
		delete(data);
		delete(manager);
		return false;
	}

	uint32 cid2;
	if (!GetComponentID(name, cid2)) {
		unittest::fail("ComponentData test: Could not get component id");
		delete(data);
		delete(manager);
		return false;
	}
	if (cid != cid2) {
		unittest::fail("ComponentData test: Component id mismatch ('%u' != '%u')", cid, cid2);
		delete(data);
		delete(manager);
		return false;
	}

	unittest::progress(25, "resize");
	uint32 avail = data->getAvailableDataSize();
	if (!data->resize(1024)) {
		unittest::fail("ComponentData test: Could not resize component");
		delete(data);
		delete(manager);
		return false;
	}
	uint32 avail2 = data->getAvailableDataSize();
	if (avail2 - avail != 1024) {
		unittest::fail("ComponentData test: Component resize failed");
		delete(data);
		delete(manager);
		return false;
	}

	unittest::progress(35, "private data");
	if (!data->setPrivateData("Private0", "Test", 5)) {
		unittest::fail("ComponentData test: Component set private data failed");
		delete(data);
		delete(manager);
		return false;
	}

	if (!data->setPrivateData("Private1", "Test", 5)) {
		unittest::fail("ComponentData test: Component set private data failed");
		delete(data);
		delete(manager);
		return false;
	}

	if (data->getPrivateDataSize("Private1") != 5) {
		unittest::fail("ComponentData test: Component get private data size failed");
		delete(data);
		delete(manager);
		return false;
	}

	char* test = new char[1024];
	if (!data->getPrivateDataCopy("Private1", test, 1024)) {
		unittest::fail("ComponentData test: Component get private data failed");
		delete(data);
		delete(manager);
		return false;
	}

	if (strcmp("Test", test) != 0) {
		unittest::fail("ComponentData test: Component get private data comparison failed");
		delete(data);
		delete(manager);
		return false;
	}

	if (!data->setPrivateData("Private1", "Test2", 6)) {
		unittest::fail("ComponentData test: Component set private data 2 failed");
		delete(data);
		delete(manager);
		return false;
	}

	if (data->getPrivateDataSize("Private1") != 6) {
		unittest::fail("ComponentData test: Component get private data size 2 failed");
		delete(data);
		delete(manager);
		return false;
	}

	if (!data->getPrivateDataCopy("Private1", test, 1024)) {
		unittest::fail("ComponentData test: Component get private data 2 failed");
		delete(data);
		delete(manager);
		return false;
	}

	if (strcmp("Test2", test) != 0) {
		unittest::fail("ComponentData test: Component get private data comparison 2 failed");
		delete(data);
		delete(manager);
		return false;
	}

	if (!data->deletePrivateData("Private1")) {
		unittest::fail("ComponentData test: Component delete private data failed");
		delete(data);
		delete(manager);
		return false;
	}



	unittest::progress(50, "create and read parameters");
	if (!data->createParameter("Param1", "Test")) {
		unittest::fail("ComponentData test: Component create string param failed");
		delete(data);
		delete(manager);
		return false;
	}

	if (data->createParameter("Param1", (int64)5)) {
		unittest::fail("ComponentData test: Component create duplicate name failed to fail");
		delete(data);
		delete(manager);
		return false;
	}

	if (!data->createParameter("Param2", (int64)5)) {
		unittest::fail("ComponentData test: Component create integer param failed");
		delete(data);
		delete(manager);
		return false;
	}

	if (!data->createParameter("Param3", 5.5)) {
		unittest::fail("ComponentData test: Component create float param failed");
		delete(data);
		delete(manager);
		return false;
	}

	// Check the value of each
	if (data->getParameterValueSize("Param1") != 5) {
		unittest::fail("ComponentData test: Component get string param size failed");
		delete(data);
		delete(manager);
		return false;
	}

	if (!data->getParameter("Param1", test, 1024)) {
		unittest::fail("ComponentData test: Component get string param failed");
		delete(data);
		delete(manager);
		return false;
	}

	if (strcmp("Test", test) != 0) {
		unittest::fail("ComponentData test: Component get string parameter comparison 2 failed");
		delete(data);
		delete(manager);
		return false;
	}

	int64 ival;
	if (!data->getParameter("Param2", ival)) {
		unittest::fail("ComponentData test: Component get integer param failed");
		delete(data);
		delete(manager);
		return false;
	}

	float64 fval;
	if (!data->getParameter("Param3", fval)) {
		unittest::fail("ComponentData test: Component get float param failed");
		delete(data);
		delete(manager);
		return false;
	}

	if (data->getParameter("Param4", fval)) {
		unittest::fail("ComponentData test: Component get non-existing param failed to fail");
		delete(data);
		delete(manager);
		return false;
	}

	// Create max min, tweak and check
	unittest::progress(60, "tweak bounded integer parameter");
	if (!data->createParameter("Param4", (int64)5, 0, 10, 1)) {
		unittest::fail("ComponentData test: Component create integer param 2 failed");
		delete(data);
		delete(manager);
		return false;
	}

	if (!data->tweakParameter("Param4", 2)) {
		unittest::fail("ComponentData test: Component tweak integer param failed");
		delete(data);
		delete(manager);
		return false;
	}

	if (!data->getParameter("Param4", ival)) {
		unittest::fail("ComponentData test: Component get integer param 2 failed");
		delete(data);
		delete(manager);
		return false;
	}

	if (ival != 7) {
		unittest::fail("ComponentData test: Component tweak integer param result failed");
		delete(data);
		delete(manager);
		return false;
	}

	if (!data->tweakParameter("Param4", -4)) {
		unittest::fail("ComponentData test: Component tweak integer param 2 failed");
		delete(data);
		delete(manager);
		return false;
	}

	if (!data->getParameter("Param4", ival)) {
		unittest::fail("ComponentData test: Component get integer param 3 failed");
		delete(data);
		delete(manager);
		return false;
	}

	if (ival != 3) {
		unittest::fail("ComponentData test: Component tweak integer param result 2 failed");
		delete(data);
		delete(manager);
		return false;
	}

	if (data->tweakParameter("Param4", 8)) {
		unittest::fail("ComponentData test: Component tweak integer param 2 failed to fail");
		delete(data);
		delete(manager);
		return false;
	}

	// Create collection, tweak and check
	unittest::progress(70, "integer collection parameter");
	int64 i64[] = {1,2,3,4,5};
	if (!data->createParameter("Param5", i64, 5, 2)) {
		unittest::fail("ComponentData test: Component create integer coll param failed");
		delete(data);
		delete(manager);
		return false;
	}

	if (!data->getParameter("Param5", ival)) {
		unittest::fail("ComponentData test: Component get integer param coll failed");
		delete(data);
		delete(manager);
		return false;
	}

	if (ival != 3) {
		unittest::fail("ComponentData test: Component tweak integer coll param result failed");
		delete(data);
		delete(manager);
		return false;
	}

	if (!data->tweakParameter("Param5", 2)) {
		unittest::fail("ComponentData test: Component tweak integer coll param failed");
		delete(data);
		delete(manager);
		return false;
	}

	if (!data->getParameter("Param5", ival)) {
		unittest::fail("ComponentData test: Component get integer param coll 2 failed");
		delete(data);
		delete(manager);
		return false;
	}

	if (ival != 5) {
		unittest::fail("ComponentData test: Component tweak integer coll param result 2 failed");
		delete(data);
		delete(manager);
		return false;
	}

	if (data->tweakParameter("Param5", -5)) {
		unittest::fail("ComponentData test: Component tweak integer coll param failed to fail");
		delete(data);
		delete(manager);
		return false;
	}


	strcpy(test, "Test1");
	strcpy(test+6, "Test2");
	strcpy(test+12, "Test3");
	strcpy(test+18, "Test4");
	strcpy(test+24, "Test5");

	unittest::progress(78, "string collection parameter");
	if (!data->createParameter("Param6", test, 5, 2)) {
		unittest::fail("ComponentData test: Component create string coll param failed");
		delete(data);
		delete(manager);
		return false;
	}

	if (!data->getParameter("Param6", test, 1024)) {
		unittest::fail("ComponentData test: Component get string param coll failed");
		delete(data);
		delete(manager);
		return false;
	}

	if (strcmp(test, "Test3") != 0) {
		unittest::fail("ComponentData test: Component tweak string coll param result failed");
		delete(data);
		delete(manager);
		return false;
	}

	if (!data->tweakParameter("Param6", 2)) {
		unittest::fail("ComponentData test: Component tweak string coll param failed");
		delete(data);
		delete(manager);
		return false;
	}

	if (!data->getParameter("Param6", test, 1024)) {
		unittest::fail("ComponentData test: Component get string param coll 2 failed");
		delete(data);
		delete(manager);
		return false;
	}

	if (strcmp(test, "Test5") != 0) {
		unittest::fail("ComponentData test: Component tweak string coll param result 2 failed");
		delete(data);
		delete(manager);
		return false;
	}

	if (data->tweakParameter("Param6", -5)) {
		unittest::fail("ComponentData test: Component tweak string coll param failed to fail");
		delete(data);
		delete(manager);
		return false;
	}

	unittest::progress(85, "delete parameter and destroy component");
	if (!data->deleteParameter("Param3")) {
		unittest::fail("ComponentData test: Component delete float param failed");
		delete(data);
		delete(manager);
		return false;
	}

	if (!data->destroyComponent()) {
		unittest::fail("ComponentData test: Could not destroy component");
		delete(data);
		delete(manager);
		return false;
	}
	delete(data);
	

	unittest::progress(92, "type level maps");
	uint16 tidt;
	if (!MemoryMaps::CreateNewTypeLevel(1, "Type1")) {
		unittest::fail("ComponentData test: Could not create new type");
		delete(manager);
		return false;
	}

	if (!MemoryMaps::CreateNewTypeLevel(2, "Type2")) {
		unittest::fail("ComponentData test: Could not create new type 2");
		delete(manager);
		return false;
	}

	if (!MemoryMaps::GetTypeLevelID("Type2", tidt)) {
		unittest::fail("ComponentData test: Could not get type id");
		delete(manager);
		return false;
	}

	if (2 != tidt) {
		unittest::fail("ComponentData test: Type id mismatch");
		delete(manager);
		return false;
	}

	if (!MemoryMaps::GetTypeLevelName(2, test, 1024)) {
		unittest::fail("ComponentData test: Could not get type name");
		delete(manager);
		return false;
	}

	if (strcmp(test, "Type2")) {
		unittest::fail("ComponentData test: Type name mismatch");
		delete(manager);
		return false;
	}
	


	//uint32 idt;
	//if (!MemoryMaps::CreateNewTopic(1, "Topic1")) {
	//	fprintf(stderr, "Could not create new topic...\n");
	//	delete(manager);
	//	return false;
	//}

	//if (!MemoryMaps::CreateNewTopic(2, "Topic2")) {
	//	fprintf(stderr, "Could not create new topic 2...\n");
	//	delete(manager);
	//	return false;
	//}

	//if (!MemoryMaps::GetTopicID("Topic2", idt)) {
	//	fprintf(stderr, "Could not get topic id...\n");
	//	delete(manager);
	//	return false;
	//}
	//
	//if (2 != idt) {
	//	fprintf(stderr, "Topic id mismatch...\n");
	//	delete(manager);
	//	return false;
	//}

	//if (!MemoryMaps::GetTopicName(2, test, 1024)) {
	//	fprintf(stderr, "Could not get topic name...\n");
	//	delete(manager);
	//	return false;
	//}
	//
	//if (strcmp(test, "Topic2")) {
	//	fprintf(stderr, "Topic name mismatch...\n");
	//	delete(manager);
	//	return false;
	//}


	//uint64 addr = 0xaabbccdd;
	//uint64 addr2 = 0xaabbccee;
	//uint64 addrt;
	//uint16 idtt;
	//if (!MemoryMaps::CreateNewNode(1, "", addr)) {
	//	fprintf(stderr, "Could not create new Node...\n");
	//	delete(manager);
	//	return false;
	//}

	//if (!MemoryMaps::CreateNewNode(2, "", addr2)) {
	//	fprintf(stderr, "Could not create new Node 2...\n");
	//	delete(manager);
	//	return false;
	//}

	//if (!MemoryMaps::GetNodeID(addr2, idtt)) {
	//	fprintf(stderr, "Could not get Node id...\n");
	//	delete(manager);
	//	return false;
	//}
	//
	//if (2 != idtt) {
	//	fprintf(stderr, "Node id mismatch...\n");
	//	delete(manager);
	//	return false;
	//}

	//if ( (addrt = MemoryMaps::GetNodeAddress(2)) == 0 ) {
	//	fprintf(stderr, "Could not get Node address...\n");
	//	delete(manager);
	//	return false;
	//}
	//
	//if (2 != addrt) {
	//	fprintf(stderr, "Node address mismatch...\n");
	//	delete(manager);
	//	return false;
	//}

	delete [] str;
	delete [] test;
	delete [] name;
	delete(manager);
	unittest::progress(100, "done");
	return true;
}

void Register_ComponentData_Tests() {
	UnitTestRunner::instance().registerTest("componentdata", ComponentData::UnitTest,
		"Component maps: private data, parameters, collections, type levels", "memory");
}

} // namespace cmlabs
