/*
Copyright 2007, Freie Universitaet Berlin. All rights reserved.

These sources were developed at the Freie Universität Berlin, Computer
Systems and Telematics group.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.

- Neither the name of Freie Universitaet Berlin (FUB) nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

This software is provided by FUB and the contributors on an "as is"
basis, without any representations or warranties of any kind, express
or implied including, but not limited to, representations or
warranties of non-infringement, merchantability or fitness for a
particular purpose. In no event shall FUB or contributors be liable
for any direct, indirect, incidental, special, exemplary, or
consequential damages (including, but not limited to, procurement of
substitute goods or services; loss of use, data, or profits; or
business interruption) however caused and on any theory of liability,
whether in contract, strict liability, or tort (including negligence
or otherwise) arising in any way out of the use of this software, even
if advised of the possibility of such damage.

This implementation was developed by the CST group at the FUB.

For documentation and questions please use the web site
http://scatterweb.mi.fu-berlin.de and the mailinglist
scatterweb@lists.spline.inf.fu-berlin.de (subscription via the Website).
Berlin, 2007
*/




/**
	@file		ScatterWeb.Process.c
	@brief 		Main file with the commands definition
	@author		Adrían Antoñana Gómez
	@author		Carlos Díaz Cánovas
	@version	rc-2008/12/07

*/


#include <ScatterWeb.System.h>
#include <ScatterWeb.CC1020.h>
#include <string.h>
#include "constants.h"
#include "dsrProcess.h"
#include "ScatterWeb.System.h"
#include "msg.h"
#include "tcp.h"


#define MSG_MAX 200
#define MAX_RESENT 100
#define SEC_RESEND 1

#define MSG_RESEND 5


uint8_t recv_s = 0;					/*Reception switch 0=off 1=on*/
uint8_t promiscous = 0;					/*Promiscous switch 0=off 1=on*/
uint8_t thisNode = 0;
uint8_t route[MAX_ROUTE_LEN], routeReceived = 0, routeFail = 0;
uint8_t resent = 0;
uint8_t lastRouteId = 0, lastRouteDest = 0, routeInProgress = 0;
uint8_t msg[MSG_MAX], last_msg[MSG_MAX];
uint8_t _iter_ = 0;
uint8_t msgInProgress = 0;
int msgNumber = 0, msgSize = 0, msgOK = 0, msgRepeat = 0, connect_repeat = 0;

uint8_t  msgLast = 0;			/*Last message number received*/
uint16_t msgLost = 0;


uint8_t last_seq = 0;
uint8_t last_ack = 0;
uint8_t last_len = 0;



void route_process();


/**
	Starts a route request if no one is already being processed

	@brief		Starts a route request
	@param[in]	dest		Destination of the route request
	@param[in]	reqid		ID of the request
	@return		void
*/
void sendRReq(uint8_t dest, uint8_t reqid) {

	DEBUGM("routeInProgress=%d; msgInProgress=%d\n", routeInProgress, msgInProgress);

	if (!routeInProgress) {

		lastRouteDest = dest;
		
//		DEBUGM("RREQ TO : %d FROM %d\n", dest, Configuration.id);
	
		lastRouteId = dsrStartRouteRequest(Configuration.id, dest, reqid);
	
		/*Wait for the route to be received, or X seconds*/
		Timers_add(SEC_RESEND*1024, route_process, 0xFFFF);
	
		routeInProgress = 1;
	}
	else {

		DEBUGM("Already processing a route\n");
	
	}
}


/**
	If a route is in progress of being discovered and still no results, sends the route request again
	and calls itself, if the route has been received, informs of it and returns. If the maximum number
	of resends has been achieved it discards the route.

	@brief		Processes a route request in progress
	@return 	void
*/
void route_process() {
netpacket_send_args_t npsargs;

	DEBUGM("routeInProgress=%d; msgInProgress=%d\n", routeInProgress, msgInProgress);

	if (routeInProgress) {
		if (routeReceived == 1) {

			DEBUGM("ROUTE OK (%d%% lost)\n", (int)((double)((double)resent/(double)MAX_RESENT)*100.0));
			DEBUGM("############################################\n\n");

			routeReceived = 0;
			routeInProgress = 0;
			resent = 0;
			routeFail = 0;
		}
		else if (resent == MAX_RESENT) {

			/*Maximum number of resends reached*/
			DEBUGM("ROUTE LOST\n");
			routeReceived = 0;
			routeInProgress = 0;
			resent = 0;
			routeFail = 1;
		}
		else {
	
			/*Resend*/
			DEBUGM("RESENDING (%d times)\n", resent);
			sendRReq(lastRouteDest, lastRouteId);
			resent++;
			Timers_add(SEC_RESEND*1024, route_process, 0xFFFF);
	
		}
	}

}


