#include <framework/monitoring/LimitViolationReporter.h>
#include <framework/monitoring/MonitoringMessageContent.h>
#include <framework/objectmanager/ObjectManagerIF.h>
#include <framework/power/Fuse.h>
#include <framework/serialize/SerialFixedArrayListAdapter.h>
#include <framework/ipc/QueueFactory.h>

object_id_t Fuse::powerSwitchId = 0;

Fuse::Fuse(object_id_t fuseObjectId, uint8_t fuseId, VariableIds ids,
		float maxCurrent, uint16_t confirmationCount) :
		SystemObject(fuseObjectId), oldFuseState(0), fuseId(fuseId), powerIF(
		NULL), currentLimit(fuseObjectId, 1, ids.pidCurrent, confirmationCount,
				maxCurrent, FUSE_CURRENT_HIGH), powerMonitor(fuseObjectId, 2,
				DataPool::poolIdAndPositionToPid(ids.poolIdPower, 0),
				confirmationCount), set(), voltage(ids.pidVoltage, &set), current(
				ids.pidCurrent, &set), state(ids.pidState, &set), power(
				ids.poolIdPower, &set, PoolVariableIF::VAR_READ_WRITE), commandQueue(
				NULL), parameterHelper(this), healthHelper(this, fuseObjectId) {
	commandQueue = QueueFactory::instance()->createMessageQueue();
}

Fuse::~Fuse() {
	QueueFactory::instance()->deleteMessageQueue(commandQueue);
}

void Fuse::addDevice(PowerComponentIF* switchSet) {
	devices.push_back(switchSet);
}

ReturnValue_t Fuse::initialize() {
	ReturnValue_t result = SystemObject::initialize();
	if (result != HasReturnvaluesIF::RETURN_OK) {
		return result;
	}
	result = parameterHelper.initialize();
	if (result != RETURN_OK) {
		return result;
	}
	result = healthHelper.initialize();
	if (result != RETURN_OK) {
		return result;
	}
	powerIF = objectManager->get<PowerSwitchIF>(powerSwitchId);
	if (powerIF == NULL) {
		return RETURN_FAILED;
	}
	return RETURN_OK;
}

void Fuse::calculatePowerLimits(float* low, float* high) {
	for (DeviceList::iterator iter = devices.begin(); iter != devices.end();
			iter++) {
		if (areSwitchesOfComponentOn(iter)) {
			*low += (*iter)->getMin();
			*high += (*iter)->getMax();
		}
	}
}

ReturnValue_t Fuse::check() {
	set.read();
	if (!healthHelper.healthTable->isHealthy(getObjectId())) {
		setAllMonitorsToUnchecked();
		set.commit(PoolVariableIF::INVALID);
		return RETURN_OK;
	}
	ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
	checkFuseState();
	calculateFusePower();
	//Check if power is valid and if fuse state is off or invalid.
	if (!power.isValid() || (state == 0) || !state.isValid()) {
		result = powerMonitor.setToInvalid();
	} else {
		float lowLimit = 0.0;
		float highLimit = RESIDUAL_POWER;
		calculatePowerLimits(&lowLimit, &highLimit);
		result = powerMonitor.checkPower(power, lowLimit, highLimit);
		if (result == MonitoringIF::BELOW_LOW_LIMIT) {
			reportEvents(POWER_BELOW_LOW_LIMIT);
		} else if (result == MonitoringIF::ABOVE_HIGH_LIMIT) {
			reportEvents(POWER_ABOVE_HIGH_LIMIT);
		}
	}
	set.commit();
	return result;
}

ReturnValue_t Fuse::serialize(uint8_t** buffer, uint32_t* size,
		const uint32_t max_size, bool bigEndian) const {
	ReturnValue_t result = RETURN_FAILED;
	for (DeviceList::const_iterator iter = devices.begin();
			iter != devices.end(); iter++) {
		result = (*iter)->serialize(buffer, size, max_size, bigEndian);
		if (result != RETURN_OK) {
			return result;
		}
	}
	return RETURN_OK;
}

uint32_t Fuse::getSerializedSize() const {
	uint32_t size = 0;
	for (DeviceList::const_iterator iter = devices.begin();
			iter != devices.end(); iter++) {
		size += (*iter)->getSerializedSize();
	}
	return size;
}

