#include "archinc.h"
#include "cli.h"
#include "strutil.h"
#include "zarlink.h"
#include "zl_util.h"
#include "nvstore.h"
#include "ip.h"

#include "uip/uip.h"
#include "uip/uip_arp.h"

#include <stdio.h>
#include <stdlib.h>

#define NVSTORE_IPADDR_NAME  "ipaddr"
#define NVSTORE_GATEWAY_NAME "gateway"
#define NVSTORE_NETMASK_NAME "netmask"

static unsigned char sidearmIpv4Addr[4] = {0, 0, 0, 0};
static unsigned char cpuIpv4Addr[4]     = {192, 168,   0,  30};
static unsigned char cpuIpv4Netmask[4]  = {255, 255, 255,   0};
static unsigned char cpuIpv4Gateway[4]  = {192, 168,   0,   1};

static void uipIpAddrSetup(void)
{
  u16_t ipaddr[2]; // uIP stuff

  // IP address
  uip_ipaddr(ipaddr, cpuIpv4Addr[0], cpuIpv4Addr[1], cpuIpv4Addr[2], cpuIpv4Addr[3]);
  uip_sethostaddr(ipaddr);

  // netmask
  uip_ipaddr(ipaddr, cpuIpv4Netmask[0], cpuIpv4Netmask[1], cpuIpv4Netmask[2], cpuIpv4Netmask[3]);
  uip_setnetmask(ipaddr);

  // Default router
  uip_ipaddr(ipaddr, cpuIpv4Gateway[0], cpuIpv4Gateway[1], cpuIpv4Gateway[2], cpuIpv4Gateway[3]);
  uip_setdraddr(ipaddr); 
}

static int ipsetHandler(int argc, char * argv[], int flags)
{
  int retVal = 0;
  int sendPort;
  char * pOutBuf;
  int bufLen;

  pOutBuf = clicharGetOutputBuffer(&bufLen);

  if (argc == 2) {
    retVal = parseDottedQuadV4(argv[1], cpuIpv4Addr);
    }
  
  if (retVal == 0) {
    sprintf(pOutBuf, "%s: Medusa CPU IP address is %d.%d.%d.%d\r\n", argv[0], cpuIpv4Addr[0], cpuIpv4Addr[1], 
      cpuIpv4Addr[2], cpuIpv4Addr[3]);
    clicharSendOutputBuffer(pOutBuf, bufLen);
    }

  uipIpAddrSetup();

#ifdef CONFIG_INCLUDE_NVSTORE
  return nvstoreWrite(NVSTORE_IPADDR_NAME, cpuIpv4Addr, 4);
#else
  return 0;
#endif
}

static int saipsetHandler(int argc, char * argv[], int flags)
{
  int retVal = 0;
  int sendPort;
  char * pOutBuf;
  int bufLen;

  pOutBuf = clicharGetOutputBuffer(&bufLen);

  if (argc == 2) {
    retVal = parseDottedQuadV4(argv[1], sidearmIpv4Addr);
    }
  
  if ((argc == 1) || (retVal == 0)) {
    sprintf(pOutBuf, "%s: SideARM IP address is %d.%d.%d.%d\r\n", argv[0], sidearmIpv4Addr[0], sidearmIpv4Addr[1], 
      sidearmIpv4Addr[2], sidearmIpv4Addr[3]);
    clicharSendOutputBuffer(pOutBuf, bufLen);
    }

  return retVal;
}

static int gwsetHandler(int argc, char * argv[], int flags)
{
  int retVal = 0;
  int sendPort;
  char * pOutBuf;
  int bufLen;

  pOutBuf = clicharGetOutputBuffer(&bufLen);

  if (argc == 2) {
    retVal = parseDottedQuadV4(argv[1], cpuIpv4Gateway);
    }
  
  if (retVal == 0) {
    sprintf(pOutBuf, "%s: default gateway is %d.%d.%d.%d\r\n", argv[0], cpuIpv4Gateway[0], cpuIpv4Gateway[1], 
      cpuIpv4Gateway[2], cpuIpv4Gateway[3]);
    clicharSendOutputBuffer(pOutBuf, bufLen);
    }

  uipIpAddrSetup();

#ifdef CONFIG_INCLUDE_NVSTORE
  return nvstoreWrite(NVSTORE_GATEWAY_NAME, cpuIpv4Gateway, 4);
#else
  return 0;
#endif
}

