#include "archinc.h"
#include "cli.h"
#include "strutil.h"
#include "zarlink.h"
#include "zl_bus.h"

#include "uip/uip.h"
#include "uip/uip_arp.h"

#include <stdio.h>
#include <stdlib.h>

static unsigned char cpuMacAddr[6];
static struct uip_eth_addr uipMacAddr;

unsigned short byteSwap16(unsigned short swapData16)
{
  unsigned char temp;
  union {
    unsigned short uint16;
    unsigned char uint8[2];
    } u;

  u.uint16 = swapData16;

  temp = u.uint8[0];
  u.uint8[0] = u.uint8[1];
  u.uint8[1] = temp;

  return u.uint16;
}

void swapMac(unsigned char * pMac)
{
  int i;
  unsigned char temp;

  if (pMac == NULL)
    return;

  for(i=0;i<3;i++) {
    temp = pMac[i];
    pMac[i] = pMac[(5-i)];
    pMac[(5-i)] = temp;
    }
  
}

static char * foo[4];

// parses a 'dotted quad' v4 IP address (i.e. "192.168.0.1") into 4 bytes
int parseDottedQuadV4(char * pDottedQuad, unsigned char * pIpAddr)
{
  int count = 0;

  if ((pDottedQuad == NULL) || (pIpAddr == NULL))
    return -1;

  foo[count++] = pDottedQuad;

  while((count < 4) && (*pDottedQuad != '\0')) {
    if (*pDottedQuad == '.') {
      *pDottedQuad++ = '\0';
      foo[count++] = pDottedQuad;
      }
    else {
      pDottedQuad++;
      }
    }

  // did we find three dots?
  if (count < 4)
    return -1;

  for(count = 0; (count < 4); count++) {
    pIpAddr[count] = (unsigned char)(strtol(foo[count], (char **)NULL, 10));
    }
  
  return 0;
}

#ifdef CONFIG_INCLUDE_ZARLINK

static int zarlinkHandler(int argc, char * argv[], int flags)
{
  int retVal = -1;
  int regIndex;
  unsigned short zlData;
  unsigned short zlIndex;
  unsigned char blah;
  char * pOutBuf;
  int bufLen;

  pOutBuf = clicharGetOutputBuffer(&bufLen);
  
  // TODO: put this in a while loop to process a list of op/data pairs

  // write gets two parameters [index] [data], read gets one
  SKIPDASH(argv[1]);

  switch(*argv[1]) {
    case 'i':
    case 'I':
      // read the interrupt status register
      blah = (unsigned char)zarlinkInterruptRead();
      sprintf(pOutBuf, "0x%02x\n", blah);
      clicharSendOutputBuffer(pOutBuf, bufLen);
      retVal = 0;
      break;
    case 'q':
    case 'Q':
      // read (query) [index] reduced output for parsing
      zlIndex = (unsigned short)strtol(argv[2], NULL, 16);
      blah = (unsigned char)zarlinkIndexedRead(zlIndex);
      sprintf(pOutBuf, "0x%02x\n", blah);
      clicharSendOutputBuffer(pOutBuf, bufLen);
      retVal = 0;
      break;
    case 'r':
    case 'R':
      // read [index]
      zlIndex = (unsigned short)strtol(argv[2], NULL, 16);
      blah = (unsigned char)zarlinkIndexedRead(zlIndex);
      sprintf(pOutBuf, "zl reg[%04x] = %02x\n", zlIndex, blah);
      clicharSendOutputBuffer(pOutBuf, bufLen);
      retVal = 0;
      break;
    case 's':
    case 'S':
      // get status
      sprintf(pOutBuf, "zl status[%02x]\r\n", zarlinkStatusRead());
      clicharSendOutputBuffer(pOutBuf, bufLen);
      retVal = 0;
      break;
    case 'w':
    case 'W':
      // write [index] [data]
      zlIndex = (unsigned short)strtol(argv[2], NULL, 16);
      zlData = (unsigned short)strtol(argv[3], NULL, 16);
      blah = (unsigned char)(zlData & 0x00ff);
      zarlinkIndexedWrite(zlIndex, blah);
      sprintf(pOutBuf, "zl reg[%04x] <-- %02x\n", zlIndex, blah);
      clicharSendOutputBuffer(pOutBuf, bufLen);
      retVal = 0;
      break;
    default:
      break;
    }
    
  return retVal;
}

static unsigned char qStats[10];

static const char yesBlar[] = {'F', 'D', 'S', 'D', 'A', '.', '.', 'M'};
static const char noBlar[]  = {'f', 'd', 's', 'U', 'a', '.', '.', 'm'};

static int quickStatHandler(int argc, char *argv[], int flags)
{
  int bufLen;
  char * pOutBuf, * foo;
  int port;
  volatile int i;
  volatile int j;

  pOutBuf = clicharGetOutputBuffer(&bufLen);

  sprintf(pOutBuf, "%s: Flow Ctrl[f]  FDX[d]  Fast[s]  Link Down/Up[U,D]  Autoneg[a]  Module[m]\r\n",argv[0]);
  clicharSendOutputBuffer(pOutBuf, bufLen);

  for(i=0;i<10;i++) {
    zarlinkIndexedWrite(0xf03, (unsigned char)i);
    qStats[i] = zarlinkIndexedRead(0xf04);
    }

  for(i=0;i<10;i++) {
    if (i == 8)
      sprintf(pOutBuf, "_cpu[%02x]  ", i);
    else if (i == 9) 
      sprintf(pOutBuf, "mmac[%02x]  ", i);
    else
      sprintf(pOutBuf, "port[%02x]  ", i);

    foo = pOutBuf;
    while(*foo != ' ') {
      foo++;
      }
    foo += 2;
    for(j=0;j<8;j++) {
      if (((qStats[i] >> j) & 0x01) != 0) {
        *foo++ = yesBlar[j];
        }
      else {
        *foo++ = noBlar[j];
        }
      *foo++ = ' ';
      }
    *foo++ = '\r'; *foo++ = '\n'; *foo = '\0';
    clicharSendOutputBuffer(pOutBuf, bufLen);
    }

  return 0;
}