ReturnValue_t Fuse::deSerialize(const uint8_t** buffer, int32_t* size,
bool bigEndian) {
	ReturnValue_t result = RETURN_FAILED;
	for (DeviceList::iterator iter = devices.begin(); iter != devices.end();
			iter++) {
		result = (*iter)->deSerialize(buffer, size, bigEndian);
		if (result != RETURN_OK) {
			return result;
		}
	}
	return RETURN_OK;
}

uint8_t Fuse::getFuseId() const {
	return fuseId;
}

void Fuse::calculateFusePower() {
	ReturnValue_t result1 = currentLimit.check();
	if (result1 != HasReturnvaluesIF::RETURN_OK || !(voltage.isValid())) {
		power.setValid(PoolVariableIF::INVALID);
		return;
	}
	//Calculate fuse power.
	power = current * voltage;
	power.setValid(PoolVariableIF::VALID);
}

ReturnValue_t Fuse::performOperation(uint8_t opCode) {
	checkCommandQueue();
	return HasReturnvaluesIF::RETURN_OK;
}

void Fuse::reportEvents(Event event) {
	if (!powerMonitor.isEventEnabled()) {
		return;
	}
	for (DeviceList::iterator iter = devices.begin(); iter != devices.end();
			iter++) {
		if (areSwitchesOfComponentOn(iter)) {
			EventManagerIF::triggerEvent((*iter)->getDeviceObjectId(), event);
		}
	}
}

MessageQueueId_t Fuse::getCommandQueue() const {
	return commandQueue->getId();
}

void Fuse::setAllMonitorsToUnchecked() {
	currentLimit.setToUnchecked();
	powerMonitor.setToUnchecked();
}

void Fuse::checkCommandQueue() {
	CommandMessage command;
	ReturnValue_t result = commandQueue->receiveMessage(&command);
	if (result != HasReturnvaluesIF::RETURN_OK) {
		return;
	}
	result = healthHelper.handleHealthCommand(&command);
	if (result == HasReturnvaluesIF::RETURN_OK) {
		return;
	}
	result = parameterHelper.handleParameterMessage(&command);
	if (result == HasReturnvaluesIF::RETURN_OK) {
		return;
	}
	command.setToUnknownCommand();
	commandQueue->reply(&command);
}

void Fuse::checkFuseState() {
	if (!state.isValid()) {
		oldFuseState = 0;
		return;
	}
	if (state == 0) {
		if (oldFuseState != 0) {
			reportEvents(FUSE_WENT_OFF);
		}
	}
	oldFuseState = state;
}

float Fuse::getPower() {
	if (power.isValid()) {
		return power;
	} else {
		return 0.0;
	}
}

void Fuse::setDataPoolEntriesInvalid() {
	set.read();
	set.commit(PoolVariableIF::INVALID);
}

ReturnValue_t Fuse::getParameter(uint8_t domainId, uint16_t parameterId,
		ParameterWrapper* parameterWrapper, const ParameterWrapper* newValues,
		uint16_t startAtIndex) {
	ReturnValue_t result = currentLimit.getParameter(domainId, parameterId,
			parameterWrapper, newValues, startAtIndex);
	if (result != INVALID_DOMAIN_ID) {
		return result;
	}
	result = powerMonitor.getParameter(domainId, parameterId, parameterWrapper,
			newValues, startAtIndex);
	return result;
}

bool Fuse::areSwitchesOfComponentOn(DeviceList::iterator iter) {
	if (powerIF->getSwitchState((*iter)->getSwitchId1())
			!= PowerSwitchIF::SWITCH_ON) {
		return false;
	}
	if ((*iter)->hasTwoSwitches()) {
		if ((powerIF->getSwitchState((*iter)->getSwitchId2())
				!= PowerSwitchIF::SWITCH_ON)) {
			return false;
		}
	}
	return true;
}

bool Fuse::isPowerValid() {
	return power.isValid();
}

ReturnValue_t Fuse::setHealth(HealthState health) {
	healthHelper.setHealth(health);
	return RETURN_OK;
}

HasHealthIF::HealthState Fuse::getHealth() {
	return healthHelper.getHealth();
}

ReturnValue_t Fuse::PowerMonitor::checkPower(float sample, float lowerLimit,
		float upperLimit) {
	if (sample > upperLimit) {
		return this->monitorStateIs(MonitoringIF::ABOVE_HIGH_LIMIT, sample,
				upperLimit);
	} else if (sample < lowerLimit) {
		return this->monitorStateIs(MonitoringIF::BELOW_LOW_LIMIT, sample,
				lowerLimit);
	} else {
		return this->monitorStateIs(RETURN_OK, sample, 0.0); //Within limits.
	}
}