

//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
//_/_/
//_/_/  This file is part of CMSDK version 2.0.0.170915_Beta4 (15 September 2017)
//_/_/  
//_/_/  developed by
//_/_/  
//_/_/  Communicative Machines Ltd. 
//_/_/    http://cmlabs.com/
//_/_/  Copyright (c) 2017
//_/_/  
//_/_/  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.
//_/_/
//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
#include "Utils.h"

namespace cmlabs {

template <typename T, typename ID>
T* GenericMemoryMap<T,ID>::CreateEntry(char* data, ID id, const char* name) {
	T* entry = NULL;
	if (data == NULL) {
		fprintf(stderr, "CreateEntry %u no data provided\n", id);
		return NULL;
	}
	GenericMapHeader* header = (GenericMapHeader*)data;

	if (id >= header->maxID) {
		fprintf(stderr, "CreateEntry bit location %u out of range\n", id);
		return NULL;
	}

	//printf("{%X} {%X} {%X} {%X}\n", 
	//	*(uint32*)((char*)header + sizeof(GenericMapHeader)),
	//	*(uint32*)((char*)header + sizeof(GenericMapHeader) + 4),
	//	*(uint32*)((char*)header + sizeof(GenericMapHeader) + 8),
	//	*(uint32*)((char*)header + sizeof(GenericMapHeader) + 12)	); fflush(stdout);

	bit check;
	if (!utils::GetBit(id, ((char*)header + sizeof(GenericMapHeader)), header->bitFieldSize, check) || (check == BITOCCUPIED)) {
		//fprintf(stderr, "CreateEntry bit location %u already in use\n", id);
		// Already in use, let's check if it is the same one
		entry = GetEntry(data, id);
		if (entry && (strcmp(name, entry->name) == 0)) {
			// Same entry name, return this existing one
			return entry;
		}
		else
			return NULL;
	}

	// Reserve it
	utils::SetBit(id, BITOCCUPIED, ((char*)header + sizeof(GenericMapHeader)), header->bitFieldSize);

	// Calc offset
	uint32 offset = sizeof(GenericMapHeader) + header->bitFieldSize + id * sizeof(T);
	if (offset > header->size) {
		fprintf(stderr, "CreateEntry offset error in Memory Map\n");
		return NULL;
	}
	entry = (T*) (data+offset);
	if ((entry->id == id) || (entry->time == 0)) {
		entry->id = id;
		entry->status = 1; // only assigned, not ready
		entry->time = GetTimeNow();
		if (name != NULL)
			utils::strcpyavail(entry->name, name, MAXKEYNAMELEN, true);
		header->count++;
		return entry;
	}
	else {
		fprintf(stderr, "CreateEntry entry error in Memory Map\n");
		return NULL;
	}
}


template <typename T, typename ID>
T* GenericMemoryMap<T,ID>::CreateFirstFreeEntry(char* data, ID &id, const char* name) {
	T* entry = NULL;
	if (data == NULL)
		return NULL;
	GenericMapHeader* header = (GenericMapHeader*)data;

	//printf("{%X} {%X} {%X} {%X}\n", 
	//	*(uint32*)((char*)header + sizeof(GenericMapHeader)),
	//	*(uint32*)((char*)header + sizeof(GenericMapHeader) + 4),
	//	*(uint32*)((char*)header + sizeof(GenericMapHeader) + 8),
	//	*(uint32*)((char*)header + sizeof(GenericMapHeader) + 12)	); fflush(stdout);

	uint32 loc;
	if (!utils::GetFirstFreeBitLoc(((char*)header + sizeof(GenericMapHeader)), header->bitFieldSize, loc) || (loc >= header->maxID))  {
		fprintf(stderr, "No more bit locations available\n");
		return NULL;
	}

	// Reserve it
	id = (ID)loc;
	utils::SetBit(id, BITOCCUPIED, ((char*)header + sizeof(GenericMapHeader)), header->bitFieldSize);

	// Calc offset
	uint32 offset = sizeof(GenericMapHeader) + header->bitFieldSize + id * sizeof(T);
	if (offset > header->size) {
		fprintf(stderr, "Offset error in Memory Map\n");
		return NULL;
	}
	entry = (T*) (data+offset);
	if ((entry->id == id) || (entry->time == 0)) {
		entry->id = id;
		entry->status = 1; // only assigned, not ready
		entry->time = GetTimeNow();
		if (name != NULL)
			utils::strcpyavail(entry->name, name, MAXKEYNAMELEN, true);
		header->count++;
		return entry;
	}
	else {
		fprintf(stderr, "Entry error in Memory Map\n");
		return NULL;
	}
}

template <typename T, typename ID>
bool GenericMemoryMap<T,ID>::DeleteEntry(char* data, ID id) {
	T* entry = NULL;
	if (data == NULL)
		return false;
	GenericMapHeader* header = (GenericMapHeader*)data;

	// Calc offset
	uint32 offset = sizeof(GenericMapHeader) + header->bitFieldSize + id * sizeof(T);
	if (offset > header->size)
		return false;
	entry = (T*) (data+offset);
	if ((entry->id != id) || (entry->time == 0))
		return false;

	// Free it
	entry->time = 0;
	header->count--;
	utils::SetBit(id, BITFREE, ((char*)header + sizeof(GenericMapHeader)), header->bitFieldSize);
	return true;
}


template <typename T, typename ID>
uint64 GenericMemoryMap<T,ID>::GetEntryTime(char* data, ID id) {
	T* entry = GetEntry(data, id);
	if (entry == NULL)
		return 0;
	return entry->time;
}

template <typename T, typename ID>
T* GenericMemoryMap<T,ID>::GetEntry(char* data, ID id) {
	T* entry = NULL;
	if (data == NULL)
		return NULL;
	GenericMapHeader* header = (GenericMapHeader*)data;

	// Calc offset
	uint32 offset = sizeof(GenericMapHeader) + header->bitFieldSize + id * sizeof(T);
	if (offset > header->size)
		return NULL;
	entry = (T*) (data+offset);
	if ((entry->id == id) && (entry->time != 0) && (entry->status > 1))
		return entry;
	else
		return NULL;
}

template <typename T, typename ID>
bool GenericMemoryMap<T,ID>::ConfirmEntry(char* data, ID id) {
	T* entry = NULL;
	if (data == NULL)
		return false;
	GenericMapHeader* header = (GenericMapHeader*)data;

	// Calc offset
	uint32 offset = sizeof(GenericMapHeader) + header->bitFieldSize + id * sizeof(T);
	if (offset > header->size)
		return false;
	entry = (T*) (data+offset);
	if ((entry->id == id) && entry->time && (entry->status >= 1)) {
		entry->status = 2;
		return true;
	}
	else
		return false;
}

template <typename T, typename ID>
bool GenericMemoryMap<T,ID>::CancelEntry(char* data, ID id) {
	T* entry = NULL;
	if (data == NULL)
		return false;
	GenericMapHeader* header = (GenericMapHeader*)data;

	// Calc offset
	uint32 offset = sizeof(GenericMapHeader) + header->bitFieldSize + id * sizeof(T);
	if (offset > header->size)
		return false;
	entry = (T*) (data+offset);
	if ((entry->id == id) && entry->time && (entry->status <= 1)) {
		entry->status = 0;
		entry->time = 0;
		return true;
	}
	else if ((entry->id == 0) || (entry->status == 0))
		return true;
	else
		return false;
}

template <typename T, typename ID>
bool GenericMemoryMap<T,ID>::GetEntryName(char* data, ID id, char* name, uint32 maxSize) {
	T* entry = GetEntry(data, id);
	if (entry == NULL)
		return NULL;
	return (utils::strcpyavail(name, entry->name, maxSize, true) > 0);
}

template <typename T, typename ID>
ID GenericMemoryMap<T,ID>::GetEntryID(char* data, const char* name, bool force) {
	T* entry = NULL;
	if (data == NULL)
		return 0;
	GenericMapHeader* header = (GenericMapHeader*)data;

	entry = (T*) (data + sizeof(GenericMapHeader) + header->bitFieldSize);
	while ((uint32)((char*)entry - data) < header->size) {
		if (stricmp(name, entry->name) == 0) {
			if ((entry->status > 1) || force)
				return entry->id;
			else
				return 0;
		}
		entry = entry + 1; //sizeof(TypeMapEntry);
	}
	return 0;
}

template <typename T, typename ID>
uint8 GenericMemoryMap<T,ID>::LookupEntryID(char* data, const char* name, ID& id) {
	T* entry = NULL;
	if (data == NULL)
		return 0;
	GenericMapHeader* header = (GenericMapHeader*)data;

	uint32 n = 0;
	entry = (T*) (data + sizeof(GenericMapHeader) + header->bitFieldSize);
	while ((uint32)((char*)entry - data) < header->size) {
		if (stricmp(name, entry->name) == 0) {
			id = entry->id;
			return entry->status;
		}
		n++;
		entry = entry + 1; //sizeof(TypeMapEntry);
	}
	return 0;
}

template <typename T, typename ID>
std::string GenericMemoryMap<T,ID>::PrintAllEntries(char* data) {
	std::string str;
	T* entry = NULL;
	if (data == NULL)
		return 0;
	GenericMapHeader* header = (GenericMapHeader*)data;

	entry = (T*) (data + sizeof(GenericMapHeader) + header->bitFieldSize);
	while ((uint32)((char*)entry - data) < header->size) {
		if (entry->id) {
			if (entry->status > 1)
				str += utils::StringFormat("%u = %s (%s)\n", entry->id, entry->name, PrintTimeString(entry->time).c_str());
			else
				str += utils::StringFormat("*%u = %s (%s)\n", entry->id, entry->name, PrintTimeString(entry->time).c_str());
		}
		entry = entry + 1; //sizeof(TypeMapEntry);
	}
	return str;
}

template <typename T, typename ID>
std::string GenericMemoryMap<T,ID>::PrintAllEntriesHTML(char* data) {
	std::string str;
	T* entry = NULL;
	if (data == NULL)
		return 0;
	GenericMapHeader* header = (GenericMapHeader*)data;

	entry = (T*) (data + sizeof(GenericMapHeader) + header->bitFieldSize);
	while ((uint32)((char*)entry - data) < header->size) {
		if (entry->id) {
			if (entry->status > 1)
				str += utils::StringFormat("<tr><td>%u</td><td>%s</td><td>%s</td></tr>", entry->id, entry->name, PrintTimeString(entry->time).c_str());
			else
				str += utils::StringFormat("<tr><td>%**u</td><td>%s</td><td>%s</td></tr>\n", entry->id, entry->name, PrintTimeString(entry->time).c_str());
		}
		entry = entry + 1; //sizeof(TypeMapEntry);
	}
	return str;
}

template <typename T, typename ID>
std::string GenericMemoryMap<T,ID>::ToXML(char* data) {
	std::string str;
	T* entry = NULL;
	if (data == NULL)
		return 0;
	GenericMapHeader* header = (GenericMapHeader*)data;

	uint32 count = 0;
	entry = (T*) (data + sizeof(GenericMapHeader) + header->bitFieldSize);
	while ((uint32)((char*)entry - data) < header->size) {
		if (entry->id) {
			str += entry->toXML();
			count++;
		}
		entry = entry + 1; //sizeof(TypeMapEntry);
	}
	std::string outerName = T::GetOuterXMLName();
	if (!count)
		return utils::StringFormat("<%s count=\"%u\" />\n", outerName.c_str(), count);
	else
		return utils::StringFormat("<%s count=\"%u\">\n%s</%s>\n", outerName.c_str(), count, str.c_str(), outerName.c_str());
}


template <typename T, typename ID>
bool GenericMemoryMap<T, ID>::WriteAllEntriesToMsg(char* data, DataMessage* msg) {
	T* entry = NULL;
	if (data == NULL)
		return false;
	GenericMapHeader* header = (GenericMapHeader*)data;

	uint32 n = 0;
	entry = (T*)(data + sizeof(GenericMapHeader) + header->bitFieldSize);
	while ((uint32)((char*)entry - data) < header->size) {
		if (entry->id) {
			if (!entry->writeToMsg(n, msg))
				return false;
			n++;
		}
		entry = entry + 1; //sizeof(TypeMapEntry);
	}
	return true;
}


template <typename T, typename ID>
uint32 GenericMemoryMap<T,ID>::GetCount(char* data) {
	if (data == NULL)
		return false;
	return ((GenericMapHeader*)data)->count;
}

template <typename T, typename ID>
uint32 GenericMemoryMap<T,ID>::GetUsage(char* data) {
	if (data == NULL)
		return 0;
	return ((GenericMapHeader*)data)->count * sizeof(T);
}

} // namespace cmlabs