static int pupHandler(int argc, char *argv[], int flags)
{
  int bufLen;
  char * pOutBuf;
  unsigned char port;

  if (argc < 2)
    return -1;

  port = (unsigned char)strtol(argv[1], (char **)NULL, 16);
  port &= 0x0f;

  pOutBuf = clicharGetOutputBuffer(&bufLen);

  sprintf(pOutBuf, "%s: forcing port [%02x] up at 100MBps/FDX\r\n",
    argv[0], port);

  clicharSendOutputBuffer(pOutBuf, bufLen);

  zarlinkIndexedWrite((port*2),0xd8);

  return 0;
}

static int pdownHandler(int argc, char *argv[], int flags)
{
  int bufLen;
  char * pOutBuf;
  unsigned char port;

  if (argc < 2)
    return -1;

  port = (unsigned char)strtol(argv[1], (char **)NULL, 16);
  port &= 0x0f;

  pOutBuf = clicharGetOutputBuffer(&bufLen);

  sprintf(pOutBuf, "%s: forcing port [%02x] down\r\n",
    argv[0], port);

  clicharSendOutputBuffer(pOutBuf, bufLen);

  zarlinkIndexedWrite((port*2),0xd0);

  return 0;
}


static unsigned char pdqTestArray[4];

static int pdqTestHandler(int argc, char * argv[], int flags)
{
  int retVal = -1;
  int regIndex;
  unsigned short zlData;
  unsigned short zlIndex;
  unsigned char blah;
  char * pOutBuf;
  int bufLen;

  if (argc != 2) {
    return -1;
    }

  pOutBuf = clicharGetOutputBuffer(&bufLen);

  if (parseDottedQuadV4(argv[1], pdqTestArray) == 0) {
    sprintf(pOutBuf, "%s: IP Addr is [%02x] [%02x] [%02x] [%02x]\r\n",
      argv[0], pdqTestArray[0], pdqTestArray[1], pdqTestArray[2], pdqTestArray[3]);
    clicharSendOutputBuffer(pOutBuf, bufLen);
    retVal = 0;
    }
  else {
    retVal = -2;
    }

  return retVal;
}

static int zlresetHandler(int argc, char * argv[], int flags)
{
  zarlinkIndexedWrite(0xf00, 0x08);
  zarlinkInit();
  return 0;
}

int zarlinkUtilMinInit(void)
{
  cliRegisterCommand("zl", zarlinkHandler, "zl [read <index>] [write <index> <hex data>] [status]");
}

#endif /* #ifdef CONFIG_INCLUDE_ZARLINK */

static int uipstatHandler(int argc, char *argv[], int flags)
{
  int retVal = -1;
  char * pOutBuf;
  int bufLen;

  pOutBuf = clicharGetOutputBuffer(&bufLen);

  sprintf(pOutBuf, "%s: IP Packets Rx[%d] Tx[%d] Drop[%d]\r\n",
    argv[0], uip_stat.ip.recv, uip_stat.ip.sent, uip_stat.ip.drop);
  clicharSendOutputBuffer(pOutBuf, bufLen);

  sprintf(pOutBuf, "%s: ICMP Packets Rx[%d] Tx[%d] Drop[%d]\r\n",
    argv[0], uip_stat.icmp.recv, uip_stat.icmp.sent, uip_stat.icmp.drop);
  clicharSendOutputBuffer(pOutBuf, bufLen);

  sprintf(pOutBuf, "%s: TCP Packets Rx[%d] Tx[%d] Drop[%d]\r\n",
    argv[0], uip_stat.tcp.recv, uip_stat.tcp.sent, uip_stat.tcp.drop);
  clicharSendOutputBuffer(pOutBuf, bufLen);

  sprintf(pOutBuf, "%s: TCP Packets Retrans[%d] Conn Attempts[%d]\r\n",
    argv[0], uip_stat.tcp.rexmit, uip_stat.tcp.synrst);
  clicharSendOutputBuffer(pOutBuf, bufLen);

  return 0;
}

int zarlinkUtilInit(void)
{
#ifdef CONFIG_INCLUDE_ZARLINK
  cliRegisterCommand("zl", zarlinkHandler, "zl [read <index>] [write <index> <hex data>] [status]");
  cliRegisterCommand("zlreset", zlresetHandler, "zlreset - software resets the Zarlink packet switch");
  cliRegisterCommand("quick", quickStatHandler, "qstat [port] - retrieve quick port status from switch");
  cliRegisterCommand("pup", pupHandler, "pup [port] - forces port up at 100Mbps/FDX");
  cliRegisterCommand("pdown", pdownHandler, "pdown [port] - forces port down");
#endif
  cliRegisterCommand("uipstat", uipstatHandler, "uipstat - get uIP stack statistics");
  return 0;
}





