#include "Channel.h"
#include <stdio.h>

#define MAX_BUFFER_LENGTH 100

void Channel :: init_channel() {
	maxBufferLength = MAX_BUFFER_LENGTH;
	transferRate = MAX_BUFFER_LENGTH;
}

Channel :: Channel() {
	init_channel();
}

Channel :: Channel(const char* name) {
	set_name(name);
	init_channel();
}

bool Channel :: set_maxBufferLength(unsigned int maxBufferLength) {
	if(maxBufferLength <= MAX_BUFFER_LENGTH) {
		this->maxBufferLength = maxBufferLength;
		return true;
	}
	return false;
}

unsigned int Channel :: get_maxBufferLength() {
	return maxBufferLength;
}

unsigned int Channel :: get_avlInputBufferUp() {
	return maxBufferLength-lInputMsgBufferUp.size();
}

unsigned int Channel :: get_avlOutputBufferUp() {
	return maxBufferLength-lOutputMsgBufferUp.size();
}

unsigned int Channel :: get_avlInputBufferDown() {
	return maxBufferLength-lInputMsgBufferDown.size();
}

unsigned int Channel :: get_avlOutputBufferDown() {
	return maxBufferLength-lOutputMsgBufferDown.size();
}

bool Channel :: set_transferRate(unsigned int transferRate) {
	if(transferRate <= MAX_BUFFER_LENGTH) {
		this->transferRate = transferRate;
		return true;
	}
	return false;
}

unsigned int Channel :: get_transferRate() {
	return transferRate;
}



bool Channel :: add_msgAtBegin (list<Message*>* buffer, Message* message) {
	try {
		buffer->push_front(message);
	}
	catch(bad_alloc& error) {
		delete message;
		return false;
	}
	return true;
}

bool Channel :: del_msgAtBegin (list<Message*>* buffer) {
	try {
		buffer->pop_front();
	}
	catch(bad_alloc& error) {
		return false;
	}
	return true;
}

bool Channel :: add_msgAtEnd (list<Message*>* buffer, Message* message) {
	try {
		buffer->push_back(message);
	}
	catch(bad_alloc& error) {
		delete message;
		return false;
	}
	return true;
}

bool del_msgAtEnd (list<Message*>* buffer) {
	try {
		buffer->pop_back();
	}
	catch(bad_alloc& error) {
		return false;
	}
	return true;
}

bool Channel :: send_MsgUp(Message* message) {
	if(message != NULL) {
		if(transferRate == 0) {
			//TODO: at the moment only one packet (in the front) gets deleted if buffer is full. However, the whole message (instruction+value) should be deleted in case of a full buffer
			if(lOutputMsgBufferUp.size() == maxBufferLength) {
				del_msgAtBegin(&lOutputMsgBufferUp);
			}
			return add_msgAtEnd(&lOutputMsgBufferUp, message);
		}
		else {
			if(lInputMsgBufferUp.size() == maxBufferLength) { 
				//TODO: at the moment only one packet (in the front) gets deleted if buffer is full. However, the whole message (instruction+value) should be deleted in case of a full buffer
				del_msgAtBegin(&lInputMsgBufferUp);
			}
			return add_msgAtEnd(&lInputMsgBufferUp, message);
		}
	}
  // FIXME
  return false;
}

bool Channel :: send_MsgUp(float msg) {
	Message* message = new Message(msg);
	return send_MsgUp(message);
}

bool Channel :: send_MsgUp(int msg) {
	Message* message = new Message(msg);
	return send_MsgUp(message);
}

bool Channel :: get_MsgUp(float* msg) {
	if(isThereFloatMsgUp()) {
		float tempMsg;
		if(lOutputMsgBufferUp.front()->getMsg(&tempMsg)) {
			*msg = tempMsg;
			del_msgAtBegin(&lOutputMsgBufferUp);
			return true;
		}
	}
	return false;
}

bool Channel :: get_MsgUp(int* msg) {
	if(isThereIntMsgUp()) {
		int tempMsg;
		if(lOutputMsgBufferUp.front()->getMsg(&tempMsg)) {
			*msg = tempMsg;
			del_msgAtBegin(&lOutputMsgBufferUp);
			return true;
		}
	}
	return false;
}

