/**
	@file		dsrProcess.c
	@brief 		File containing the source code of dsr protocol handling functions
	@author		Adrían Antoñana Gómez
	@author		Carlos Díaz Cánovas
	@version	rc-2008/12/07

	@warning	This file depends on libscatterweb and can not be used standalone

*/

#include <ScatterWeb.System.h>
#include <ScatterWeb.CC1020.h>
#include "constants.h"
#include "dsrProcess.h"
#include "dsr.h"
#include "types.h"

uint8_t reqNum = 0;
uint8_t sentNum = 0;
uint8_t sent[MAX_SENT];


/**
	Cleans the sent list, requests sent are stored in an global array
	with the number of requests sent and the next request number. This
	function resets the counters

	@brief		Cleans the sent request-ids "cache"
	@return		void
*/
void clean() {

	sentNum = 0;
	reqNum = 0;

}


/**
	Checks if a number is in a list (generic), returns 0
	if the number is not in the list, 1 if it is

	@brief		Checks for a number in a list
	@param[in]	search		Number to search
	@param[in]	*in		List to search in
	@param[in]	listLen		Length of the list
	@return		int		0 if the number is not in the list 1 if it is
*/
int inList(uint8_t search, uint8_t *in, uint8_t listLen) {
int i = 0;
	
	for(i=0; i<listLen; i++) {
		if (in[i] == search) return 1;
	
	}

return 0;
}


/**
	Start a route request sending the first message to all the node in broadcast mode.
	Broadcast mode is set as being 100 the destination

	@brief		Starts a route request
	@param[in]	thisNode	ID of the actual node
	@param[in]	dest		Final destination
	@param[in]	id		ID of the request, if 0 is provided then a new one is generated
	@return		uint8_t		0 is everything is OK, BUFFER_FULL if the request-ids cache is full
*/
uint8_t dsrStartRouteRequest(uint8_t thisNode, uint8_t dest, uint8_t id) {
uint8_t source = 0, requestId = 0;
uint8_t packet[MAX_ROUTE_LEN];
char data[1];
_dsr_packet p;
netpacket_send_args_t npsargs;
int i = 0;


	/*A maximum number of requests ID can be stored*/
	if (sentNum == MAX_SENT) return BUFFER_FULL;

	/*Now, time to build the packet of the route request*/
	source = (uint8_t) thisNode;
	data[0] = source;

	/*If id is 0 we generate one*/
	if (!id) {
		srand(reqNum++);
	
		do {
			requestId = 1 + (rand() % 255);
		} while(inList(requestId, sent, sentNum));
	
		requestId = (uint8_t) reqNum++;
	
		/*And let's add the new reqid to the "black list"*/
		sent[sentNum] = requestId;
		sentNum++;
	}
	else requestId = id;

	p = _dsrMakePacket(_dsrMakeHeader(source, dest, requestId, ROUTE_REQUEST, 0, 0, 1), _dsrMakePayload((uint8_t *)data));
	_dsrMakeFrame(p, packet);

	/*Packet maid, we send it*/
	Net_sendArgsInit(&npsargs);
	npsargs.netheader.to = BCAST;
	npsargs.netheader.type = USERDEFINED_PACKET+ROUTE;
	npsargs.netheader.flags = 0;
	npsargs.payload[0].buffer = (uint8_t*) packet;
	npsargs.payload[0].size = HEADER_LEN + 1;
	Net_send(&npsargs);

	/*Clean in X seconds*/
	Timers_add(REFRESH_T*1024, clean, 0xFFFF);

	DEBUGM("to = %d, len = %d, type = %X\n", BCAST, HEADER_LEN+1, npsargs.netheader.type);

return 0;
}


