/*************************************************
*                      Smailmon                  *
*************************************************/

/* Copyright (c) University of Cambridge 1995. */

/* P. Hazel, February 1995*/
/* Last Modified: March 1995 */

#include "smailmon.h"

/* This module contains the main program of smailmon, which
sets up the world and then lets the XtAppMainLoop function
run things off X events. */


/*************************************************
*               Static variables                 *
*************************************************/

/* Fallback resources */

static String fallback_resources[] = {"smailmon.geometry: +150+0", NULL};

/* X11 fixed argument lists */

static Arg quit_args[] = {
  {XtNfromVert, (XtArgVal) NULL},         /* must be first */
  {XtNlabel,    (XtArgVal) " Quit "},
  {"left",      XawChainLeft},
  {"right",     XawChainLeft},
  {"top",       XawChainTop},
  {"bottom",    XawChainTop}
};

static Arg update_args[] = {
  {XtNfromVert, (XtArgVal) NULL},         /* must be first */
  {XtNlabel,    (XtArgVal) " Update "},
  {"left",      XawChainLeft},
  {"right",     XawChainLeft},
  {"top",       XawChainTop},
  {"bottom",    XawChainTop}
};

static Arg log_args[] = {
  {XtNfromVert, (XtArgVal) NULL},         /* must be first */
  {"editType",  XawtextEdit},
  {"useStringInPlace", (XtArgVal)TRUE},
  {"string",    (XtArgVal)""},            /* dummy to get it going */
  {"scrollVertical", XawtextScrollAlways},
  {"top",       XawChainTop},
  {"bottom",    XawChainTop}
};

static Arg queue_args[] = {
  {XtNfromVert, (XtArgVal) NULL},         /* must be first */
  {"editType",  XawtextEdit},
  {"string",    (XtArgVal)""},            /* dummy to get it going */
  {"scrollVertical", XawtextScrollAlways},
  {"top",       XawChainTop},
  {"bottom",    XawChainBottom}
};

static Arg get_size_args[] = {
  {"width",     (XtArgVal)0},
  {"height",    (XtArgVal)0}
};

XtActionsRec menu_action_table[] = {
  { "menu-create",  menu_create } };

/* Miscellaneous local variables */

static int tick_queue_accumulator = 999999;
static int tick_stripchart_accumulator = 999999;
static int tick_interval = 2;



/*************************************************
*               Debug print routine              *
*************************************************/

void debug(char *format, ...)
{
va_list ap;
va_start(ap, format);

if (DEBUG == NULL)
  {
  if ((DEBUG = fopen("DEBUG", "w")) == NULL)
    {
    printf("Failed to open DEBUG\n");
    exit(99);
    }
  else printf("Opened debugging file DEBUG\n");
  }

vfprintf(DEBUG, format, ap);
va_end(ap);
}


/*************************************************
*             Callback routines                  *
*************************************************/


void quitAction(Widget w, XtPointer client_data, XtPointer call_data)
{
exit(0);
}

void updateAction(Widget w, XtPointer client_data, XtPointer call_data)
{
scan_spool_input(TRUE);
queue_display();
tick_queue_accumulator = 0;
}






/*************************************************
*              Regexp error handler              *
*************************************************/

/* The regexp routines require an error handler. The function
doesn't identify where the error happened, but since we
compile all the expressions at the start, we can detect whether
it's a compilation error or a runtime error. Give a message for
the former and collapse, but ignore the latter. We maintain a
pointer to regexps in the process of compilation so as to be
able to say which one it was that failed. */

void regerror(char *s)
{
if (!smailmon_initialized)
  {
  printf("*** Error in regular expression: %s\n", s);
  if (regexp_compiling != NULL)
    printf("*** Failed expression: %s\n", regexp_compiling);
  exit(99);
  }
}



/*************************************************
*              Ticker function                   *
*************************************************/

/* This function is called initially to set up the starting data
values; it then sets a timeout so that it continues to be called
every 2 seconds. */

