

//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
//_/_/
//_/_/  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 "MemoryRequestQueues.h"

namespace cmlabs {

bool MemoryRequestQueues::InitQueue(char* data, uint32 size) {
	if (!data || !size) return false;
	memset(data, 0, sizeof(RequestQueueHeader));
	RequestQueueHeader* header = (RequestQueueHeader*) data;
	header->size = size;
	header->createdTime = header->lastUpdateTime = GetTimeNow();
	return true;
}

bool MemoryRequestQueues::CheckQueue(char* data, uint32 size) {
	if (!data || !size) return false;
	return ((RequestQueueHeader*) data)->size == size;
}

bool MemoryRequestQueues::AddToQueue(char* data, DataMessage* msg) {
	if (!data || !msg) return false;
	RequestQueueHeader* qHeader = (RequestQueueHeader*) data;
	uint32 msgSize = msg->getSize();

	uint32 spaceToEnd, inUse = 0, inUse2 = 0;
	char* qData = ((char*)qHeader) + sizeof(RequestQueueHeader);
	uint32 qDataSize = qHeader->size - sizeof(RequestQueueHeader);

	if (qHeader->endPos == qHeader->startPos) {
		spaceToEnd = qDataSize;
		if (spaceToEnd >= msgSize) {
			// add msg at buffer start
			memcpy(qData, msg->data, msgSize);
			qHeader->startPos = 0;
			qHeader->endPos = msgSize;
			qHeader->count++;
			qHeader->padding = 0;
		}
		else {
			// queue not big enough
			return false;
		}
	}
	else if (qHeader->endPos > qHeader->startPos) {
		spaceToEnd = qDataSize - qHeader->endPos;
		if (spaceToEnd >= msgSize) {
			// add msg here
			memcpy(qData+qHeader->endPos, msg->data, msgSize);
			qHeader->endPos += msgSize;
			qHeader->count++;
			qHeader->padding = 0;
		}
		else if (qHeader->startPos >= msgSize) {
			// fill end rest of space with 0
			memset(qData+qHeader->endPos, 0, spaceToEnd);
			qHeader->padding = spaceToEnd;
			// add msg at buffer start
			memcpy(qData, msg->data, msgSize);
			qHeader->endPos = msgSize;
			qHeader->count++;
		}
		else {
			// queue not big enough
			return false;
		}
	}
	else {
		spaceToEnd = qHeader->startPos - qHeader->endPos;
		if (spaceToEnd > msgSize) {
			// add msg here
			memcpy(qData+qHeader->endPos, msg->data, msgSize);
			qHeader->endPos += msgSize;
			qHeader->count++;
		}
		else {
			// queue not big enough
			return false;
		}
	}
	return true;
}

DataMessage* MemoryRequestQueues::GetNextQueueMessage(char* data) {
	if (!data) return NULL;
	RequestQueueHeader* qHeader = (RequestQueueHeader*) data;
	if (!qHeader->count)
		return NULL;

	DataMessage* msg = new DataMessage(((char*)qHeader) + sizeof(RequestQueueHeader) + qHeader->startPos, true);
	qHeader->startPos += msg->getSize();
	if (qHeader->startPos >= qHeader->size - sizeof(RequestQueueHeader) - qHeader->padding) {
		qHeader->startPos = 0;
		qHeader->padding = 0;
	}
	qHeader->count--;
	return msg;
}







bool MemoryRequestQueues::InitRequestMap(char* data, uint32 count) {
	if (!data || !count) return false;

	uint32 requestsBitFieldSize = utils::Calc32BitFieldSize(count);
	uint32 requestsSize = sizeof(RequestStatusHeader) + requestsBitFieldSize + count*sizeof(RequestStatusEntry);

	memset(data, 0, sizeof(RequestStatusHeader));
	RequestStatusHeader* header = (RequestStatusHeader*) data;
	header->countTotal = count;
	header->size = requestsSize;
	header->bitFieldSize = requestsBitFieldSize;
	header->createdTime = header->lastUpdateTime = GetTimeNow();

	memset(((char*)header)+sizeof(RequestStatusHeader), 255, requestsBitFieldSize);
	// Position 0 is not allowed, used to indicate error or unused	
	utils::SetBit(0, BITOCCUPIED, ((char*)header + sizeof(RequestStatusHeader)), header->bitFieldSize);

	return true;
}

bool MemoryRequestQueues::CheckRequestMap(char* data, uint32 count) {
	if (!data || !count) return false;
	return ((RequestQueueHeader*) data)->count == count;
}

uint64 MemoryRequestQueues::AddNewRequest(char* data, uint16 status, uint32 from, uint32 to) {
	if (!data) return false;

	RequestStatusHeader* header = (RequestStatusHeader*) data;
	if (header->countInUse >= header->countTotal-1)
		return 0;

	uint32 loc;
	if (!utils::GetFirstFreeBitLoc(((char*)header + sizeof(RequestStatusHeader)), header->bitFieldSize, loc))
		return 0;
	if (loc >= header->countTotal)
		return 0;

	uint64 reqID = (uint64)loc;
	RequestStatusEntry* entry = GetRequestEntry(data, reqID);
	if (!entry)
		return 0;

	// Reserve it
	utils::SetBit(loc, BITOCCUPIED, ((char*)header + sizeof(RequestStatusHeader)), header->bitFieldSize);
	header->countInUse++;

	entry->id = reqID;
	entry->status = status;
	entry->createdTime = entry->lastUpdateTime = GetTimeNow();
	entry->from = from;
	entry->to = to;
	return reqID;
}

bool MemoryRequestQueues::SetRequestStatus(char* data, uint64 id, uint16 status) {
	RequestStatusEntry* entry = GetRequestEntry(data, id);
	if (!entry)
		return false;
	entry->status = status;
	return true;
}

uint32 MemoryRequestQueues::GetRequestStatus(char* data, uint64 id, uint64& createdTime, uint64& lastUpdateTime) {
	RequestStatusEntry* entry = GetRequestEntry(data, id);
	if (!entry)
		return false;
	return entry->status;
}


bool MemoryRequestQueues::CloseRequest(char* data, uint64 id) {
	RequestStatusHeader* header = (RequestStatusHeader*) data;
	RequestStatusEntry* entry = GetRequestEntry(data, id);
	if (!entry)
		return false;
	memset(entry, 0, sizeof(RequestStatusEntry));
	// clear bitfield entry
	utils::SetBit((uint32)id, BITFREE, ((char*)header + sizeof(RequestStatusHeader)), header->bitFieldSize);
	header->countInUse--;
	return true;
}

RequestStatusEntry* MemoryRequestQueues::GetRequestEntry(char* data, uint64 id) {
	if (data == NULL)
		return NULL;
	RequestStatusEntry* entry = NULL;
	RequestStatusHeader* header = (RequestStatusHeader*) data;

	// Calc offset
	uint32 offset = sizeof(RequestStatusHeader) + header->bitFieldSize + (uint32)id * sizeof(RequestStatusEntry);
	if (offset > header->size)
		return NULL;
	entry = (RequestStatusEntry*) (data+offset);
	if ((entry->id == id) && (entry->createdTime != 0))
		return entry;
	else if (!entry->id && !entry->createdTime)
		return entry;
	else
		return NULL;
}

} // namespace cmlabs
