/* 1610, Fri 12 Sep 97

   CHART.C:  Strip chart in screen window for AU monitor
             Time functions for PC (using class PentiumClock)

   Copyright (C) 1992-1999 by Nevil Brownlee,
   ITSS Technology Development,  The University of Auckland */

/*
 * $Log: chart.cpp,v $
 * Revision 1.1.1.2  1999/10/03 21:06:22  nevil
 * *** empty log message ***
 *
 * Revision 1.1.1.1.2.2  1999/05/26 02:41:37  nevil
 * Integrate V6 and ASN code into PC versions of the meter.
 * This required a rework of the makefiles, using @cflags.opt files
 * to provide a much longer command line to the Borland C compiler.
 *
 * Revision 1.1.1.1.2.1  1999/01/08 01:31:52  nevil
 * Implementation of TCP attributes, part 3
 *
 * 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  1998/10/28 20:31:26  nevil
 * Import of NeTraMet 4.3b1
 *
 * Revision 1.1.3.2.2.1  1998/10/19 22:32:43  nevil
 * Meter improvements, mostly arising from developments for the
 * OCxMON meter.  These are documented in notes_oc.txt
 *
 * Revision 1.1.3.2  1998/10/14 04:53:21  nevil
 * Merge Nicolai's patches into 4.2.1 distribution
 *
 * Revision 1.1.1.1  1998/10/13 01:35:03  nevil
 * Import of NeTraMet 4.2.1
 *
 * Revision 1.1.1.1  1998/08/24 12:09:29  nguba
 * NetraMet 4.2 Original Distribution
 */

#define UPTIME_DBG 1

#ifndef __DPMI32__
#include <bios.h>
#endif
#include <dos.h>

#include <stdio.h>
#include <stdlib.h>
#include <mem.h>
#include <bios.h>    /* biostime() */
#include <conio.h>   /* gettextinfo() */
#include <math.h>

#include "ausnmp.h"

#include "asn1.h"
#include "snmp.h"
#include "snmpimpl.h"

#include "..\types\types.h"  /* Bit8, Bit16, Bit32 .. */
#include "..\intel\timestmp.h"      /* class PentiumClock */

#include "pktsnap.h"
#include "flowhash.h"
#include "met_vars.h"

#ifndef CLK_86  /* CLK_86 timer handles offset itself */
PentiumClock uptime_offset;
#endif

#if UPTIME_DBG
extern int bad_uptimes;  /* Declared in met_vars.c */
static unsigned long last_s = 0L;
static PentiumClock last_clock;
static Bit32 last_rb = 0;

#define SZUTLOG 20
struct utlog_ent {
   Bit32 pkt_interval;  /* Nbr of packets since last bad time */
   Bit64 time, last_time;
   };
static struct utlog_ent utlog[SZUTLOG];   
static int utlogx = 0;
static Bit32 ut_count = 0;

extern "C"
void print_uptime_log(void)
{
   int j,k,n;
   char msg[80];
   struct utlog_ent *ep;
   if (bad_uptimes > SZUTLOG) {
      n = SZUTLOG;
      k = utlogx;
      }
   else {
      n = bad_uptimes;
      k = utlogx+(SZUTLOG-bad_uptimes);  if (k >= SZUTLOG) k -= SZUTLOG;
      }
   for (j = 0; j != n; ++j) {
      ep = &utlog[k];
      sprintf(msg, "%8lu  %08lx %c %08lx", 
         ep->pkt_interval, ep->time.low, 
         ep->time < ep->last_time ? '<' : '>', 
         ep->last_time.low);
      printf(msg);
      w_roll(0,7, scr_chrtcol-1,scr_lrow, 1);
      if (++k == SZUTLOG) k = 0;
      }
   printf("%d bad uptimes", bad_uptimes);
   }
#endif


extern "C"
void start_uptime_clock(void)
{
#ifndef CLK_86
   uptime_offset.set();
#endif
   }

