/* 1655, Thu 17 Feb 00

   X_SUPPORT.C:  Interface between x_ob.c and x_nm_rc.c

   Copyright (C) 1996-2002 by Nevil Brownlee,
   CAIDA | University of Auckland */

/*
 * $Log: x_support.c,v $
 * Revision 1.1.1.2.2.10  2002/02/23 01:57:25  nevil
 * Moving srl examples to examples/ directory.  Modified examples/Makefile.in
 *
 * Revision 1.1.1.2.2.6  2000/08/08 19:44:48  nevil
 * 44b8 release
 *
 * Revision 1.1.1.2.2.4  2000/06/06 03:38:15  nevil
 * Combine NEW_ATR with TCP_ATR, various bug fixes
 *
 * Revision 1.1.1.2.2.1  1999/11/29 00:17:24  nevil
 * Make changes to support NetBSD on an Alpha (see version.history for details)
 *
 * Revision 1.1.1.2  1999/10/03 21:06:21  nevil
 * *** empty log message ***
 *
 * Revision 1.1.1.1.2.2  1999/09/22 05:38:38  nevil
 * Improve code to work properly on 64-bit machines
 * - Add OS=ALPHA handling to configure.in
 * - Clean up the Alpha compiler warnings
 * - Change all the snmp-related code to use Bit32 instead of unsigned long
 *
 * Revision 1.1.1.1.2.1  1999/01/08 01:38:34  nevil
 * Distribution file for 4.3b7
 *
 * Revision 1.1.1.1  1998/11/16 03:57:28  nevil
 * Import of NeTraMet 4.3b3
 *
 * Revision 1.1.1.1  1998/11/16 03:22:01  nevil
 * Import of release 4.3b3
 *
 * Revision 1.1.1.1.2.1  1998/11/11 23:14:42  nevil
 * Only include malloc.h if we HAVE_MALLOC_H
 *
 * Revision 1.1.1.1  1998/10/28 20:31:26  nevil
 * Import of NeTraMet 4.3b1
 *
 * Revision 1.1.3.2  1998/10/18 23:44:17  nevil
 * Added Nicolai's patches, some 'tidying up' of the source
 *
 * Revision 1.1.3.1  1998/10/13 02:48:32  nevil
 * Import of Nicolai's 4.2.2
 *
 * Revision 1.1.1.1  1998/08/24 12:09:29  nguba
 * NetraMet 4.2 Original Distribution
 *
 * Revision 1.4  1998/05/07 04:28:53  rtfm
 * Implement NetFlowMet, the Cisco NetFlow RTFM meter
 */

#if HAVE_CONFIG_H
#include <ntm_conf.h>
#endif

#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/time.h>
#include <errno.h>
#include <syslog.h>
#if HAVE_SYS_SELECT_H
# include <sys/select.h>
#endif
#if HAVE_MALLOC_H
# include <malloc.h>
#endif
#include <string.h>

#include <Xm/Xm.h>
#include "n_plot.h"

#include "ausnmp.h"

#include "asn1.h"
#include "snmp.h"
#include "snmpimpl.h"
#include "snmpapi.h"
#include "snmpclnt.h"
#include "mib.h"

#include "nmc.h"
#include "nmc_c64.h"
#include "x_nm_rc.h"

String colors[] = {
   "Black", "Navy", "Blue", "LimeGreen", 
   "Orange", "Red", "Magenta", "Purple"
   };
#define NCOLORS  8

void set_log_type(XtPointer lt)
{
   switch ((IntFromPtr)lt) {
   case LGSAMPLE:
      logging_samples = 1;
      break;
    case LGPOINT:
      logging_points = 1;
      break;
    case LGNONE:
      logging_samples = logging_points = 0;
      break;
      }
   }

void set_name_type(XtPointer nt)
{
   show_names = (IntFromPtr)nt ? 1 : 0;  /* No compiler warning on Alpha NetBSD! */
   }


