/* Displays the current national debt in a window, updated once a second.
   Public domain.  By Jamie Zawinski <jwz@lucid.com>.
   Inspired by an Amiga program by Ron Charlton <charlton@cs.utk.edu>.
   Recent debt and population numbers from sonroc@nemesis.zycad.com.
   cc -O -o xdebt xdebt.c -lXaw -lXmu -lXt -lX11
 */

#include <stdio.h>
#include <X11/Xos.h>
#include <X11/Intrinsic.h>
#include <X11/Xaw/Label.h>

#ifdef SYSV
#define timelocal mktime
#endif

/* These types are floats because the interesting values are >42 bits. */
typedef double bignum;
typedef bignum debt_t;
typedef bignum pop_t;

/* The US National Debt, and its rate of increase as of December 31, 1988
   (in dollars per second) according to the 1989 Survey of Current Business.

#define DEBT		2707284000000.0
#define DEBT_DELTA	7673.015
static struct tm debt_tm = { 0, 0, 0, 31, 11, 88 };
 */

/* The US National Debt, and its rate of increase as of June 30, 1992
   (in $/second) based on numbers from the 5 July 1992 Albuquerque Journal
   ... thanks to Brooke King brooke@fuchsia.albuq.ingr.com
 */
#define DEBT		3965170506502.395
#define DEBT_DELTA	14132.887
static struct tm debt_tm = { 0, 0, 0, 30, 5, 92 };

/* The US Population, and its rate of increase, as of July 1, 1990
   (in people/second) based on numbers from the CIA's World Factbook 1990
   ... thanks to tom@genie.slhs.udel.edu
 */
#define POP		250410000.0
#define POP_DELTA	0.0714151266
static struct tm pop_tm = { 0, 0, 0, 1, 6, 90 };


debt_t
national_debt_at (now)
     time_t now;
{
  time_t debt_date = timelocal (&debt_tm);
  time_t seconds_since_then = now - debt_date;
  debt_t delta_since_then = DEBT_DELTA * seconds_since_then;
  return DEBT + delta_since_then;
}

pop_t
population_at (now)
     time_t now;
{
  time_t pop_date = timelocal (&pop_tm);
  time_t seconds_since_then = now - pop_date;
  pop_t delta_since_then = POP_DELTA * seconds_since_then;
  return POP + delta_since_then;
}

debt_t 
per_capita_at (now)
     time_t now;
{
  return (national_debt_at (now) / population_at (now));
}

extern char *index ();

void
bignum_to_string (debt, s)
     bignum debt;
     char *s;
{
  int L, i = 0;
  char buf [255];
  char *b = buf;
  sprintf (b, "%lf", debt);
  b = index (b, '.') - 1;
  L = b - buf;
  s += L + (L / 3);
  *(s+1) = 0;
  i = -1;
  while (1)
    {
      if (++i == 3) i = 0, *s-- = ',';
      if (b < buf) return;
      *s-- = *b--;
    }
}


enum mode_t { debt_mode, pop_mode, cap_mode } mode;

static int update;

static char *defaults[] = {
  "*Label.font:	*-times-bold-r-*-*-*-240-*-*-*-*-*-*",
  "*mode:	debt",
  "*update:	1",
  NULL
};

static XrmOptionDescRec options [] = {
  { "-update",		"*update",	XrmoptionSepArg, 0 },
  { "-mode",		"*mode",	XrmoptionSepArg, 0 },
  { "-debt",		"*mode",	XrmoptionNoArg, "debt" },
  { "-population",	"*mode",	XrmoptionNoArg, "population" },
  { "-per-capita",	"*mode",	XrmoptionNoArg, "per-capita" }
};


static void
get_resources (dpy)	/* Easier than making a subclass... */
     Display *dpy;
{
  char *name, *class, *type, buf1[255], buf2[255];
  XrmValue value;
  XtGetApplicationNameAndClass (dpy, &name, &class);
  sprintf (buf1, "%s.update", name);
  sprintf (buf2, "%s.Update", class);
  XrmGetResource (XtDatabase (dpy), buf1, buf2, &type, &value);
  if (sscanf (value.addr, " %d ", &update) == 0 || update < 1)
    {
      fprintf (stderr, "%s: update must be a positive integer, not \"%s\"\n",
	       name, value.addr);
      update = 1;
    }
  sprintf (buf1, "%s.mode", name);
  sprintf (buf2, "%s.Mode", class);
  mode = debt_mode;
  XrmGetResource (XtDatabase (dpy), buf1, buf2, &type, &value);
  if      (!strcmp (value.addr, "debt")) mode = debt_mode;
  else if (!strcmp (value.addr, "population")) mode = pop_mode;
  else if (!strcmp (value.addr, "per-capita")) mode = cap_mode;
  else
    fprintf (stderr,
  "%s: mode must be \"debt\", \"population\", or \"per-capita\", not \"%s\"\n",
	     name, value.addr);
}


static void
relabel_window (shell)
     Widget shell;
{
  /* If the user hasn't specified the -title option, set the window title. */
  char buf1 [100], buf2 [100], *s;
  XrmValue value;
  char *type;
  XrmDatabase db = XtDatabase (XtDisplay (shell));
  char *name, *class;
  XtGetApplicationNameAndClass (XtDisplay (shell), &name, &class);
  sprintf (buf1, "%s.title", name);
  sprintf (buf2, "%s.Title", class);
  if (XrmGetResource (db, buf1, buf2, &type, &value)) return;
  switch (mode) {
  case debt_mode: s = "Current U.S. National Debt";		break;
  case cap_mode:  s = "Current U.S. National Debt, Per Capita";	break;
  case pop_mode:  s = "Current U.S. Population";		break;
  default:   abort ();
  }
  XStoreName (XtDisplay (shell), XtWindow (shell), s);
}


static void
timer (w, id)
     Widget w;
     XtIntervalId id;
{
  char buf [255], *b = buf;
  Arg av [10];
  int ac = 0;
  time_t now = time ((time_t *) 0);
  bignum num;
  buf [0] = '$';
  switch (mode) {
  case debt_mode: num = national_debt_at (now), b++;	break;
  case pop_mode:  num = population_at (now);		break;
  case cap_mode:  num = per_capita_at (now), b++;	break;
  default:   abort ();
  }
  bignum_to_string (num, b);
  XtSetArg (av [ac], XtNlabel, buf); ac++;
  XtSetValues (w, av, ac);
  XtAppAddTimeOut (XtWidgetToApplicationContext (w), update * 1000, timer, w);
}

void
main (argc, argv)
     int argc;
     char **argv;
{
  XtAppContext app;
  Widget shell = XtAppInitialize (&app, "XDebt", options, XtNumber (options),
				  &argc, argv, defaults, NULL, 0);
  Widget label;
  if (argc > 1)
    {
      fprintf (stderr, "%s: unknown option %s\n", argv[0], argv[1]);
      fprintf (stderr, "options: -update <seconds>\n\
	 -font <font>\n\
	 -mode [ debt | per-capita | population ]\n");
      exit (-1);
    }
  get_resources (XtDisplay (shell));
  label = XtCreateManagedWidget ("label", labelWidgetClass, shell, NULL, 0);
  timer (label, 0);
  XtRealizeWidget (shell);
  relabel_window (shell);
  XtAppMainLoop (app);
}