extern "C"
Bit32 uptime_cs(void)
{
   PentiumClock now;
   now.set();
#ifndef CLK_86
   now -= uptime_offset;
#endif
   return now.get_seconds()*100.0;
   }

extern "C"
Bit32 uptime_s(void)
{
   static long ts;
   ts = uptime_cs()/100;
   return ts;
   }

#if OCX_NTM || OC3_NTM
extern "C"
double uptime(void)  /* Since startup, in 'native' ticks */
{
   PentiumClock now;
   now.set();
#ifndef CLK_86
   now -= uptime_offset;
#endif
   return now.get_seconds()*100.0;
   }

extern "C"
double centiseconds(double s)
{
   return s*100.0;
   }

extern "C"
double microseconds(double s)
{
   return s*1000000.0;
   }

#else  /* PC 32- or 16-bit (Unix meters do this in meter*.c) */

extern "C"
counter64 uptime(void)  /* Since startup, in 'native' ticks */
{
   PentiumClock now;
   counter64 r;
   now.set();
#ifndef CLK_86
   now -= uptime_offset;
#endif
   r.high = now.high;  r.low = now.low;
   return r;
   }
   
extern "C"
double centiseconds(counter64 nt)
{
   PentiumClock now;
#if UPTIME_DBG
   struct utlog_ent *ep;
   Bit32 rb;
   now.high = nt.high;  now.low = nt.low;
   rb = now.get_seconds()*100.0;
   if (rb < last_rb) {
      ep = &utlog[utlogx];
      ep->pkt_interval = ut_count;
      ep->time = now;
      ep->last_time = last_clock;
      if (++utlogx == SZUTLOG) utlogx = 0;
      ++bad_uptimes;
      ut_count = 0;
      }
   else ++ut_count;
   last_rb = rb;
   last_clock = now;
   return rb;
#else
   now.high = nt.high;  now.low = nt.low;
   return now.get_seconds()*100.0;
#endif
   }

extern "C"
double microseconds(counter64 nt)
{
   PentiumClock now;
   now.high = nt.high;  now.low = nt.low;
   return now.get_seconds()*1000000.0;
   }
#endif

#define MTICKS     1092L  /* 18.20000 per second */
#define HTICKS    65543L  /* 18.20639 */
#define DTICKS  1573040L  /* 18.20648 */

extern "C"
void set_tod(void)
{
   Bit32 tt;
   tt = biostime(0,0L);

   tod_h = tt/HTICKS;  tt -= (long)tod_h*HTICKS;
   tod_m = tt/MTICKS;  tt -= (long)tod_m*MTICKS;
   tod_s = (tt*10+91L)/182L;
   if (tod_s == 60) {
      ++tod_m;  tod_s = 0;
      }
   if (tod_m == 60) {
      ++tod_h;  tod_m = 0;
      }
   }

extern "C"
void scinit(void)  /* Initialise screen routines */
{
   struct text_info r;
   gettextinfo(&r);

   scr_lrow = r.screenheight - 1;  /* Display screen size */
   scr_lcol = r.screenwidth - 1;
   scr_mtrow = 7;   /* Top line of status message area */
   scr_chrtcol = scr_lcol - 38;  /* Leftmost col of chart */
   }
   
extern "C"
void scpos(int x, int y)  /* Set Cursor Position */
{
#ifdef __DPMI32__
   union REGS regs;
   regs.h.ah = 2;
   regs.h.bh = 0;
   regs.h.dh = y;  regs.h.dl = x;
   regs.w.flags = 0;
   int386(0x10, &regs, &regs);
#else
   _AH = 2;  _BH = 0;  _DH = y;  _DL = x;
   geninterrupt(0x10);
#endif
   }