void set_initial_parameters(void)
{
   n_flows_to_plot = n_selected_flows = 0;
   ordinate = (XtPointer)PQPKTS;  /* Byte */
   ptype = (XtPointer)PTSAMPKB;  /* Rate */
   selection = (XtPointer)STRECENT;  /* Recent samples */
   set_axis_range((XtPointer)SX15M);
   set_axis_range((XtPointer)SY900);
   }

void set_ordinate_type(XtPointer ot)
{
   ordinate = ot;
   nm_select_points();  /* Change selection */
   nm_compute_points();  /* Reflect new scale factors */
   }

void set_metric_type(XtPointer pt)
{
   char *msg;
   ptype = pt;
   nm_select_points();  /* Change selection */
   nm_compute_points();  /* Reflect new scale factors */
   if (logging_points) {
      switch ((IntFromPtr)(ptype = pt)) {
      case PTSAMPPC:  /* Sample % */
         msg = "Plotting sample %%";
         break;
      case PTSAMPKB:  /* Sample kbps */
         msg = "Plotting sample counts";
         break;
      case PTFLOWKB:  /* Total flow kB */
         msg = "Plotting total counts";
         break;
         }
      log_msg(LOG_INFO, FALSE, "\n%s\n", msg);
      }
   }

void set_select_type(XtPointer st)
{
   selection = st;
   nm_select_points();  /* Change selection */
   nm_compute_points();  /* Reflect new scale factors */
   }


void set_axis_range(XtPointer at)
{
   switch ((IntFromPtr)at) {
   case SX100S:
      pxmin = 0.7;  pxaxis = 1.0;  pxmax = 100.0;
      pxscale = 1.0;
      xtype = at;
      break;
   case SX15M:
      pxmin = 0.7;  pxaxis = 1.0;  pxmax = 1000.0;
      pxscale = 1.0;
      xtype = at;
      break;
   case SX2H:
      pxmin = 0.7;  pxaxis = 1.0;  pxmax = 120.0;
      pxscale = 60.0;
      xtype = at;
      break;
   case SY40P:
      pymin = 0.06;  pyaxis = 0.1;  pymax = 40.0;
      pyscale = 1.0;
      ytype = at;
      break;
   case SY900:
      pymin = 0.55;  pyaxis = 1.0;  pymax = 900.0;
      pyscale = 1.0;
      ytype = at;
      break;
   case SY9K:
      pymin = 0.07;  pyaxis = 0.1;  pymax = 9.0;
      pyscale = 1000.0;
      ytype = at;
      break;
   case SY90K:
      pymin = 0.7;  pyaxis = 1.0;  pymax = 90.0;
      pyscale = 1000.0;
      ytype = at;
      break;
   case SY900K:
      pymin = 7.0;  pyaxis = 10.0;  pymax = 900.0;
      pyscale = 1000.0;
      ytype = at;
      break;
   case SY9M:
      pymin = 0.07;  pyaxis = 0.1;  pymax = 9.0;
      pyscale = 1000000.0;
      ytype = at;
      break;
   case SY90M:
      pymin = 0.7;  pyaxis = 1.0;  pymax = 90.0;
      pyscale = 1000000.0;
      ytype = at;
      break;
      }
   nm_compute_points();  /* Reflect new scale factors */
   }

void draw_xaxis(void)
{
   switch ((IntFromPtr)xtype) {
   case SX100S:
   case SX15M:
      xaxis(pxaxis,pyaxis, 10.0);
      xaxis(pxaxis/5,pyaxis, 10.0);
      xaxis(pxaxis/2,pyaxis, 10.0);
      aoutst("seconds",-7);
      break;
   case SX2H:
      xaxis(pxaxis/2,pyaxis, 10.0);
      xaxis(pxaxis/5,pyaxis, 10.0);
      xaxis(pxaxis,pyaxis, 10.0);
      aoutst("minutes",-7);
      break;
      }
   }

