/******************************************************/
/* Copyright (C) 2008 Arch Rock Corporation 2008      */
/* License Agreement: refer to LICENSE.txt            */
/*                                                    */
/******************************************************/

/*!
@file server.c

The functions in this file initialize the wireless interface and start the TCP echo server.

*/
#include <sys/svcs.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <netinet/inet.h>
#include <netinet/in.h>
#include <iwconfig.h>
#include <raven.h>
#include "ieee802154.h"
#include "uart.h"
#include "echo.h"

// function prototypes
void __main(event_t event, void * cbargs, void * context); 
void timer_handler(event_t event, void * cbaargs, void * context);
void iwconfig_handler(event_t event, void * cbargs, void * context);
void route_notify_handler(event_t event, void *cbargs, void *context);
void iwstatus_handler(event_t event, void *cbargs, void *context);

//! timer descripter, used to hold the return value of timer()
static int16_t g_timerfd;

//! Maximum LED blink window, in units of 200ms epochs
#define LEDMAX 20

//! Current LED blink count, in units of 200ms epochs, initialized to 0
static uint8_t LED_cnt = 0;

//! Current LED blink limit, in units of 200ms epochs, initialized to 0
static uint8_t LED_limit = 0;

//! timer_handler 
//! 
//! @brief this function is invoked when the timer is fired
//!
//! @param event equals to SETITIMER as this is a setitimer() callback
//! @param cbargs passes \em resource back from setitimer() call
//! @param context passes \em context back from setitimer() call
//!
//! @return none
//!
void timer_handler(event_t event, void * cbaargs, void * context)
{  
  LED_cnt++;
  if (LED_cnt < LED_limit) { 
    if (LED_cnt & 1) led_on();	/* toggle till we hit limit */
    else led_off();
  } else {
    led_off();			/* then silent */
  }

  if (LED_cnt >= LEDMAX) {
    LED_cnt = 0;		/* reset count */
  }
}

//! setBlink
//!
//! @brief this function sets LED_cnt and LED_limit
//!
//! LED_cnt = count
//! 
//! LED_limit = LED_cnt * 2
//!
//! @param count is the value of LED_cnt
//!
//! @return none
//!
void setBlink(uint8_t count) {
  LED_cnt = 0;
  LED_limit = count*2;
}


//! Network interface information from iwconfig
static net_device_info_t g_lpan0_info;

//! default 802.15.4 channel, defiend in ieee802154.h
uint8_t  ieee154_channel = default_ieee154_channel;

//! default 80215.4 PAN ID, defined in ieee802154.h
uint16_t ieee154_panid   = default_ieee154_panid;

//! Network interface information from iwconfig
static net_device_info_t g_lpan0_info;

//! maximum number of characters used to display IPv6 address
#define MAXIP 40

//! default display string preceding IP addresses
#define IPMSG "IPv6:"

//! iwstatus_handler 
//! 
//! @brief this function is invoked when iwconfig() STATUS operation is complete
//!
//! @param event equals to IWCONFIG as this is a iwconfig() STATUS callback
//! @param cbargs passes \em dev_info back from iwconfig() STATUS call
//! @param context passes \em context back from iwconfig() STATUS call
//!
//! @return none
//!
void iwstatus_handler(event_t event, void *cbargs, void *context)  {
  char ipaddr[MAXIP]=IPMSG;
  setBlink(5);
  if (inet_ntop(AF_INET6, &g_lpan0_info.v6_gaddr, 
		&ipaddr[sizeof(IPMSG)-1],MAXIP-sizeof(IPMSG)+1)) {
    print_msg(ipaddr,strlen(ipaddr));	// Print global IPv6 address
    TCPecho_init();		/* Start TCP echo server */
  }
}

//! route_notify_handler 
//! 
//! @brief this function is invoked when svccntl() ROUTESVC_NOTIFY signal takes place
//!
//! @param event equals to SVCCNTRL as this is a svccntrl() ROUTESVC_NOTIFY callback
//! @param cbargs passes \em resource back from svccntrl() ROUTESVC_NOTIFY call
//! @param context passes \em context back from svccntrl() ROUTESVC_NOTIFY call
//!
//! @return none
//!
void route_notify_handler(event_t event, void *cbargs, void *context)  {
  if (event.flags == F_NETWORK_JOINED) {
    setBlink(4);
    /* Request LPAN status */
    iwconfig(LPAN0, STATUS, 0, NULL,
	     iwstatus_handler,	
	     &g_lpan0_info, 	// device info
	     NULL);		// context
  }
  else if (event.flags == F_LEFT_NETWORK) {
    char  leave_msg[] = "LPOUT";
    print_msg(leave_msg, sizeof(leave_msg));
  }
  else {
    char  error_msg[] = "ERROR";
    print_msg(error_msg, sizeof(error_msg));
  }
}

//! iwconfig_handler 
//! 
//! @brief this function is invoked when iwconfig() UP operation is complete
//!
//! @param event equals to IWCONFIG as this is a iwconfig() UP callback
//! @param cbargs passes \em dev_info back from iwconfig() UP call
//! @param context passes \em context back from iwconfig() UP call
//!
//! @return none
//!
void iwconfig_handler(event_t event, void * cbargs, void * context)
{
  setBlink(3);
  // If network interface is not up, reboot now.
  if (!(g_lpan0_info.flags & IFF_UP)){
      // cannot start radio, reboot node
    svccntl(REBOOT_CNTL, START, NULL, NULL, NULL);   
    return;	
  } else {
    char IWMSG[] = "LPAN UP";
    print_msg(IWMSG,sizeof(IWMSG)); /* LPAN UP */
  }
  /* Requestion notification when routing is valid */
  svccntl(ROUTESVC_NOTIFY, START, 
	  route_notify_handler, 
	  NULL, NULL);
}



//! iwconfig_init
//!
//! Initialize the wireless interface 
//!
//! @param net_device is the data structure used to hold the interface configuration data
//!
//! @return none
//!
void iwconfig_init(net_device_t net_device) {
  net_device.channel = ieee154_channel; 
  net_device.panid   = ieee154_panid;
  net_device.tx_pwr = 0;	// 0dBm
  setBlink(2);
  iwconfig(LPAN0,		// interface name
	   UP,			// command: Bring up LPAN0
	   0,			// no options
	   &net_device,		// device settings
	   iwconfig_handler,	// operation completion handler 
	   &g_lpan0_info, 	// call-back args passed to completion handler
	   NULL);		// context
}


//! __main 
//!
//! @brief this function is invoked after boot
//!
//! @param event equals MAIN as this is a boot event
//! @param cbargs is NULL for boot event
//! @param context is NULL for boot event
//!
//! @return none
//!
void __main(event_t event, void * cbargs, void * context)
{
  itimerval_t timevalue;
  net_device_t net_device;
  
  uart_init();			/* Enable serial interface to 3290 */
  // Create a periodic timer to fire timer_handler every 200 milliseconds.
  g_timerfd = timer();
  if (g_timerfd > 0){
	timevalue.it_interval.tv_sec  = 0;
	timevalue.it_interval.tv_usec = 200000;
	timevalue.it_value.tv_sec     = 0;
	timevalue.it_value.tv_usec    = 0;
	setitimer(g_timerfd, &timevalue, NULL, timer_handler, NULL, NULL);
  }
  setBlink(1);
  iwconfig_init(net_device);
  return;
}