/**
	Command to request a route
	@note	Parameters refer to command parameters

	@brief		route_request command
	@param		<id>
*/
COMMAND( route_request, CMDFLAG_SERIAL, cmdargs ) {
uint8_t dest;
uint16_t count = Cmd_SplitArgs( cmdargs->args );
char* arg = (char*) cmdargs->args;

	if (count < 1) {
		printf("USAGE: route_request <id>\n");
		return;
	}
	
	dest = atoi(arg);
	sendRReq(dest, 0);
	

}


int msgToSend = 0;
/**
	Sends a message if the messages to send counter is not void. Also calls itself
	after 256 clock ticks.

	@brief		Sends a message
	@return		void
*/
/*void sndMsg() {

	
	if (msgToSend) {

		msg_make(msg, route+1, route[0], NULL, msgSize, (msgNumber-msgToSend)+1);
		msg_process(msg, Configuration.id);

		msgToSend--;
		Timers_add(256, sndMsg, 0xFFFF);
	}

}*/
void sndMsg() {
extern uint8_t state;	/*TCP state*/
uint8_t tcp_msg[10];

	/*Start a connection*/
	if (state == CLOSED_LISTEN)
		tcp_connect(tcp_msg, route, Configuration.id);

	/*Connection established*/
	//if (msgToSend && (state == ESTABLISHED)) {
	if (!msgOK && (state == ESTABLISHED)) {

		/*Send the message*/
		tcp_process(msg, msgSize, route[0], route+1, Configuration.id, 0);

		/*Copy the message to be able to resend it outside*/
		for(_iter_=0;_iter_<msgSize;_iter_++) last_msg[_iter_] = msg[_iter_];

		/*Send the message again if necessary, N times*/
		if (msgRepeat) {
			Timers_add(2048, sndMsg, 0xFFFF);
			msgRepeat--;
		}

		/*After N resends, we close the connection*/
		else{
			msgOK = 1;
			msgInProgress = 0;
			printf("Failed to send the message, closing connection.\n");

			/*Since the communication is broken we force the closing*/
			state = CLOSED_LISTEN;

		}
	}

	/*Wait until the connection has been established*/
	else if (!msgOK && (state != ESTABLISHED)) {
			if (connect_repeat) {
				Timers_add(1024, sndMsg, 0xFFFF);
				DEBUGM("Retry (%d)\n", MSG_RESEND-connect_repeat);
			}
			else {
				state = CLOSED_LISTEN;
				DEBUGM("Timed out\n");
				msgInProgress = 0;
				msgOK = 1;
				
			}
			connect_repeat--;
	}


}





/**
	If a route has been discovered and a messgae is willing to be sent calls @link sndMsg() sndMsg @endlink
	to start sending the message. If the route is in progres it tries again after 1024 clock ticks.

	@brief		Starts the message send procedure
	@return		void
*/
void sendM() {
uint8_t *mPayload = NULL;
int i = 0;

	DEBUGM("routeInProgress=%d; msgInProgress=%d\n", routeInProgress, msgInProgress);

	/*All done !*/
	if (!routeInProgress && msgInProgress) {

		/*Send the message if the route was found*/
		if (!routeFail) {

			/*Send the message msgNumber times*/
			msgToSend = msgNumber;
			msgOK = 0;
			msgRepeat = MSG_RESEND;
			connect_repeat = MSG_RESEND;
			Timers_add(256, sndMsg, 0xFFFF);
			/*
			for (i=0;i<msgNumber;i++) {
				msg_make(msg, route+1, route[0], NULL, msgSize, i+1);
				msg_process(msg, Configuration.id);
			}

			msgInProgress = 0;
			*/

		}
		else {
		
			DEBUGM("Message not sent: no route found\n");

		}
	}

	/*If not...*/
	else if (routeInProgress && msgInProgress) {
	
		/*Try again in a second*/
		Timers_add(1024, sendM, 0xFFFF);
	
	}

}


/**
	Command to send a message
	@note	Parameters refer to command parameters

	@brief		msg_send command
	@param		<dest>
	@param		<number_of_messages>
	@param		<payload_size>
*/
COMMAND( msg_send, CMDFLAG_SERIAL, cmdargs ) {
uint16_t count = Cmd_SplitArgs( cmdargs->args );
char* arg = (char*) cmdargs->args;
uint8_t dest = 0;

	if (count < 3) {
		printf("USAGE: msg_send <dest> <number_of_messages> <payload_size>\n");
		return;
	}

	DEBUGM("routeInProgress=%d; msgInProgress=%d\n", routeInProgress, msgInProgress);

	/*Already waiting for a route*/
	if (!routeInProgress)  {

		/*Already handling a message*/
		if (!msgInProgress) {

			dest = atoi(arg);
			arg = ( char* )Cmd_GetNextArg( arg );
			msgNumber = atoi(arg);
			arg = ( char* )Cmd_GetNextArg( arg );
			msgSize = atoi(arg);
	
			/*libscatterweb does not allow messages bigger that 127 Bytes, we are limitting to 120 to avoid problems with headers*/
			if (msgSize > 120) {
				DEBUGM("Only messages below 120 bytes allowed\n");
				return;
			}
	
			msgInProgress = 1;
	
			/*Wait until the route is found to send the message*/
			Timers_add(1024, sendM, 0xFFFF);
		}

		else {
		
			printf("Already handling a message, please wait...");
		}

	}
	else {
	
		printf("Please wait until the route in found to request for new messages...\n");
	
	}

}