static char *y_units(void)
{
   switch ((IntFromPtr)ptype) {
   case PTSAMPPC:
      return "%";
      break;
   case PTSAMPKB:
      switch ((IntFromPtr)ordinate) {
      case PQBYTES:
         switch ((IntFromPtr)ytype) {
         case SY40P:
         case SY900:
            return "bps";
            break;
         case SY9K:
         case SY90K:
         case SY900K:
            return "kbps";
            break;
         case SY9M:
         case SY90M:
            return "Mbps";
            break;
            }
         break;
      case PQPKTS:
         switch ((IntFromPtr)ytype) {
         case SY40P:
         case SY900:
            return "pps";
            break;
         case SY9K:
         case SY90K:
         case SY900K:
            return "kpps";
            break;
         case SY9M:
         case SY90M:
            return "Mpps";
            break;
            }
         break;
         }
      break;
   case PTFLOWKB:
      switch ((IntFromPtr)ordinate) {
      case PQBYTES:
         switch ((IntFromPtr)ytype) {
         case SY40P:
         case SY900:
            return "B";
            break;
         case SY9K:
         case SY90K:
         case SY900K:
            return "kB";
            break;
         case SY9M:
         case SY90M:
            return "MB";
            break;
            }
         break;
      case PQPKTS:
         switch ((IntFromPtr)ytype) {
         case SY40P:
         case SY900:
            return "pkt";
            break;
         case SY9K:
         case SY90K:
         case SY900K:
            return "kpkt";
            break;
         case SY9M:
         case SY90M:
            return "Mpkt";
            break;
            }
         break;
         }
      break;
      }
   }

void draw_yaxis(void)
{
   char *yu = y_units();
   switch ((IntFromPtr)ytype) {
   case SY40P:
      yaxis(pxaxis,pyaxis, 10.0);
      yaxis(pxaxis,pyaxis/2, 10.0);
      yaxis(pxaxis,pyaxis/5, 10.0);
      break;
   case SY900:
   case SY9K:
   case SY90K:
   case SY900K:
   case SY9M:
   case SY90M:
      yaxis(pxaxis,pyaxis, 10.0);
      yaxis(pxaxis,pyaxis/5, 10.0);
      yaxis(pxaxis,pyaxis/2, 10.0);
      break;
      }
   aoutst(yu, -strlen(yu));
   }

void draw_title(void)
{
   char buf[100], *bp;
   switch ((IntFromPtr)ordinate) {
   case PQBYTES:
      bp = strmov(buf, "Byte ");
      break;
   case PQPKTS:
      bp = strmov(buf, "Packet ");
      break;
      }
   switch ((IntFromPtr)ptype) {
   case PTSAMPPC:
      bp = strmov(bp, "%");
      break;
   case PTSAMPKB:
      bp = strmov(bp, "Rate");
      break;
   case PTFLOWKB:
      bp = strmov(bp, "Count");
      break;
      }
   bp = strmov(bp, " vs Flow Duration for ");
   switch ((IntFromPtr)selection) {
   case STLAST:
      bp = strmov(bp, "Last Sample");
      break;
   case STRECENT:
      bp = strmov(bp, "Recent Samples");
      break;
   case STALL:
      bp = strmov(bp, "All Samples");
      break;
      }
   *bp = '\0';
   aoutst(buf,-strlen(buf));
   }


int sort_cmp(const void *a, const void *b)
{
   if (((struct flow_select *)a)->traffic <  /* Reverse numeric order */ 
      ((struct flow_select *)b)->traffic) return 1;
   else if (((struct flow_select *)a)->traffic == 
      ((struct flow_select *)b)->traffic) return 0;
   else return -1;
   }

