#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include "et1000.h"
#include "smartbits.h"
#include "tests_body.h"

extern int force_stop;

/**********************************************************************/

/**********************************************************************/
/* linkToSmartBits
	Checks state of link to SmartBits chassis.  If it exists we skip over
	the rest of the procedure.  If it does not exist (the return value of
	ETGetLinkStatus is less than 1) the user is prompted for the IP address of
	the chassis.
	The status of the ETSocketLink command is tested and if there is an error
	(status is negative), an error message is displayed.

*/
void linkToSmartBits()
{
   char ipaddr[16] = IPADR;
    // ETGetLinkStatus will be positive if we're linked
   int iRetVal = ETGetLinkStatus();
    // If return was negative we're not linked. Prompt for IP and link
   if (iRetVal < 1) 
   {
	  printf("\nConnecting to SmartBits chassis...\n");
	  iRetVal = NSSocketLink(ipaddr,16385,RESERVE_NONE);
	  // Return Value is negative if link did not succeed. Display message and exit
	  if ( iRetVal < 0)
	  {
	     printf("ERROR: %d\n", iRetVal);
	     printf("   Could not link to chassis at IP address %s\n", ipaddr);
		 exit(-1);
	  } 
   }
}

/**********************************************************************/
/* setTrigger
	Sets a trigger to match the base source MAC address.  Since we have a 
	cycle count of five on the VFD1 we are triggering on, our trigger will fire
	every fifth packet

*/
void setTrigger(int h1, int s1, int p1)
{
	HTTriggerStructure MyTrigger;
	// start 48 bits after preamble (SOURCE MAC)
	MyTrigger.Offset = 48;
	// trigger pattern is 6 bytes long
	MyTrigger.Range = 6;
	// data to match is 66 55 44 33 22 11
	MyTrigger.Pattern[0] = 0x11;
	MyTrigger.Pattern[1] = 0x22;
	MyTrigger.Pattern[2] = 0x33;
	MyTrigger.Pattern[3] = 0x44;
	MyTrigger.Pattern[4] = 0x55;
	MyTrigger.Pattern[5] = 0x66;
	// send config to card 
	CHECKERROR(HTTrigger(HTTRIGGER_1,HTTRIGGER_ON,&MyTrigger,h1,s1,p1));
}

/**********************************************************************/
/* clearCounters
	zero out the counters on the target Hub Slot Port
*/
void	clearCounters(int h1, int s1, int p1)
{
	CHECKERROR(HTClearPort(h1, s1, p1));
}

/**********************************************************************/

void show_cntrs(HTCountStructure *cs, int p1)
{
  HTCountStructure cs_local;

  if (cs == NULL) {
    cs = &cs_local;
    CHECKERROR(HTGetCounters(cs, HUB, SLOT, p1));
  }
	printf("=========================================\n");
	printf("Counter Data Card %d PORT %d\n", (SLOT + 1), p1);
	printf("=========================================\n");
	printf("\t Transmitted Pkts  %lu\n", cs->TmtPkt);
	printf("\t Received Pkts     %lu\n", cs->RcvPkt);
	printf("\t Collisions        %lu\n", cs->Collision);
	printf("\t Received Triggers %lu\n", cs->RcvTrig);
	printf("\t CRC Errors        %lu\n", cs->CRC);
	printf("\t Alignment Errors  %lu\n", cs->Align);
	printf("\t Oversize Pkts     %lu\n", cs->Oversize);
	printf("\t Undersize Pkts    %lu\n", cs->Undersize);
	printf("\t Mbit/s            %lu\n", cs->RcvByteRate *8 /1000000L);
	printf("=========================================\n");
}
/**********************************************************************/

int get_allcntrs(int mask, HTCountStructure *cntrs)
{
  int i, ret = 0;

  for(i=0; i<SMB_PORTS; ++i) {
    if (mask & PORT_TO_PMASK(i))
      ret |= HTGetCounters(cntrs+i, HUB, SLOT, i);
  }

  if(ret) {
    fprintf(stderr, "Could not read SMB counters\n");
    return -1;
  }
  return 0;
}

