#include "ParameterHelper.h"
#include "ParameterMessage.h"
#include "../objectmanager/ObjectManagerIF.h"

ParameterHelper::ParameterHelper(ReceivesParameterMessagesIF* owner) :
		owner(owner) {}

ParameterHelper::~ParameterHelper() {
}

ReturnValue_t ParameterHelper::handleParameterMessage(CommandMessage *message) {
    if(storage == nullptr) {
        // ParameterHelper was not initialized
        return HasReturnvaluesIF::RETURN_FAILED;
    }

	ReturnValue_t result = HasReturnvaluesIF::RETURN_FAILED;
	switch (message->getCommand()) {
	case ParameterMessage::CMD_PARAMETER_DUMP: {
		ParameterWrapper description;
		uint8_t domain = HasParametersIF::getDomain(
				ParameterMessage::getParameterId(message));
		uint8_t uniqueIdentifier = HasParametersIF::getUniqueIdentifierId(
				ParameterMessage::getParameterId(message));
		result = owner->getParameter(domain, uniqueIdentifier,
				&description, &description, 0);
		if (result == HasReturnvaluesIF::RETURN_OK) {
			result = sendParameter(message->getSender(),
					ParameterMessage::getParameterId(message), &description);
		}
	}
		break;
	case ParameterMessage::CMD_PARAMETER_LOAD: {
	    ParameterId_t parameterId = 0;
	    uint8_t ptc = 0;
	    uint8_t pfc = 0;
	    uint8_t rows = 0;
	    uint8_t columns = 0;
	    store_address_t storeId = ParameterMessage::getParameterLoadCommand(
	            message, &parameterId, &ptc, &pfc, &rows, &columns);
	    Type type(Type::getActualType(ptc, pfc));

		uint8_t domain = HasParametersIF::getDomain(parameterId);
		uint8_t uniqueIdentifier = HasParametersIF::getUniqueIdentifierId(
		        parameterId);
		uint16_t linearIndex = HasParametersIF::getIndex(parameterId);

		ConstStorageAccessor accessor(storeId);
		result = storage->getData(storeId, accessor);
		if (result != HasReturnvaluesIF::RETURN_OK) {
			sif::error << "ParameterHelper::handleParameterMessage: Getting"
					<< " store data failed for load command." << std::endl;
			break;
		}

		ParameterWrapper streamWrapper;
		result = streamWrapper.set(type, rows, columns, accessor.data(),
		        accessor.size());
		if(result != HasReturnvaluesIF::RETURN_OK) {
		    return result;
		}

		ParameterWrapper ownerWrapper;
		result = owner->getParameter(domain, uniqueIdentifier, &ownerWrapper,
				&streamWrapper, linearIndex);

		result = ownerWrapper.copyFrom(&streamWrapper, linearIndex);
		if (result != HasReturnvaluesIF::RETURN_OK) {
		    return result;
		}

        result = sendParameter(message->getSender(),
                ParameterMessage::getParameterId(message), &ownerWrapper);
		break;
	}
	default:
		return HasReturnvaluesIF::RETURN_FAILED;
	}

	if (result != HasReturnvaluesIF::RETURN_OK) {
		rejectCommand(message->getSender(), result, message->getCommand());
	}

	return HasReturnvaluesIF::RETURN_OK;
}

ReturnValue_t ParameterHelper::sendParameter(MessageQueueId_t to, uint32_t id,
		const ParameterWrapper* description) {
	size_t serializedSize = description->getSerializedSize();

	uint8_t *storeElement;
	store_address_t address;

	ReturnValue_t result = storage->getFreeElement(&address, serializedSize,
			&storeElement);
	if (result != HasReturnvaluesIF::RETURN_OK) {
		return result;
	}

	size_t storeElementSize = 0;

	result = description->serialize(&storeElement, &storeElementSize,
			serializedSize, SerializeIF::Endianness::BIG);

	if (result != HasReturnvaluesIF::RETURN_OK) {
		storage->deleteData(address);
		return result;
	}

	CommandMessage reply;

	ParameterMessage::setParameterDumpReply(&reply, id, address);

	MessageQueueSenderIF::sendMessage(to, &reply, ownerQueueId);

	return HasReturnvaluesIF::RETURN_OK;
}

ReturnValue_t ParameterHelper::initialize() {
    ownerQueueId = owner->getCommandQueue();

    storage = objectManager->get<StorageManagerIF>(objects::IPC_STORE);
    if (storage == nullptr) {
        return ObjectManagerIF::CHILD_INIT_FAILED;
    }
    return HasReturnvaluesIF::RETURN_OK;
}

void ParameterHelper::rejectCommand(MessageQueueId_t to, ReturnValue_t reason,
		Command_t initialCommand) {
	CommandMessage reply;
	reply.setReplyRejected(reason, initialCommand);
	MessageQueueSenderIF::sendMessage(to, &reply, ownerQueueId);
}