void nm_select_points(void)
{
   unsigned int fi;
   unsigned long ft;
   n_flows_to_plot = n_selected_flows = 0;
   for (fi = 1; fi != max_flows+2; ++fi) {
      if (selection == STLAST) {
         if (flow_table[fi].active > 0) continue;
         }
      else if ((IntFromPtr)selection == STRECENT) {
         if (flow_table[fi].active >= n_recent) continue;
         }
      if (ordinate == PQBYTES) {  /* Compute traffic */
         switch ((IntFromPtr)ptype) {
         case PTSAMPPC:
         case PTSAMPKB:
            ft = flow_table[fi].up_byte_rate +
               flow_table[fi].down_byte_rate;
            break;
         case PTFLOWKB:
            sum64(ft, flow_table[fi].up_byte_count,
               flow_table[fi].down_byte_count);
            break;
            }
         }
      else {
         switch ((IntFromPtr)ptype) {
         case PTSAMPPC:
         case PTSAMPKB:
            ft = flow_table[fi].up_pkt_rate +
               flow_table[fi].down_pkt_rate;
            break;
         case PTFLOWKB:
            sum64(ft, flow_table[fi].up_pkt_count,
               flow_table[fi].down_pkt_count);
            break;
            }
         }
      tt += ft;
      select_table[n_selected_flows].traffic = ft;
      select_table[n_selected_flows].flownbr = fi;
      ++n_selected_flows;
      }
   qsort(select_table, n_selected_flows, sizeof(struct flow_select),
      sort_cmp);
   }

void nm_compute_points(void)
{
   unsigned short i;
   struct flow_select *fsp;
   struct display_data *ddp;
   unsigned long s_time;
   float s_scale;

   n_flows_to_plot = 0;
   if (n_selected_flows == 0) return;
   if (n_selected_flows > nd_flows)  /* nd_flows = max nbr of flows to plot */
      n_selected_flows = nd_flows;
   for (i = 0; i != n_selected_flows; ++i) {  /* Build plot data */
      fsp = &select_table[i];
      if (fsp->traffic == 0) break;  /* Very few active flows! */
      ddp = &dflows[n_flows_to_plot];
      ddp->flownbr = fsp->flownbr;
      ddp->active = flow_table[fsp->flownbr].active;
      ddp->vx = (float)(flow_table[fsp->flownbr].lasttime -
         flow_table[fsp->flownbr].starttime)/(100.0*pxscale);
      if (ddp->vx <= 0)  /* NetFlow sometimes has LastTime < FirstTime */
         ddp->vx = 0.01;
      switch ((IntFromPtr)ptype) {
      case PTSAMPPC:
         ddp->vy = (float)(fsp->traffic)*100.0/  /* traffic % */
            ((float)tt*pyscale);
         break;
      case PTSAMPKB:
         s_time = flow_table[fsp->flownbr].interval;
         if (s_time == 0)  s_time = 10;  /* Avoid divide by zero */
         /* NetFlow sometimes gives FirstTime <= LastTime.
            Rather than ignore them, we use a time of 10ms */
         s_scale = ((float)s_time/100.0)*pyscale;
         switch ((IntFromPtr)ordinate) {
         case PQBYTES:
            ddp->vy = ((float)fsp->traffic*8.0)/s_scale;  /* rate bps */
            break;
         case PQPKTS:
            ddp->vy = (float)fsp->traffic/s_scale;  /* rate pps */
            break;
            }
         break;
      case PTFLOWKB:
         ddp->vy = (float)fsp->traffic/pyscale;  /* flow bytes */
         break;
	 }
      ++n_flows_to_plot;
      }
   }

void nm_plot_points(void)
{
   int i;  struct display_data *ddp;
   float px,py;
   unsigned char s_active = MXIDLE;
   for (i = 0; i != n_flows_to_plot; ++i) {
      ddp = &dflows[i];
      if (ddp->vx < pxmin) px = pxmin;
         else if (ddp->vx > pxmax) px = pxmax;
         else px = ddp->vx;
      if (ddp->vy < pymin) py = pymin;
         else if (ddp->vy > pymax) py = pymax;
         else py = ddp->vy;
      vtostransform(&ddp->sx,&ddp->sy, px,py);
      movabs(ddp->sx,ddp->sy);
      if (ddp->active != s_active) {
         s_active = ddp->active >= NCOLORS ? NCOLORS-1 : ddp->active;
         set_plot_color(colors[s_active]);
         }
      pltsym(flow_table[ddp->flownbr].flowkind);
      }
   }

