/* pppmon/src/xpppmon.c
 *
 * Description:
 * Connect to the pppmon server, display statistics with a simple
 * curve (solid or a line).
 *
 * This software is in the public domain.
 *
 * Please send all bug reports, requests for information, etc. to:
 *   Calle Karlsson
 *   KaSH Software AB
 *   calle@kash.se
 *
 * History:
 * 950525 ckn Initial coding - version 0.1
 * EndHistory.
 */

#include <stdio.h>
#include "pppmon.h"
#include <ctype.h>
#include <xview/xview.h>
#include <xview/frame.h>
#include <xview/canvas.h>
#include <xview/xv_xrect.h>
#include <xview/cms.h>

static Notify_value CheckPPP (void);   
static void CanvasRepaint (Canvas canvas, Xv_Window PaintWindow, Display * dpy,
                           Window xwin, Xv_xrectlist * xRects);
static int ConnectTo (char * HostName, char * Service, char * Protocol);


/* I really hate globals.. */

static DclPPPMonitorMsg PMP [1];

static int
   SolidCurve  =     0,
   MaxCPS      =  1800, /* This value is somewhat odd */
   CanvasWidth,
   CanvasHeight,
   FontSize;

static Canvas
   canvas;

static Display
   * dpy;

static Window
   CanvasWindow;

static Attr_attribute
   INSTANCE;

static XFontStruct
   * FontStruct;

unsigned long
   * PixelTable;

int
   ListenFD;

#define MinCanvasHeight  54
#define MaxCanvasHeight 108
#define MinCanvasWidth   65
#define MaxCanvasWidth  295

/* Some colors that might come in handy */
#define WHITE             0
#define RED               1
#define GREEN             2
#define BLUE              3
#define GRAY              4
#define BLACK             5
#define NCOLORS           6


int
main (int argc, char ** argv)
{
   Frame frame;
   char * ip = NULL, ** FontNames, Buffer [64], * HostName = "localhost";
   int nFonts, fd;
   GC gc;
   Cms cms;
   static Xv_singlecolor colors [NCOLORS] = {
      { 255, 255, 255 }, /* white */
      { 255,   0,   0 }, /* red   */
      {   0, 255,   0 }, /* green */
      {   0,   0, 255 }, /* blue  */
      { 160, 160, 160 }, /* gray  */
      {   0,   0,   0 }  /* black */
   };
   
   xv_init (XV_INIT_ARGC_PTR_ARGV, & argc, argv, NULL);
   
   cms = xv_create (NULL, CMS,
                    CMS_SIZE, NCOLORS,
                    CMS_COLORS, colors,
                    NULL);

   PixelTable = (unsigned long *) xv_get (cms, CMS_INDEX_TABLE);
   
   for (argc --, argv ++; argc > 0; argc --, argv ++)
   {
      if (! strcasecmp (argv [0], "-solid"))
         SolidCurve = 1;
      else if (! strcasecmp (argv [0], "-host"))
      {
         if (argc < 1)
         {
            printf ("Missing hostname\n");
            exit (1);
         }
         HostName = argv [1];
         argc --, argv ++;
      }
      else if (! strcasecmp (argv [0], "-bps"))
      {
         if (argc < 1)
         {
            printf ("Missing bps argument\n");
            exit (1);
         }
         MaxCPS = atoi (argv [1]) / 8;
         argc --, argv ++;
      }
      else if (! strcasecmp (argv [0], "-h"))
      {
         printf ("Usage: xmlmon [options] where options is one or more of:\n");
         printf (" -bps <bps>      Give baudrate of modem (default 14400)\n");
         printf (" -solid          Draw a solid curve\n");
         printf (" -help           Display generic XView help\n");
         printf (" -h              You already know about this option\n");
         printf (" -host <host>    Connect to <hostname> for statistics\n");
         exit (1);
      }
      else
      {
         printf ("Bad argument (%s)\n", argv [0]);
         exit (0);
      }
   }
         
   CanvasWidth  = MinCanvasWidth;
   CanvasHeight = MinCanvasHeight;

   /* This isn't used yet */
   INSTANCE = xv_unique_key ();
   
   frame = (Frame) xv_create (NULL, FRAME,
                              XV_KEY_DATA, INSTANCE, ip,
                              XV_WIDTH, CanvasWidth,
                              XV_LABEL, "PPP Monitor",
                              FRAME_CLOSED, FALSE,
                              FRAME_SHOW_HEADER, FALSE,
                              FRAME_SHOW_FOOTER, FALSE,
                              FRAME_SHOW_RESIZE_CORNER, FALSE,
                              NULL);

   dpy = (Display *) xv_get (frame, XV_DISPLAY);
   gc  = DefaultGC (dpy, DefaultScreen (dpy));
   
   strcpy (Buffer, "*courier-medium-r-*-*-10-*");
   FontNames = XListFonts (dpy, Buffer, 1, & nFonts);
      
   if (nFonts < 1)
   {
      fprintf (stderr, "XListFonts ");
      perror (Buffer);
      exit (1);
   }

   if (! (FontStruct = XLoadQueryFont (dpy, FontNames [0])))
   {
      perror ("XLoadQueryFont");
      exit (1);
   }
   
   XFreeFontNames (FontNames);

   XSetFont (dpy, gc, FontStruct->fid);

   FontSize = FontStruct->ascent + FontStruct->descent;

   canvas = (Canvas) xv_create (frame, CANVAS,
                                XV_KEY_DATA, INSTANCE, ip,
                                WIN_CMS, cms,
                                XV_X, 0,
                                XV_Y, 0,
                                XV_WIDTH, CanvasWidth,
                                XV_HEIGHT, CanvasHeight+FontSize,
                                CANVAS_X_PAINT_WINDOW, TRUE,
                                CANVAS_REPAINT_PROC, CanvasRepaint,
                                NULL);
   xv_set (canvas_paint_window (canvas), XV_KEY_DATA, INSTANCE, ip, NULL);

   CanvasWindow = (Window) xv_get (canvas_paint_window (canvas), XV_XID);

   window_fit_height (frame);

   if ((ListenFD = ConnectTo (HostName, "pppmon", "tcp")) < 0)
   {
      printf ("Couldn't connect to %s\n", HostName);
      exit (1);
   }

   notify_set_input_func (frame, CheckPPP, ListenFD);

   xv_main_loop (frame);
   return 0;
}