bool Channel :: isThereFloatMsgUp() {
	if(lOutputMsgBufferUp.size() > 0 ) {
		if(lOutputMsgBufferUp.front() != NULL) {
			return lOutputMsgBufferUp.front()->isMsgFloat();
		}
	}
	return false;
}

bool Channel :: isThereIntMsgUp() {
	if(lOutputMsgBufferUp.size() > 0 ) {
		if(lOutputMsgBufferUp.front() != NULL) {
			return lOutputMsgBufferUp.front()->isMsgInt();
		}
	}
	return false;
}

bool Channel :: send_MsgDown(Message* message) {
	if(message != NULL) {
		if(transferRate == 0) {
			if(lOutputMsgBufferDown.size() == maxBufferLength) { 
				del_msgAtBegin(&lOutputMsgBufferDown);
			}
			return add_msgAtEnd(&lOutputMsgBufferDown, message);
		}
		else {
			if(lInputMsgBufferDown.size() == maxBufferLength) { 
				del_msgAtBegin(&lInputMsgBufferDown);
			}
			return add_msgAtEnd(&lInputMsgBufferDown, message);
		}
	}
  //FIXME
  return false;
}

bool Channel :: send_MsgDown(float msg) {
	Message* message = new Message(msg);
	return send_MsgDown(message);
}

bool Channel :: send_MsgDown(int msg) {
	Message* message = new Message(msg);
	return send_MsgDown(message);
}

bool Channel :: get_MsgDown(float* msg) {
	if(isThereFloatMsgDown()) {
		float tempMsg;
		if(lOutputMsgBufferDown.front()->getMsg(&tempMsg)) {
			*msg = tempMsg;
			del_msgAtBegin(&lOutputMsgBufferDown);
			return true;
		}
	}
	return false;
}

bool Channel :: get_MsgDown(int* msg) {
	if(isThereIntMsgDown()) {
		int tempMsg;
		if(lOutputMsgBufferDown.front()->getMsg(&tempMsg)) {
			*msg = tempMsg;
			del_msgAtBegin(&lOutputMsgBufferDown);
			return true;
		}
	}
	return false;
}

bool Channel :: isThereFloatMsgDown() {
	if(lOutputMsgBufferDown.size() > 0 ) {
		if(lOutputMsgBufferDown.front() != NULL) {
			return lOutputMsgBufferDown.front()->isMsgFloat();
		}
	}
	return false;
}

bool Channel :: isThereIntMsgDown() {
	if(lOutputMsgBufferDown.size() > 0 ) {
		if(lOutputMsgBufferDown.front() != NULL) {
			return lOutputMsgBufferDown.front()->isMsgInt();
		}
	}
	return false;
}

bool Channel :: transferMsgs(list<Message*>* dest_buffer, list<Message*>* src_buffer) {	
	unsigned int NumOfMsgsToMove;

	if(transferRate <= src_buffer->size()) {
		NumOfMsgsToMove = transferRate;
	}
	else {
		NumOfMsgsToMove = src_buffer->size();
	}

	if(NumOfMsgsToMove <= maxBufferLength-dest_buffer->size()) {
		for(unsigned int i=0; i<NumOfMsgsToMove; i++) {	
			if(add_msgAtEnd(dest_buffer, src_buffer->front())) {
				if(!del_msgAtBegin(src_buffer)) {
					return false;
				}
			}
			else {
				return false;
			}
		}
		return true;
	}
	return false;
}

bool Channel :: trigger() {
	bool flag_worked = transferMsgs(&lOutputMsgBufferUp, &lInputMsgBufferUp) && transferMsgs(&lOutputMsgBufferUp, &lInputMsgBufferUp);
	printf("Channel %s: in_up %zu, out_up %zu, in_dn %zu, out_dn %zu,\n", name, lInputMsgBufferUp.size(), lOutputMsgBufferUp.size(), lInputMsgBufferDown.size(), lOutputMsgBufferDown.size());
	return flag_worked;
}
