/**
 * @file	MapPacketExtraction.cpp
 * @brief	This file defines the MapPacketExtraction class.
 * @date	26.03.2013
 * @author	baetz
 */

#include <framework/datalinklayer/MapPacketExtraction.h>
#include <framework/ipc/QueueFactory.h>
#include <framework/serviceinterface/ServiceInterfaceStream.h>
#include <framework/storagemanager/StorageManagerIF.h>
#include <framework/tmtcpacket/SpacePacketBase.h>
#include <framework/tmtcservices/AcceptsTelecommandsIF.h>
#include <framework/tmtcservices/TmTcMessage.h>
#include <string.h>

MapPacketExtraction::MapPacketExtraction(uint8_t setMapId,
		object_id_t setPacketDestination) :
		lastSegmentationFlag(NO_SEGMENTATION), mapId(setMapId), packetLength(0), bufferPosition(
				packetBuffer), packetDestination(setPacketDestination), packetStore(
				NULL), tcQueueId(MessageQueueSenderIF::NO_QUEUE) {
	memset(packetBuffer, 0, sizeof(packetBuffer));
}

ReturnValue_t MapPacketExtraction::extractPackets(TcTransferFrame* frame) {
	uint8_t segmentationFlag = frame->getSequenceFlags();
	ReturnValue_t status = TOO_SHORT_MAP_EXTRACTION;
	switch (segmentationFlag) {
	case NO_SEGMENTATION:
		status = unpackBlockingPackets(frame);
		break;
	case FIRST_PORTION:
		packetLength = frame->getDataLength();
		if (packetLength <= MAX_PACKET_SIZE) {
			memcpy(packetBuffer, frame->getDataField(), packetLength);
			bufferPosition = &packetBuffer[packetLength];
			status = RETURN_OK;
		} else {
			sif::error
					<< "MapPacketExtraction::extractPackets. Packet too large! Size: "
					<< packetLength << std::endl;
			clearBuffers();
			status = CONTENT_TOO_LARGE;
		}
		break;
	case CONTINUING_PORTION:
	case LAST_PORTION:
		if (lastSegmentationFlag == FIRST_PORTION
				|| lastSegmentationFlag == CONTINUING_PORTION) {
			packetLength += frame->getDataLength();
			if (packetLength <= MAX_PACKET_SIZE) {
				memcpy(bufferPosition, frame->getDataField(),
						frame->getDataLength());
				bufferPosition = &packetBuffer[packetLength];
				if (segmentationFlag == LAST_PORTION) {
					status = sendCompletePacket(packetBuffer, packetLength);
					clearBuffers();
				}
				status = RETURN_OK;
			} else {
				sif::error
						<< "MapPacketExtraction::extractPackets. Packet too large! Size: "
						<< packetLength << std::endl;
				clearBuffers();
				status = CONTENT_TOO_LARGE;
			}
		} else {
			sif::error
					<< "MapPacketExtraction::extractPackets. Illegal segment! Last flag: "
					<< (int) lastSegmentationFlag << std::endl;
			clearBuffers();
			status = ILLEGAL_SEGMENTATION_FLAG;
		}
		break;
	default:
		sif::error
				<< "MapPacketExtraction::extractPackets. Illegal segmentationFlag: "
				<< (int) segmentationFlag << std::endl;
		clearBuffers();
		status = DATA_CORRUPTED;
		break;
	}
	lastSegmentationFlag = segmentationFlag;
	return status;
}

ReturnValue_t MapPacketExtraction::unpackBlockingPackets(
		TcTransferFrame* frame) {
	ReturnValue_t status = TOO_SHORT_BLOCKED_PACKET;
	uint32_t totalLength = frame->getDataLength();
	if (totalLength > MAX_PACKET_SIZE)
		return CONTENT_TOO_LARGE;
	uint8_t* position = frame->getDataField();
	while ((totalLength > SpacePacketBase::MINIMUM_SIZE)) {
		SpacePacketBase packet(position);
		uint32_t packetSize = packet.getFullSize();
		if (packetSize <= totalLength) {
			status = sendCompletePacket(packet.getWholeData(),
					packet.getFullSize());
			totalLength -= packet.getFullSize();
			position += packet.getFullSize();
			status = RETURN_OK;
		} else {
			status = DATA_CORRUPTED;
			totalLength = 0;
		}
	}
	if (totalLength > 0) {
		status = RESIDUAL_DATA;
	}
	return status;
}

ReturnValue_t MapPacketExtraction::sendCompletePacket(uint8_t* data,
		uint32_t size) {
	store_address_t store_id;
	ReturnValue_t status = this->packetStore->addData(&store_id, data, size);
	if (status == RETURN_OK) {
		TmTcMessage message(store_id);
		status = MessageQueueSenderIF::sendMessage(tcQueueId,&message);
	}
	return status;
}

void MapPacketExtraction::clearBuffers() {
	memset(packetBuffer, 0, sizeof(packetBuffer));
	bufferPosition = packetBuffer;
	packetLength = 0;
	lastSegmentationFlag = NO_SEGMENTATION;
}

ReturnValue_t MapPacketExtraction::initialize() {
	packetStore = objectManager->get<StorageManagerIF>(objects::TC_STORE);
	AcceptsTelecommandsIF* distributor = objectManager->get<
			AcceptsTelecommandsIF>(packetDestination);
	if ((packetStore != NULL) && (distributor != NULL)) {
		tcQueueId = distributor->getRequestQueue();
		return RETURN_OK;
	} else {
		return RETURN_FAILED;
	}
}

void MapPacketExtraction::printPacketBuffer(void) {
	sif::debug << "DLL: packet_buffer contains: " << std::endl;
	for (uint32_t i = 0; i < this->packetLength; ++i) {
		sif::debug << "packet_buffer[" << std::dec << i << "]: 0x" << std::hex
				<< (uint16_t) this->packetBuffer[i] << std::endl;
	}
}

uint8_t MapPacketExtraction::getMapId() const {
	return mapId;
}