#include "contiki.h"
#include "contiki-net.h"

#include "dev/leds.h"
#include "net/rime/rimeaddr.h"
#include "net/rime/packetbuf.h"
#include "net/rime/broadcast.h"

#include "net/rime/network_l.h"

#include "node-id.h"

#define DEBUG 1
#if DEBUG
#include <stdio.h>
#define PRINTF(...) printf(__VA_ARGS__)
#else
#define PRINTF(...)
#endif

#include "../mobility.h"

static volatile uint32_t last_rcvd_ul= 0, count_ul = 0;
static volatile uint32_t last_rcvd_dl= 0, count_dl = 0;

static volatile u8_t dl_feedback = 0;

#define N_NODES  30
static packet_delivery_t list_nodes[N_NODES];



PROCESS(sink_packet_process, "sink-demo With Network_layer");
PROCESS(counter_process, "sink_counter-process");
AUTOSTART_PROCESSES(&sink_packet_process, &counter_process);

/*----------------------------------------------------------------------------*/
/*implements a packet on the leds from 0 to 7
 *param count - the value to be displayed of the leds
 */
static void leds_set(uint8_t count){

 //the shift is due to the change on the leds in the Tmote Sky platform
         if(count & LEDS_GREEN){
           leds_on(LEDS_GREEN<<1);
         }else{
            leds_off(LEDS_GREEN<<1);
         }

         if(count & LEDS_YELLOW){
           leds_on(LEDS_YELLOW>>1);
         }else{
           leds_off(LEDS_YELLOW>>1);
         }

         if(count & LEDS_RED){
          leds_on(LEDS_RED);
         }else{
          leds_off(LEDS_RED);
         }
}
/*----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------*/
#if SINK_SERIAL
static void inpacket_call(network_msg_t *p, rimeaddr_t *from, void* payload, uint8_t len){
    u8_t exists = 0, k;   
    u8_t pos = N_NODES;
    
     u8_t *d   =(u8_t*)p;
     
    packet_delivery_t *ptr_list;
    network_statistics_t *res = (network_statistics_t*) payload;;
  
     //statistics_add_update(d, from, payload);
    for (k = 0; k < N_NODES; k++) {
        ptr_list = &list_nodes[k];

        if (ptr_list->state == 1) {
            if (rimeaddr_cmp(&ptr_list->node_id, from)) {
                exists = 1;
                
                if (*(d + 3) == NETWORK_PERFORMANCE) {
                   
                    ptr_list->tx_packets = res->tx_packets;
                    ptr_list->packets_lost = (res->tx_packets - ptr_list->rx_packets);                  
                }else{
                    ptr_list->rx_packets++;
                }
                break;
            }
        }else {            
            pos = k;
        }
    }
    
    if(exists == 0 && pos < N_NODES){
        list_nodes[pos].state = 1;
        list_nodes[pos].rx_packets = 1;
        rimeaddr_copy(&list_nodes[pos].node_id, from);
        
        if (*(d + 3) == NETWORK_PERFORMANCE) {
            list_nodes[pos].tx_packets = res->tx_packets;
            list_nodes[pos].packets_lost = (res->tx_packets - list_nodes[pos].rx_packets);
        }
    }
    
    if ((*(d + 3) == NETWORK_PERFORMANCE)) {
        //PRINTF("NET_STAT_FROM: %d.%d\n",from->u8[0], from->u8[1]);
        if (res->rp_type == 1) {
            u32_t admin_time = (res->ad_tn - res->ad_t0);
            u32_t conv_time = (res->nc_tn - res->nc_t0);
            if (res->ad_tn < res->ad_t0) {
                admin_time = (u32_t) (65536 + res->ad_tn - res->ad_t0);
            }
            if (res->nc_tn < res->nc_t0) {
                conv_time = (u32_t) (65536 + res->nc_tn - res->nc_t0);
            }
            PRINTF("NET_STAT_FROM: %d.%d ",from->u8[0], from->u8[1]);
            PRINTF("CH: %d.%d ", res->c_router.u8[0], res->c_router.u8[1]);
            PRINTF("ADMIN_TIME: %li ", admin_time);
            PRINTF("CONV_TIME: %li\n", conv_time);
        }
	if (res->rp_type == 2) {
            u32_t admin_time = (res->ad_tn - res->ad_t0);
            u32_t handov_time = (res->hv_tn - res->hv_t0);
            if (res->ad_tn < res->ad_t0) {
                admin_time = (u32_t) (65536 + res->ad_tn - res->ad_t0);
            }
            if (res->hv_tn < res->hv_t0) {
                handov_time = (u32_t) (65536 + res->hv_tn - res->hv_t0);
            }
            PRINTF("NET_STAT_FROM: %d.%d ",from->u8[0], from->u8[1]);
            PRINTF("HDVR_TIME: %li ", handov_time);
            PRINTF("HV_AD_TIME: %li ", admin_time);
            PRINTF("OLD_CH: %d.%d ", res->c_router.u8[0], res->c_router.u8[1]);
            PRINTF("CURR_CH: %d.%d\n", res->n_router.u8[0], res->n_router.u8[1]);
        }
	if (res->rp_type == 3) {
	      PRINTF("PKT_RX_TX: %li %li ", ptr_list->rx_packets,res->tx_packets);
	      if(exists == 1){          
		  PRINTF("LX: %li ", ptr_list->packets_lost);
	      }else{          
		  //PRINTF("packet_loss: %li\n", list_nodes[pos].packets_lost);
	      }
              PRINTF(" FROM: %d.%d\n", from->u8[0], from->u8[1]);
        }               
    }

   
    if(*(d+3) == DATA_RATE_COUNTER){ 
        packet_t *pack = (packet_t*)payload;
        dl_feedback = pack->seqno;
        //PRINTF("dl_feed: %d\n", dl_feedback);
    }
    leds_toggle(LEDS_BLUE);
    count_ul++;
}
#endif
/*----------------------------------------------------------------------------*/
static void inpacket(rimeaddr_t *from, void* payload, uint8_t len){
   
  count_ul++;
}
/*----------------------------------------------------------------------------*/
PROCESS_THREAD(sink_packet_process, ev, data)
{
    

   PROCESS_EXITHANDLER(network_close());  
   
   PROCESS_BEGIN();

   static rimeaddr_t addr, dest;
   addr.u8[0] = 1;
   addr.u8[1] = 0;

   dest.u8[0] = 2;
   dest.u8[1] = 0;
   
   rimeaddr_set_node_addr(&addr);

   network_open();

   #if SINK_SERIAL
   network_recv_call(inpacket_call);
   #else
   network_recv_func(inpacket);
   #endif
   
   static uint8_t val = 0;   
   
   while(1){
            //PROCESS_YIELD();
            static struct etimer et;

            etimer_set(&et, CLOCK_SECOND);
            PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et));

            packet_t *packet = (packet_t*)network_getbuffer(sizeof (packet_t));
            packet->seqno = val++;
            packet->type  = 0;

            network_broadcast();
                        
            count_dl++;
   }

   PROCESS_END();
}
/*----------------------------------------------------------------------------*/
PROCESS_THREAD(counter_process, ev, data){
    PROCESS_BEGIN();
    while(1){ 
            static struct etimer et;

            etimer_set(&et, CLOCK_SECOND);
            PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et));
            //if((count_ul-last_rcvd_ul) > 0){
	      //PRINTF("%li %li %2d\n", ((count_ul-last_rcvd_ul)+dl_feedback), (count_ul-last_rcvd_ul), dl_feedback);
	      PRINTF("%li pps\n", (count_ul-last_rcvd_ul));
	    //}
            last_rcvd_ul = count_ul;
            last_rcvd_dl = count_dl;
    }
    PROCESS_END();
}