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

/*!
@file echo.c

@brief The functions in this file maintains the TCP echo server

*/

#include <sys/svcs.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <notifychange.h>

#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <netinet/inet.h>
#include <iwconfig.h>

#include <raven.h>
#include "ieee802154.h"
#include "uart.h"
#include "server.h"
#include "echo.h"

// System Event Handler prototypes
void tcpEcho_handler (event_t event, void * cbargs, void * context);
void err_handler     (event_t event, void * cbargs, void * context);

//! Standard TCP Echo Service Port
#define ECHO_PORT 7

//! User defined TCP transmission queue buffer size
#define BUFSIZE 512
 
//! Socket ID
static int16_t g_sockfd;

//! Local Socket Address 
static sockaddr_in6_t g_localaddr;

//! TCP event descriptor
static tcp_event_t g_app_tcp_event;

//! Pass this buffer for the stack
//! to use as TCP transmission queue buffer for
//! the socket.
static uint8_t g_tcpbuf[BUFSIZE];

//! tcpEcho_handler
//!
//! @breif Implements the TCP state machine to for TCP connection
//! 
//! @param event equals to TCPBIND as this is a tcpbind() callback
//! @param cbargs passes \em tcp_event back from tcpbind() call
//! @param context passes \em context back from tcpbind() call
//!
//! @return none
//!
void tcpEcho_handler(event_t event, void * cbargs, void * context)
{  
  // Strings to display when state change occurs
  char  AMSG[] = "Acpt";
  char  CMSG[] = "Conn";
  char  RMSG[] = "RCV";
  char  ZMSG[] = "Closed";
  char  EMSG[] = "TCP err";

  switch(g_app_tcp_event.type){
  case ACCEPT:
    // accept an incoming TCP call
    print_msg(AMSG,sizeof(AMSG));
    accept(g_sockfd, &g_app_tcp_event.addr);
    break;
  case CONNECTED:
    print_msg(CMSG,sizeof(CMSG));
    break;
  case RECV:
    // Note that on return, g_app_tcp_event
    // will once again owned by the kernel.
    // Copying of g_app_tcp_event.recvbuf 
    // right here is required if application 
    // wants to retain the data after this callback
    // returns.
    print_msg(RMSG,sizeof(RMSG));
    // echo bytes back to the sender
    if (send(g_sockfd, g_app_tcp_event.recvbuf, 
	     g_app_tcp_event.recvlen, 0) == -1)
      // send failed, notify me again in err_handler 
      // when send is ready.  
      notifywrite(g_sockfd, err_handler, NULL, NULL);
    break;
  case CLOSED:
	print_msg(ZMSG,sizeof(ZMSG));
    break;
  default: 
    print_msg(EMSG,sizeof(EMSG));
  }
}

//! err_handler
//!
//! @brief Notification that send may be possible again
//!
//! @param event equals to NOTIFYWRITE as this is a notifywrite() callback
//! @param cbargs passes \em resource back from notifywrite() call
//! @param context passes \em context back from notifywrite() call
//!
//! @return none
//!
void err_handler(event_t event, void * cbargs, void * context)
{
  char msg[] = "Out of Buffer.\n";

  // Notify sender that some bytes have been dropped
  // due to buffer limitation.
  if (send(g_sockfd, msg, sizeof(*msg), 0) == -1)
    notifywrite(g_sockfd, err_handler, NULL, NULL);
}

//! default message to print to the LCD
#define EchoMSG "ECHO"

//! TCPecho_init
//!
//! @brief initializes the TCP socket
//!
//! @param none
//!
//! @return none
//!
void TCPecho_init()
{
  char *emsg = EchoMSG;
  print_msg(emsg,sizeof(EchoMSG));
  setBlink(1);
  // Construct a IP address structure and socket for LOCALHOST:ECHO
  //
  memset(&g_localaddr.sin6_addr, 0, sizeof(g_localaddr.sin6_addr));
  g_localaddr.sin6_port = ECHO_PORT;
  g_sockfd = socket(AF_INET6, SOCK_STREAM, 0);

  if (g_sockfd > 0)
    // After the bind call, listening on the port 
    // starts automatically.
    // tcp_handler will handle all TCP events.
    tcpbind(g_sockfd, &g_localaddr, 
	    g_tcpbuf, BUFSIZE,
	    tcpEcho_handler, &g_app_tcp_event, NULL);
}