/* Called when we get a message from our statistics provider */

static Notify_value
CheckPPP (void)
{
   struct tm * tm;
   time_t StartTime;
   
   if (read (ListenFD, PMP, sizeof (PMP)) == sizeof (PMP))
   {
      PMP->UpTime    = ntohl (PMP->UpTime);
      PMP->RBytes    = ntohl (PMP->RBytes);
      PMP->SBytes    = ntohl (PMP->SBytes);
      PMP->TimeSpent = ntohl (PMP->TimeSpent);
      PMP->RTT       = ntohl (PMP->RTT); /* NIY */

      if (PMP->UpTime)
      {
         StartTime = time (0) - PMP->UpTime;
         tm = localtime (& StartTime);
      }

      CanvasRepaint (canvas, 0, dpy, CanvasWindow, NULL);
   }

   return 0;
}


/* Repaint procedure */

static void
CanvasRepaint (Canvas thisCanvas, Xv_Window PaintWindow, Display * thisDpy,
               Window xwin, Xv_xrectlist * xRects)
{
   static int LastY = -99;
   static int LastUsage = 0, LastScale = 20, LastWasDown = 0;
   int X, NewY, LatestCPS, NewUsage;
   GC gc = DefaultGC (thisDpy, DefaultScreen (thisDpy));
   char Buffer [32];

   if (! PMP->UpTime)
   {
      if (LastWasDown)
         return;

      LastWasDown = 1;
      sprintf (Buffer, "Link DOWN  ");
      XDrawImageString (thisDpy, xwin, gc,
                        2, CanvasHeight+FontSize-FontStruct->descent,
                        Buffer, strlen (Buffer));
      XClearArea (thisDpy, xwin, 1, 0, CanvasWidth-5, CanvasHeight-1, 0);
      return;
   }
   
   LastWasDown = 0;

   if (LastY == -99)
   {
      LastY = CanvasHeight;
      XSetGraphicsExposures (dpy, gc, 0);
   }
   
   NewUsage = (PMP->RBytes+PMP->SBytes) * 1000 / PMP->TimeSpent;

   X    = CanvasWidth-7;
   NewY = CanvasHeight - NewUsage * CanvasHeight / (float) MaxCPS;

   XCopyArea (thisDpy, xwin, xwin, gc, 1, 0, X+2, CanvasHeight, 0, 0);

   if (SolidCurve)
   {
      XSetForeground (thisDpy, gc, PixelTable [GRAY]);
      XDrawLine (thisDpy, xwin, gc, X+1, CanvasHeight, X+1, NewY);
      XSetForeground (thisDpy, gc, PixelTable [BLACK]);
   }
   else
      XDrawLine (thisDpy, xwin, gc, X, LastY, X+1, NewY);
   
   LastY     = NewY;
   LastUsage = NewUsage;

   if (PMP->UpTime > 3600)
      sprintf (Buffer, "UP %2dh%2dm",
               PMP->UpTime / 3600, (PMP->UpTime % 3600) / 60);
   else if (PMP->UpTime > 59)
      sprintf (Buffer, "UP %2dm%2ds", PMP->UpTime / 60, PMP->UpTime % 60);
   else
      sprintf (Buffer, "UP %2ds   ", PMP->UpTime);
   
   XDrawImageString (thisDpy, xwin, gc, 
                     2, CanvasHeight+FontSize-FontStruct->descent,
                     Buffer, strlen (Buffer));
   XDrawLine (thisDpy, xwin, gc,
              0, CanvasHeight, CanvasWidth-4, CanvasHeight);
}

/* Connect to our statistics provider */

static int
ConnectTo (char * HostName, char * Service, char * Protocol)
{
   struct servent * sp;
   struct hostent * hp;
   struct sockaddr_in Addr [1];
   int fd, Port;
   
   if (! (sp = getservbyname (Service, Protocol)))
   {
      perror ("getservbyname");
      Port = 5676; /* Any port will do as long as it's the same as pppmon.c */
   }
   else
      Port = sp->s_port;

   if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0)
   {
      perror ("socket");
      return -1;
   }

   if (hp = gethostbyname (HostName))
      memcpy (& Addr->sin_addr, hp->h_addr, hp->h_length);
   else
   {
      unsigned long f;
      
      if ((f = inet_addr (HostName)) < 0)
      {
         printf ("Unknown host (%s)\n", HostName);
         return(-1);
      }
      memcpy (& Addr->sin_addr, & f, sizeof (f));
   }
   Addr->sin_family = AF_INET;
   Addr->sin_port   = ntohs (Port);
   
   if (connect (fd, (struct sockaddr *) Addr, sizeof (Addr)) < 0)
   {
      perror ("connect");
      return (-1);
   }

   return fd;
}