static int masksetHandler(int argc, char * argv[], int flags)
{
  int retVal = 0;
  int sendPort;
  char * pOutBuf;
  int bufLen;

  pOutBuf = clicharGetOutputBuffer(&bufLen);

  if (argc == 2) {
    retVal = parseDottedQuadV4(argv[1], cpuIpv4Netmask);
    }
  
  if (retVal == 0) {
    sprintf(pOutBuf, "%s: netmask is %d.%d.%d.%d\r\n", argv[0], cpuIpv4Netmask[0], cpuIpv4Netmask[1], 
      cpuIpv4Netmask[2], cpuIpv4Netmask[3]);
    clicharSendOutputBuffer(pOutBuf, bufLen);
    }

  uipIpAddrSetup();

#ifdef CONFIG_INCLUDE_NVSTORE
  return nvstoreWrite(NVSTORE_NETMASK_NAME, cpuIpv4Netmask, 4);
#else
  return 0;
#endif
}

// get routines to support metadata get command
int ipCpuAddrGet(char *ipCpuAddr)
{
  int i;

  if (ipCpuAddr == NULL)
    return -1;

  for (i=0;i<4;i++)
    ipCpuAddr[i] = cpuIpv4Addr[i];

  return 0;
}

// get routines to support metadata get command
int ipCpuNetmaskGet(char *ipCpuNetmask)
{
  int i;

  if (ipCpuNetmask == NULL)
    return -1;

  for (i=0;i<4;i++)
    ipCpuNetmask[i] = cpuIpv4Netmask[i];

  return 0;
}

// get routines to support metadata get command
int ipCpuGatewayGet(char *ipCpuGateway)
{
  int i;

  if (ipCpuGateway == NULL)
    return -1;

  for (i=0;i<4;i++)
    ipCpuGateway[i] = cpuIpv4Gateway[i];

  return 0;
}

// get routines to support metadata get command
int ipSidearmAddrGet(char *ipSidearmAddr)
{
  int i;

  if (ipSidearmAddr == NULL)
    return -1;

  for (i=0;i<4;i++)
    ipSidearmAddr[i] = sidearmIpv4Addr[i];

  return 0;
}

// returns 0 if the IP address given is the same as the CPU, -1 otherwise
int ipIsSideARMAddr(unsigned char * compareIpAddr)
{
  int i;
  int retVal = 0;

  if (compareIpAddr == NULL)
    return -1;

  for(i=0;i<4;i++) { // IP v4
    if (sidearmIpv4Addr[i] != compareIpAddr[i]) {
      retVal = -1;
      break;
      }
    }

  return retVal;
}

int ipInit(void)
{
  int retVal;
  int bufLen;
  char * pOutBuf;
  char length;

  pOutBuf = clicharGetOutputBuffer(&bufLen);

#ifdef CONFIG_INCLUDE_NVSTORE

  length = 4;
  retVal = nvstoreRead(NVSTORE_IPADDR_NAME, cpuIpv4Addr, &length);

  if ((retVal != 0) || (length != 4)) {
    sprintf(pOutBuf, "!! no valid cpu IPv4 address is stored\r\n");
    clicharSendOutputBuffer(pOutBuf, bufLen);
    }

  length = 4;
  retVal = nvstoreRead(NVSTORE_GATEWAY_NAME, cpuIpv4Gateway, &length);

  if ((retVal != 0) || (length != 4)) {
    sprintf(pOutBuf, "!! no cpu default gateway is stored\r\n");
    clicharSendOutputBuffer(pOutBuf, bufLen);
    }

  length = 4;
  retVal = nvstoreRead(NVSTORE_NETMASK_NAME, cpuIpv4Netmask, &length);

  if ((retVal != 0) || (length != 4)) {
    sprintf(pOutBuf, "!! no cpu IP netmask is stored\r\n");
    clicharSendOutputBuffer(pOutBuf, bufLen);
    }

  uipIpAddrSetup();

#endif /* CONFIG_INCLUDE_NVSTORE */

  cliRegisterCommand("ipset", ipsetHandler, "ipset [IP Addr] - sets Medusa CPU IP v4 address");
  cliRegisterCommand("saipset", saipsetHandler, "saipset [IP Addr] - sets SideARM IP v4 address");
  cliRegisterCommand("gwset", gwsetHandler, "gwset [IP Addr] - sets default gateway");
  cliRegisterCommand("maskset", masksetHandler, "maskset [IP netmask] - sets IP v4 netmask");

  return 0;
}