int nm_nearest_flow(int sx,int sy)
{
   int i;  struct display_data *ddp;
   struct flow_info fi;
   int d, mind, mini;

   if (n_flows_to_plot == 0) return -1;
   ddp = &dflows[0];  mini = 0;
   mind = (sx-ddp->sx)*(sx-ddp->sx) + (sy-ddp->sy)*(sy-ddp->sy);
   for (i = 1; i != n_flows_to_plot; ++i) {
      ddp = &dflows[i];
      d = (sx-ddp->sx)*(sx-ddp->sx) + (sy-ddp->sy)*(sy-ddp->sy);
      if (d < mind) {
         mind = d;  mini = i;
         }
      }
   if (mind > 100)  /* Not close enough to a point */
      return -1;
   else return mini;
   }

void nm_flow_details(char *msg, int action, int mini)
{
   struct display_data *ddp;
   struct flow_info fi;
   unsigned char a,col;
   char buf[300], *mp;

   ddp = &dflows[mini];
   sprintf(buf,"(%.2f ", ddp->vx);
   mp = strmov(msg,buf);
   switch ((IntFromPtr)xtype) {
   case SX100S:
   case SX15M:
      mp = strmov(mp,"s, ");
      break;
   case SX2H:
      mp = strmov(mp,"m, ");
      break;
      }
   sprintf(buf,"%.2f %s)  ", ddp->vy,y_units());
   mp = strmov(mp,buf);

   switch (action) {
   case 1:  /* Button 1 up */
      memset(&fi, 0, sizeof(fi));
      if (flow_info(plot_ms, &fi, ddp->flownbr) == 0) {
         mp = strmov(mp, "no info (flow memory recovered by meter)");
         *mp = '\0';
         break;
         }
      for (col = plot_ms->format[a = 0]; ; ) {
         if (col != '\0') mp = sdisplay_attrib(mp, &fi,col,plot_ms);
         if ((col = plot_ms->format[a+1]) == '\0') break;
         mp = strmov(mp,plot_ms->separator[a++]);
         }
      *mp = '\0';
      if (logging_points) {
         log_msg(LOG_INFO, FALSE, "%s\n",msg);
         }
      break;
   case 2:  /* Button 2 down */
      sprintf(buf, "Packets: Total %lu fwd %lu back, "
            "Sample %lu fwd %lu back in %.2f s",
#if SIZEOF_LONG_LONG == 8 || SIZEOF_LONG == 8
         flow_table[ddp->flownbr].up_pkt_count,
         flow_table[ddp->flownbr].down_pkt_count,
         flow_table[ddp->flownbr].up_pkt_rate,
         flow_table[ddp->flownbr].down_pkt_rate,
#else
         flow_table[ddp->flownbr].up_pkt_count.low,
         flow_table[ddp->flownbr].down_pkt_count.low,
         flow_table[ddp->flownbr].up_pkt_rate,
         flow_table[ddp->flownbr].down_pkt_rate,
#endif
         (float)flow_table[ddp->flownbr].interval/100.0 );
      mp = strmov(mp,buf);
      *mp = '\0';
      break;
   case 3:  /* Button 3 down */
      sprintf(buf, "kBytes: Total %.1f fwd %.1f back, "
            "Sample %.1f fwd %.1f back in %.2f s",
         (float)fraction64(flow_table[ddp->flownbr].up_byte_count, 
            1000.0),
         (float)fraction64(flow_table[ddp->flownbr].down_byte_count,
            1000.0),
         (float)flow_table[ddp->flownbr].up_byte_rate/1000.0,
         (float)flow_table[ddp->flownbr].down_byte_rate/1000.0,
         (float)flow_table[ddp->flownbr].interval/100.0 );
      mp = strmov(mp,buf);
      *mp = '\0';
      break;
      }
   }