int show_allcntrs(FILE *f, int mask, HTCountStructure *cntrs)
{
  HTCountStructure cs[SMB_PORTS];
  int i, ret = 0;

  if (cntrs == NULL) {
    cntrs = cs;
    ret = get_allcntrs(mask, cntrs);
  }

  if (!f) f = stdout;

  if(ret) {
    fprintf(stderr, "Could not read SMB counters\n");
    return -1;
  }

	fprintf(f, "=====================================================================\
====================================================================\n");
  fprintf(f, "| Port \t| Tframe \t| Rframe \t| Collis \t| Rtrig \t| CRCerr \t| Oversz \t| Undersz \t| Mbit/s \t|\n");
	fprintf(f, "=====================================================================\
====================================================================\n");
  for(i = 0; i<SMB_PORTS; ++i) {
    if (mask & (1<<i) )
      fprintf(f, "| %u \t| %10lu \t| %10lu \t| %10lu \t| %10lu \t| %10lu \t| %10lu \t| %10lu \t| %10lu \t|\n",
          i, cntrs[i].TmtPkt, cntrs[i].RcvPkt, cntrs[i].Collision, cntrs[i].RcvTrig, cntrs[i].CRC,
          cntrs[i].Oversize, cntrs[i].Undersize, cntrs[i].RcvByteRate*8/1000000L);
  }

  return 0;
}

int clear_allcntrs(int mask)
{
  int i, ret = 0;

  for(i=0; i<SMB_PORTS; ++i) {
    if (mask & (1<<i))
      ret |= HTClearPort(HUB, SLOT, i);
  }

  return ret;
}
/**********************************************************************/

int show_floss(FILE *f, HTCountStructure *cntrs, unsigned long *total)
{
  int i;
  float loss[SMB_PORTS];

  fprintf(f, "===========================\n");
  fprintf(f, "| Port \t| Loss rate \t|\n");
  fprintf(f, "===========================\n");
  for(i=0; i<SMB_PORTS; ++i) {
    if(total[i]!=0) 
      loss[i] = (total[i]-cntrs[i].RcvTrig)*100.0/total[i];
    else
      loss[i] = 0;
    fprintf(f, "| %d  \t| %f \t|\n", i, loss[i]);
  }
}

/**********************************************************************/

int set_port_config(int p)
{
  NSPhyConfig phycfg;
  GIGAutoFiberNegotiate autoneg;

  bzero(&phycfg, sizeof(NSPhyConfig));

#if FIBER
  phycfg.ucActiveMedia = FIBER_MODE;
#else
  phycfg.ucActiveMedia = COPPER_MODE;
#endif

  if (HTSetStructure(NS_PHY_CONFIG, 0, 0, 0, &phycfg, sizeof(NSPhyConfig),
        HUB, SLOT, p) < 0)
    return -1;
  //else
  //  printf("Port %d configuration set\n", p);

#if FIBER
  bzero(&autoneg, sizeof(GIGAutoFiberNegotiate));
  autoneg.ucMode = 1;
  //autoneg.ucRestart = 1;
  CHECKERROR( HTSetStructure(GIG_STRUC_AUTO_FIBER_NEGOTIATE, 0, 0, 0, &autoneg,
      sizeof(GIGAutoFiberNegotiate), HUB, SLOT, p));
  //printf("Autoneg set for port %d\n", p);

  HTResetPort(RESET_PARTIAL, HUB, SLOT, p);

  //bzero(&phycfg, sizeof(NSPhyConfig));
  //phycfg.ucActiveMedia = COPPER_MODE;
  //HTSetStructure(NS_PHY_CONFIG, 0, 0, 0, &phycfg, sizeof(NSPhyConfig), HUB, SLOT, p);
  //NSDelay(2);
  //bzero(&phycfg, sizeof(NSPhyConfig));
  //phycfg.ucActiveMedia = FIBER_MODE;
  //HTSetStructure(NS_PHY_CONFIG, 0, 0, 0, &phycfg, sizeof(NSPhyConfig), HUB, SLOT, p);
  //NSDelay(2);

  //bzero(&autoneg, sizeof(GIGAutoFiberNegotiate));
  //autoneg.ucMode    = 1;
  //autoneg.ucRestart = 1;
  //CHECKERROR( HTSetStructure(GIG_STRUC_AUTO_FIBER_NEGOTIATE, 0, 0, 0, &autoneg,
  //    sizeof(GIGAutoFiberNegotiate), HUB, SLOT, p));
  //NSDelay(5);
  //HTSetStructure(NS_PHY_CONFIG, 0, 0, 0, &phycfg, sizeof(NSPhyConfig), HUB, SLOT, p);
  //NSDelay(2);
  //printf("test1\n"); fflush(stdout);
  //HTResetPort(RESET_FULL, HUB, SLOT, p);
  //printf("test2\n"); fflush(stdout);
  //NSDelay(2);
  //bzero(&phycfg, sizeof(NSPhyConfig));
  //phycfg.ucActiveMedia = FIBER_MODE;
  //HTSetStructure(NS_PHY_CONFIG, 0, 0, 0, &phycfg, sizeof(NSPhyConfig), HUB, SLOT, p);
  //printf("test3\n"); fflush(stdout);
  //NSDelay(2);
#endif



  return 0;
}