static void ticker(XtPointer p, XtIntervalId *i)
{
tick_queue_accumulator += tick_interval;
tick_stripchart_accumulator += tick_interval;
read_log();

/* If we have passed the queue update time, we must do a full
scan of the queue, checking for new arrivals, etc. This will
as a by-product set the count of items for use by the stripchart
display. */

if (tick_queue_accumulator >= queue_update)
  {
  scan_spool_input(TRUE);
  queue_display();
  tick_queue_accumulator = 0;
  if (tick_stripchart_accumulator >= stripchart_update)
    tick_stripchart_accumulator = 0;
  }

/* Otherwise, if we have exceeded the stripchart interval,
do a reduced queue scan that simply provides the count for
the stripchart. */

else if (tick_stripchart_accumulator >= stripchart_update)
  {
  scan_spool_input(FALSE);
  tick_stripchart_accumulator = 0;
  }



/*******
{
XawTextPosition begin, end;

XawTextGetSelectionPos(queue_widget, &begin, &end);
if (begin != end)
  {
  printf("queue selection %d %d\n", begin, end);
  }

XawTextGetSelectionPos(log_widget, &begin, &end);
if (begin != end)
  {
  printf("log selection %d %d\n", begin, end);
  }
}
**********/



/* Reset the timer for next time */

XtAppAddTimeOut(X_appcon, tick_interval * 1000, ticker, 0);
}



void action_hook(Widget w, XtPointer client_data, String name,
  XEvent *event, String *string, Cardinal *n)
{
printf("action hook, name = %s Widget=%d queue_widget=%d\n",
  name, w, queue_widget);
}





/*************************************************
*               Initialize                       *
*************************************************/