/**
	Command to terminate the current tcp connection

	@brief		tcp_close command
*/
COMMAND( tcp_close, CMDFLAG_SERIAL, cmdargs ) {

	tcp_close(msg, route, Configuration.id);

}

/**
	Command to reset the lost packets count

	@brief		lost_reset command
*/
COMMAND( lost_reset, CMDFLAG_SERIAL, cmdargs ) {

	msgLost = 0;
	msgLast = 0;

}


/**
	If a broadcast packet is received and it is a route packet this one is treated as a route request.
	If a pcket for this node is received then depending if it a route packet or a message packet is
	it treated as a route reply or as a message going to its destination.

	@brief		Treats received packets
	@param[in]	*args
	@return		bool
*/
bool Process_radioHandler(struct netpacket_handler_args* args)
{

	/*Notes: * The received packet data becomes invalid when this handler is completed.*/

	// Pass packet through command interface
	if ( Cmd_radioHandler( args ) ) {
		// packet handled by messaging module
		return true;
	}


	/*We signal that a packet is received by blinking the led*/
	_Data_redOff();

//	DEBUGM("Packet : from: %d, to: %d\n", args->netheader->from, args->netheader->to);

	/*BROADCAST*/
	if (args->netheader->to == BCAST) {

		DEBUGM("BCAST packet received (type = %X)\n", args->netheader->type);

		if (args->netheader->type == USERDEFINED_PACKET+ROUTE) {
			DEBUGM("ROUTE packet received\n");
			dsrProcessRouteRequest(args->payload, Configuration.id);
		}

		_Data_redOn();
		return true;
	
	}

	/*For me*/
	if(args->netheader->to == Configuration.id) {

		DEBUGM("Packet for me (type = %X)\n", args->netheader->type);

		if (args->netheader->type == USERDEFINED_PACKET+ROUTE) {
			DEBUGM("ROUTE packet received\n");
			if (dsrProcessRouteReply(args->payload, Configuration.id, route)) {
//				DEBUGM("Route received !\n");
				routeReceived = 1;
				route_process();
			}
		}

		if (args->netheader->type == USERDEFINED_PACKET+MESSAGE) {

			DEBUGM("Message for me\n");

			for (_iter_=0; _iter_<9; _iter_++) printf("- [%d] %d\n", _iter_,
				*((uint8_t *)((args->payload)+_iter_)));
				
			/*If it returns something it's that we have a message for us*/
			//if (msg_process(args->payload, Configuration.id)) {
				
			tcp_process(args->payload,
			msg_len(args->payload),
			*(msg_get_routelen(args->payload)),
			msg_get_route(args->payload),
			Configuration.id, 1);
		
				
			//}
		}

		_Data_redOn();		
		return true;
	}


_Data_redOn();
return false;

}



//				Messages start in 1
//				msgLost += *(msg_get_message(args->payload)+0) - (msgLast + 1);
//				msgLast = *(msg_get_message(args->payload)+0);
//				DEBUGM("MSG ID = %d (%d lost)\n", *(msg_get_message(args->payload)+0), msgLost);
//				for (_iter_ = 0; _iter_ < msg_len(args->payload); _iter_++)
//					DEBUGM("[%d] %X\n", _iter_, *(msg_get_message(args->payload)+_iter_)  );


/***********************************************************************************************/

void Process_sensorHandler(void* sdata)
{

}

uint8_t led_s = 0;

void Process_init()
{
	System_registerCallback( C_RADIO, ( fp_vp )Process_radioHandler );
	System_registerCallback( C_SENSOR, ( fp_vp )Process_sensorHandler );
	_Data_redOn();
}

/**
 * @brief	Ping request
 *
 * Send a ping request to another node
 * Example: ping 24
 */
COMMAND( ping, 0, cmdargs )
{
	uint16_t node;

	node = String_parseUint16( cmdargs->args, NULL );
	if ( node > 0 ) {
		Net_sendPing((netaddr_t)node);
	}
}

/**
 * @brief Echo Arguments
 *
 * Testcase 1: long command name
 * Testcase 2: argument splitting
 */
COMMAND( echoargs, CMDFLAG_SERIAL, cmdargs )
{
	uint16_t i;
	uint16_t count = Cmd_SplitArgs( cmdargs->args );
	char* arg = (char*) cmdargs->args;

	for ( i = 1; i <= count; i++ ) {
		printf("%u: %*s\n", i, strlen(arg), arg);
		arg = ( char* )Cmd_GetNextArg( arg );
	}
	snprintf(cmdargs->response_args, cmdargs->response_bufsize, "%u args found", count );
}

// Software header.
APP(
    APPH_STATIC_APP,	// Application Type
    __APP_NAME,			// Application Name (8 chars)
    1, 0				// Aplication Version (2 bytes)
);