/**********************************************************************/

void show_card_info(int p)
{
  GIGCardInfo ci;
  NSPhyConfig phycfg;
  CHECKERROR(HTGetStructure(GIG_STRUC_CARD_INFO, 0, 0, 0, &ci, sizeof(GIGCardInfo), HUB, SLOT, p));
  CHECKERROR(HTGetStructure(NS_PHY_CONFIG_INFO, 0, 0, 0, &phycfg, sizeof(NSPhyConfig), HUB, SLOT, p));

  //printf("Configuration: %x\n", ci.uiLinkConfiguration);
  printf("ActiveMedia: %d\n", phycfg.ucActiveMedia);

  //phycfg.ucActiveMedia = FIBER_MODE;

}

int detect_stop(void)
{
  fd_set fds;
  struct timeval tv;
  int ret;

  FD_ZERO(&fds);
  FD_SET(0, &fds);
  tv.tv_sec = 0;
  tv.tv_usec = 500000;

  ret = select(1, &fds, NULL, NULL, &tv);

  if(ret > 0 && getchar() == 'q') force_stop = 1;

  return ret;
}


long ratio_to_fps(int frame_size, float ratio)
{
  long max_fps = 1000000000L / (frame_size+24) / 8;
  //printf("max fps: %lu\n", max_fps);

  return max_fps*ratio;
}

long ratio_to_gap(int frame_size, float ratio)
{
  long fps = ratio_to_fps(frame_size, ratio);

  return (1000000000L - (frame_size+12)*8*fps)/fps;
}


int reset_ports(int mask)
{
  int i, ret = 0;

  for (i=0; i<SMB_PORTS; ++i) {
    if(mask & (1<<i)) {
      //requested port reset
      ret = HTResetPort(RESET_FULL, HUB, SLOT, i);
      ret |= HTClearPort(HUB, SLOT, i);
      ret |= set_port_config(i);
      if (ret) {
        fprintf(stderr, "Could not reset & configure port %d\n", i);
        return ret;
      }
    }
  }

  return 0;
}

int run_traffic(int mask, int start)
{
  int i, ret;

  //for (i=0; i<SMB_PORTS; ++i) {
  //  if (mask & (1<<i)) {
  //    if (start)
  //      ret = HTRun(HTRUN, HUB, SLOT, i);
  //    else
  //      ret = HTRun(HTSTOP, HUB, SLOT, i);
  //    if (ret) {
  //      fprintf(stderr, "Could not %s transmission on port %d\n", start?"start":"stop", i);
  //      return ret;
  //    }
  //  }
  //}

  HGSetGroup(NULL);
  for (i=0; i<SMB_PORTS; ++i) {
    if (mask & (1<<i))
      HGAddtoGroup(HUB, SLOT, i);
  }

  if(start) ret = HGStart();
  else      ret = HGStop();

  if (ret) {
    fprintf(stderr, "Could not %s transmission on ports %x\n", start?"start":"stop", mask);
    return ret;
  }

  return 0;
}

int wait_linkup(int mask)
{
  int i, led, tout;

  for(i=0; i<SMB_PORTS; ++i) {
    if (mask & (1<<i)) {
      tout = LINKUP_TIMEOUT;
      do {
        NSDelay(1);
        led = HTGetLEDs(HUB, SLOT, i);
        --tout;
      } while( !(led & HTLED_LINKED) && tout );
      //printf("Port %d LED: 0x%X\n", i, led);
      if (!tout) {
        printf("Port %d is still down...\n", i);
        return -1;
      }
    }
  }

  return 0;
}

/* sends fnum frames from each masked port
 * dmac ff:ff:ff:ff:ff:ff
 * smac mac[n] */