int main(int argc, char **argv)
{
int i;
struct stat statdata;
Widget outer_form_widget,
       stripchart_form_widget,
       update_widget,
       quit_widget;

/* Set up the version string and date and output them */

version_init();
printf("\nSmailmon version %s (compiled %s) initializing\n",
  version_string, version_date);

/* Get the large buffer from free store */

queue_buffer = (char *)malloc(queue_buffer_size);

/* Initialize various things from the environment and arguments. */

init(argc, argv);

/* Get the buffer for storing the string for the log display. */

log_display_buffer = (char *)malloc(log_buffer_size);
log_display_buffer[0] = 0;

/* Initialize the data structures for the stripcharts */

stripchart_init();

/* Open the log file and position to the end of it. Save its inode
so that we can detect when the file is switched to another one. */

LOG = fopen(log_file, "r");
if (LOG == NULL)
  {
  printf("*** Can't open log file\n");
  exit(99);
  }

fseek(LOG, 0, SEEK_END);
log_position = ftell(LOG);
stat(log_file, &statdata);
log_inode = statdata.st_ino;


/* Now initialize the X world and create the top-level widget */

toplevel_widget = XtAppInitialize(&X_appcon, "Smailmon", NULL, 0, &argc, argv,
  fallback_resources, NULL, 0);
X_display = XtDisplay(toplevel_widget);
xs_SetValues(toplevel_widget, 4,
  "title",     window_title,
  "iconName", "smailmon",
  "minWidth",  103,
  "minHeight", 162);


/* Create the action for setting up the menu in the queue display
window, and register the action for positioning the menu. */

XtAppAddActions(X_appcon, menu_action_table, 1);
XawSimpleMenuAddGlobalActions(X_appcon);

/* Set up translation tables for the text widgets we use. We don't
want all the generality of editing, etc. that the defaults provide.
This cannot be done before initializing X - the parser complains
about unknown events, modifiers, etc. in an unhelpful way... The
queue text widget has a different table which includes the button
for popping up the menu. Note that the order of things in these
tables is significant. Shift<thing> must come before <thing> as
otherwise it isn't noticed. */

/* Do we want these ?
  <Key>R: search(backward)
   Ctrl<Key>S: search(forward)
*/

queue_trans = XtParseTranslationTable(
  "Shift<Btn1Down>:menu-create() XawPositionSimpleMenu(menu) MenuPopup(menu)\n\
   <Btn1Down>:     select-start()\n\
   <Btn1Motion>:   extend-adjust()\n\
   <Btn1Up>:       extend-end(PRIMARY,CUT_BUFFER0)\n\
   <Btn3Down>:     extend-start()\n\
   <Btn3Motion>:   extend-adjust()\n\
   <Btn3Up>:       extend-end(PRIMARY,CUT_BUFFER0)\n\
   <Key>Up:        scroll-one-line-down()\n\
   <Key>Down:      scroll-one-line-up()\n\
  ");

text_trans = XtParseTranslationTable(
  "<Btn1Down>:     select-start()\n\
   <Btn1Motion>:   extend-adjust()\n\
   <Btn1Up>:       extend-end(PRIMARY,CUT_BUFFER0)\n\
   <Btn3Down>:     extend-start()\n\
   <Btn3Motion>:   extend-adjust()\n\
   <Btn3Up>:       extend-end(PRIMARY,CUT_BUFFER0)\n\
   <Key>Up:        scroll-one-line-down()\n\
   <Key>Down:      scroll-one-line-up()\n\
  ");


/* Create a toplevel form widget to hold all the other things */

outer_form_widget = XtCreateManagedWidget("form", formWidgetClass,
  toplevel_widget, NULL, 0);

/* Now create an inner form to hold the stripcharts */

stripchart_form_widget = XtCreateManagedWidget("form", formWidgetClass,
  outer_form_widget, NULL, 0);
xs_SetValues(stripchart_form_widget, 5,
  "defaultDistance", 8,
  "left",            XawChainLeft,
  "right",           XawChainLeft,
  "top",             XawChainTop,
  "bottom",          XawChainTop);

/* Create the queue count stripchart and its label. */

create_stripchart(stripchart_form_widget, "queue");

/* Now create the configured input/output stripcharts; note
the total number includes the queue stripchart. */

for (i = 1; i < stripchart_number; i++)
  create_stripchart(stripchart_form_widget, stripchart_title[i]);

/* Next in vertical order comes the Quit button */

quit_args[0].value = (XtArgVal) stripchart_form_widget;
quit_widget = XtCreateManagedWidget("quit", commandWidgetClass,
  outer_form_widget, quit_args, XtNumber(quit_args));
XtAddCallback(quit_widget, "callback",  quitAction, NULL);

/* Create an Ascii text widget for the log tail display. */

log_args[0].value = (XtArgVal) quit_widget;
log_widget = XtCreateManagedWidget("log", asciiTextWidgetClass,
  outer_form_widget, log_args, XtNumber(log_args));
XawTextDisplayCaret(log_widget, FALSE);
xs_SetValues(log_widget, 5,
  "translations", text_trans,
  "string",    log_display_buffer,
  "length",    log_buffer_size,
  "height",    log_depth,
  "width",     log_width);

if (log_font != NULL)
  {
  XFontStruct *f = XLoadQueryFont(X_display, log_font);
  if (f != NULL) xs_SetValues(log_widget, 1, "font", f);
  }


/* The update button */

update_args[0].value = (XtArgVal) log_widget;
update_widget = XtCreateManagedWidget("update", commandWidgetClass,
  outer_form_widget, update_args, XtNumber(update_args));
XtAddCallback(update_widget, "callback",  updateAction, NULL);


/* Create an Ascii text widget for the queue display. */

queue_args[0].value = (XtArgVal) update_widget;
queue_widget = XtCreateManagedWidget("queue", asciiTextWidgetClass,
  outer_form_widget, queue_args, XtNumber(queue_args));
XawTextDisplayCaret(queue_widget, FALSE);

xs_SetValues(queue_widget, 3,
  "height",    queue_depth,
  "width",     queue_width,
  "translations", queue_trans);

/*
XtAppAddActionHook(X_appcon, action_hook, NULL);
*/

if (queue_font != NULL)
  {
  XFontStruct *f = XLoadQueryFont(X_display, queue_font);
  if (f != NULL) xs_SetValues(queue_widget, 1, "font", f);
  }

/* Call the ticker function to get the initial data set up. It
arranges to have itself recalled every 2 seconds. */

ticker(NULL, NULL);

/* Everything is now set up; this flag is used by the regerror
function and also by the queue reader. */

smailmon_initialized = TRUE;
printf("\nSmailmon running\n");

/* Realize the toplevel and thereby get things displayed */

XtRealizeWidget(toplevel_widget);

/* Find out the size of the initial window, and set that as its
maximum. */

XtGetValues(toplevel_widget, get_size_args, 2);
xs_SetValues(toplevel_widget, 2,
  "maxWidth",  get_size_args[0].value,
  "maxHeight", get_size_args[1].value);

/* Enter the application loop which handles things from here
onwards. The return statement is never obeyed, but is needed to
keep pedantic ANSI compilers happy. */

XtAppMainLoop(X_appcon);

return 0;
}

/* End of main.c */