static void sputc(int c)  /* Write char to screen, don't move cursor */
{  // Only used in chart()
#ifdef __DPMI32__
   union REGS regs;
   regs.w.flags = 0;
   regs.h.bh = 0;
   regs.w.cx = 1;
   regs.h.ah = 10;
   regs.h.al = c;
   int386(0x10, &regs, &regs);
#else
   _BH = 0;  _CX = 1;  _AH = 10;  _AL = c;
   geninterrupt(0x10);
#endif
   }

void swrite(int c)  /* Write char to screen */
{
#ifdef __DPMI32__
   union REGS regs;
   regs.w.flags = 0;
   regs.h.ah = 14;
   regs.h.al = c;
   int386(0x10, &regs, &regs);
#else
   _AH = 14;  _AL = c;
   geninterrupt(0x10);
#endif
}

#define UP     0x06  /* BIOS scroll parameters */
#define DOWN   0x07
#define CHATR  0x07  /* Screen attribute for chart */

extern "C"
void bios_scroll(int way, int tx,int ty, int bx,int by, int nrows, int attr)
{
#ifdef __DPMI32__
   union REGS regs;
   regs.w.flags = 0;
   regs.h.ah = (way == UP) ? UP : DOWN;  /* Scroll direction */
   regs.h.al = nrows;  /* Rows to scroll */
   regs.h.bh = attr;  /* Screen attribute for blank fill */
   regs.h.ch = ty;  regs.h.cl = tx;  /* Top left */
   regs.h.dh = by;  regs.h.dl = bx;  /* Bottom right */
   int386(0x10, &regs, &regs);
#else
   _AH = (way == UP) ? UP : DOWN;  /* Scroll direction */
   _AL = nrows;  /* Rows to scroll */
   _BH = attr;  /* Screen attribute for blank fill */
   _CH = ty; _CL = tx;  /* Top left */
   _DH = by; _DL = bx;  /* Bottom right */
   geninterrupt(0x10);
#endif
   }

extern "C"
void sclear(void)
{
   bios_scroll(UP, 0,0,  scr_lcol,scr_lrow, 0, CHATR);  /* Blank screen */
   }

extern "C"
void w_clear(int tx,int ty, int bx,int by)
{
   bios_scroll(UP, tx,ty,  bx,by, 0, CHATR);  /* Blank window */
   }

extern "C"
void w_roll(int tx,int ty, int bx,int by, int n)
{
   bios_scroll(UP, tx,ty,  bx,by, n, CHATR);  /* Roll window up n lines */
   scpos(tx,by);  /* Leave cursor at start of bottom window line */
   }

#define DOT  0xFA  /* Small dot in centre of char rectangle */

extern "C"
void chart(int tx,int ty, int bx,int by, int x1,int x2,int x3)
{
   int w,k;
#define TIMEWIDTH  8
   char buf[TIMEWIDTH+102], *bar;
   bios_scroll(UP, tx,ty,  bx,by, 1, CHATR);
   w = bx+1-tx - TIMEWIDTH;  /* Plot chars from bar[0] to bar[w-1], 0-org */
   bar = &buf[TIMEWIDTH];
   if (tod_s%30 == 0) {
      sprintf(buf, "%02d%02d:%02d +", tod_h,tod_m,tod_s);
      memset(bar+1,DOT,w-1);
      }
   else {
      memset(buf, ' ',TIMEWIDTH+w);
      bar[0] = '|';
      }
   for (k = 10; k < w; k += 10) bar[k] = ':';
   if (x1 >= w) x1 = w;
   if (x2 >= w) x2 = w;
   if (x3 >= w) x3 = w;
   for (k = x1+1; k < x3; ++k) bar[k] = '-';
   for (k = 10; k < w; k += 10)
      if (bar[k] == '-') bar[k] = '+';
   bar[x1] = '<';
   bar[x3] = '>';
   bar[x2] = '*';
   k = bar[w-1];  bar[w-1] = '\0';
   scpos(tx,by);
   for (bar = buf; *bar; bar++) swrite(*bar);
   sputc(k);  /* Leave cursor at (bx,by) */
   }