/**
	This function is called everytime a broadcast message is received.
	If the message on the packet is a route request if processes it
	resending to the nodes in range or starting a route reply if the
	final node has been reached.

	@brief		Processes a received route request
	@param[in]	*data		The received packet
	@param[in]	thisNode	The actual node ID
	@return		uint8_t		0 is everything is OK, BUFFER_FULL if the request-ids cache is full

*/
uint8_t dsrProcessRouteRequest(uint8_t *data, uint8_t thisNode) {
_dsr_packet p;
uint8_t source, dest, jump, requestId, payloadLen;
uint8_t payload[MAX_ROUTE_LEN], packet[MAX_ROUTE_LEN];
int i = 0;
netpacket_send_args_t npsargs;


	/*Get the request id from the received packet*/
	requestId = data[POS_REQ_ID];

	DEBUGM("reqId = %d - inList = %d - reqNum = %d - sentNum = %d\n", requestId, inList(requestId, sent, sentNum), reqNum, sentNum);

	/*RReq*/
	if (data[POS_TYPE] == ROUTE_REQUEST && !inList(requestId, sent, sentNum)) {

//		DEBUGM("Route Request received\n");

		/*First let's add the new reqid to the "black list"*/
		sent[sentNum] = requestId;
		sentNum++;

		/*Clean in X seconds*/
		Timers_add(REFRESH_T*1024, clean, 0xFFFF);

		/*Past X seconds the buffer will be cleaned*/
		if (sentNum == MAX_SENT) return BUFFER_FULL;

		source = thisNode;
		dest = data[POS_DST];
		payloadLen = data[POS_PAYLOAD_LEN] + 1;

		/*Let's generate the payload, we first copy the existing one*/		
		for (i = 0; i < payloadLen; i++) {
			payload[i] = data[HEADER_LEN+i];
		}
		/*And then add the new node (this one !) to the list*/
		payload[payloadLen-1] = source;

		/*If the final destination is this node, start a RReply*/
		if (data[POS_DST] == thisNode) {
			DEBUGM("Destination reached, starting route reply...\n");

			/*We create the packet structure*/
			jump = payloadLen - 2;
			dest = payload[0];
			p = _dsrMakePacket(_dsrMakeHeader(source, dest, requestId, ROUTE_REPLY, jump, 0, payloadLen),\
			_dsrMakePayload((uint8_t *)payload));

			/*Then the packet array*/
			_dsrMakeFrame(p, packet);

			/*And send, finally, but just to one node !*/
			Net_sendArgsInit(&npsargs);
			npsargs.netheader.to = payload[jump];
			npsargs.netheader.type = USERDEFINED_PACKET+ROUTE;
			npsargs.netheader.flags = 0;
			npsargs.payload[0].buffer = (uint8_t*) packet;
			npsargs.payload[0].size = HEADER_LEN + payloadLen;
			Net_send(&npsargs);

		}
		/*If not, just rebroadcast*/
		else {
			/*We created the packet structure*/
			p = _dsrMakePacket(_dsrMakeHeader(source, dest, requestId, ROUTE_REQUEST, 0, 0, payloadLen),\
			_dsrMakePayload((uint8_t *)payload));
	
			/*Then the packet array*/
			_dsrMakeFrame(p, packet);

			/*And send, finally*/
			Net_sendArgsInit(&npsargs);
			npsargs.netheader.to = BCAST;
			npsargs.netheader.type = USERDEFINED_PACKET+ROUTE;
			npsargs.netheader.flags = 0;
			npsargs.payload[0].buffer = (uint8_t*) packet;
			npsargs.payload[0].size = HEADER_LEN + payloadLen;
			Net_send(&npsargs);

//			DEBUGM("Rebroadcasted route request.\n");

		}
	
	}

return 0;
}


/**
	Ths function is called everytime a ROUTE packet is received for this node
	specifically. It bounces the route reply to the next node on the packet list
	or annouces a received route if this is for the actual node.

	@brief		Processes a route reply
	@param[in]	*data		Packet received
	@param[in]	thisNode	The actual node ID
	@param[out]	*ret		The route, 1 byte per node, memory should have been reserved for it
	@return		uint8_t		0 if no route received, 1 if route has been received
*/
uint8_t dsrProcessRouteReply(uint8_t *data, uint8_t thisNode, uint8_t *ret) {
uint8_t source, payloadLen, jump, dest, requestId;
uint8_t payload[MAX_ROUTE_LEN], packet[MAX_ROUTE_LEN];
_dsr_packet p;
unsigned int i = 0;
netpacket_send_args_t npsargs;


	if (data[POS_TYPE] == ROUTE_REPLY) {

//		DEBUGM("Route reply received\n");

		/*Build a new packet*/
		source = thisNode;
		payloadLen = data[POS_PAYLOAD_LEN];
	
		/*Let's generate the payload, we first copy the existing one*/
		for (i = 0; i < payloadLen; i++) {
			payload[i] = data[HEADER_LEN+i];
		}
				
		/*Next position in the payload to send to*/
		jump = !(data[POS_JUMP]) ? 0 : data[POS_JUMP] - 1;
			
		/*If it's the end of the reply... great !*/
		if (jump == 0 && payload[jump] == source) {
			DEBUGM("Route complete : ");
			
			/*Store the route and return it, first pos will be the route length*/
			ret[0] = payloadLen;
			for (i = 1; i < payloadLen+1; i++) ret[i] = payload[i-1];

			for (i = 0; i < payloadLen; i++) printf("%d%s", payload[i], (i==payloadLen-1)?"\n":" -> ");

			return 1;

		}
		else {
			dest = payload[jump];
			requestId = data[POS_REQ_ID];
	
			/*We create the packet structure*/
			p = _dsrMakePacket(_dsrMakeHeader(source, dest, requestId, ROUTE_REPLY, jump, 0, payloadLen),\
			_dsrMakePayload((uint8_t *)payload));

			/*Then the packet array*/
			_dsrMakeFrame(p, packet);
					
			/*And send, finally*/
			Net_sendArgsInit(&npsargs);
			npsargs.netheader.to = dest;
			npsargs.netheader.type = USERDEFINED_PACKET+ROUTE;
			npsargs.netheader.flags = 0;
			npsargs.payload[0].buffer = (uint8_t*) packet;
			npsargs.payload[0].size = HEADER_LEN + payloadLen;
			Net_send(&npsargs);

			DEBUGM("Bouncing route reply : %d -> [%d] -> %d\n", payload[jump+2], source, dest);

		}
	}

/*All ok, but no route still*/
return 0;
}