int smb_learning(int mask, char mac[][6], int fnum)
{
  int i;
  char broadcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
  //char broadcast[6] = {0x90, 0xe2, 0xba, 0x17, 0xa7, 0xaf};

  //printf("Learning on ports: 0x%X\n", mask);
  wait_linkup(mask);
  for (i=0; i<SMB_PORTS; ++i) {
    if (mask & (1<<i)) {
      setup_stream(i, broadcast, mac[i], FRAME_SZ, fnum, 0.001, 0, -1, 0);
      setup_stream(i, broadcast, mac[i], FRAME_SZ, fnum, 0.001, 0, -1, -1);
    }
  }
  run_traffic(mask, 1);

  return 0;
}

int smb_set_trigger(char *pat, int port, int offset, int range, int trigno)
{
  HTTriggerStructure trig;
  int i;

  bzero(&trig, sizeof(HTTriggerStructure));
  trig.Offset = offset*8;
  trig.Range  = range;
  for(i=0; i<range; ++i) {
    if (trigno == HTTRIGGER_1)
      trig.Pattern[i] = pat[5-i];
    else
      trig.Pattern[i] = pat[i];
  }
  
  //printf("port: %d, offset: %d, range: %d, %02X:%02X:%02X:%02X:%02X:%02X, %02X:%02X:%02X:%02X:%02X:%02X\n", port, trig.Offset, trig.Range,
  //    pat[0]&0xff, pat[1]&0xff, pat[2]&0xff, pat[3]&0xff, pat[4]&0xff, pat[5]&0xff,
  //    trig.Pattern[0]&0xff, trig.Pattern[1]&0xff, trig.Pattern[2]&0xff,
  //    trig.Pattern[3]&0xff, trig.Pattern[4]&0xff, trig.Pattern[5]&0xff);

  if (trigno == HTTRIGGER_1)
    return( HTTrigger(trigno, HTTRIGGER_ON, &trig, HUB, SLOT, port) );
  else
    /* TRIGGER 2 (ethtype) when TRIG1 (SMAC) met */
    return( HTTrigger(trigno, HTTRIGGER_DEPENDENT, &trig, HUB, SLOT, port) );
}

/***************************************************/

int wait_test_done(int mask, int sec)
{
  int i, run = 1;
  HTCountStructure cntrs[SMB_PORTS];

  if (sec>0) {
    /* continous stream, wait for a given time */
    sleep(sec);
    return 0;
  }

  /* wait untill all frames on all required ports sent */
  while(run) {
    get_allcntrs(mask, cntrs);
    run = 0;
    for(i=0; i<SMB_PORTS; ++i) {
      if ((mask & PORT_TO_PMASK(i)) && cntrs[i].TmtPktRate != 0)
        run = 1;
    }
  }
  return 0;
}


/***************************************************/
int exp_rx_frames(struct smb_test *wrst, HTCountStructure *cntrs, unsigned long *exp)
{
  int i_port, idp, j;
  struct smb_port_tx *p_tx;

  /* clear exp[] */
  for(i_port=0; i_port<SMB_PORTS; ++i_port)
    exp[i_port] = 0;

  for(i_port=0; i_port<SMB_PORTS; ++i_port) {
    /* take TX port */
    p_tx = &(wrst->ptx[i_port]);
    /* iterate through DST portx of TX port */
    for(idp=0; idp < p_tx->d_ports_n; ++idp) {
      if(p_tx->d_ports[idp] < SMB_PORTS && wrst->ftime == 0) {
        /* first check if DST port is not broadcast */
        /* increment appropriate port */
        exp[ p_tx->d_ports[idp] ] += p_tx->fnum;
      }
      else if(p_tx->d_ports[idp] < SMB_PORTS && wrst->ftime > 0) {
        /* still unicast, but now TX time was given instead of fnum */
        /* all streams from one port have the same load */
        exp[ p_tx->d_ports[idp] ] += cntrs[i_port].TmtPkt/p_tx->d_ports_n;
        /* now check if at the end of the stream run, one more frame wasn't sent
         * to analyzed port */
        if( cntrs[i_port].TmtPkt % p_tx->d_ports_n > idp )
          exp[ p_tx->d_ports[idp] ]++;
      }
      else if(p_tx->d_ports[idp] == TX_PALL){
        /* broadcast - increment all but TX port port */
        for(j=0; j<SMB_PORTS; ++j)
          if(j != i_port) 
            exp[j] + cntrs[i_port].TmtPkt;
      }
    }
  }
    
  return 0;
}
