/* xscreensaver, Copyright (c) 1997 Jamie Zawinski <jwz@jwz.org>
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation.  No representations are made about the suitability of this
 * software for any purpose.  It is provided "as is" without express or 
 * implied warranty.
 *
 * Wendy, let me explain something to you.  Whenever you come in here and
 * interrupt me, you're BREAKING my CONCENTRATION.  You're DISTRACTING me!
 * And it will then take me time to get back to where I was. You understand?
 * Now, we're going to make a new rule.  When you come in here and you hear
 * me typing, or whether you DON'T hear me typing, or whatever the FUCK you
 * hear me doing; when I'm in here, it means that I am working, THAT means
 * don't come in!  Now, do you think you can handle that?
 */

#include <ctype.h>
#include "screenhack.h"

char *progclass = "XJack";

char *defaults [] = {
  ".background:		#FFF0B4",
  ".foreground:		#000000",
  "XJack.font:		-*-courier-medium-r-*-*-*-240-*-*-m-*-*-*",
  "*delay:		50000",
  0
};

XrmOptionDescRec options [] = {
  { "-delay",		".delay",	XrmoptionSepArg, 0 },
  { "-font",		".font",	XrmoptionSepArg, 0 },
  { 0, 0, 0, 0 }
};

void
screenhack (Display *dpy, Window window)
{
  static const char *source = "All work and no play makes Jack a dull boy.  ";
  /* If you're here because you're thinking about making the above string be
     customizable, then you don't get the joke.  You loser. */
  const char *s = source;
  int columns, rows;		/* characters */
  int left, right;		/* characters */
  int char_width, line_height;	/* pixels */
  int x, y;			/* characters */
  int mode = 0;
  int hspace = 15;		/* pixels */
  int vspace = 15;		/* pixels */
  Bool break_para = True;
  Bool caps = False;
  int sentences = 0;
  int paras = 0;

  XWindowAttributes xgwa;
  XGCValues gcv;
  GC gc;
  int delay = get_integer_resource ("delay", "Integer");
  char *fontname = get_string_resource ("font", "Font");
  XFontStruct *font = XLoadQueryFont (dpy, fontname);

  if (!font)
    font = XLoadQueryFont (dpy, "-*-*-medium-r-*-*-*-240-*-*-m-*-*-*");
  if (!font)
    font = XLoadQueryFont (dpy, "-*-courier-medium-r-*-*-*-180-*-*-m-*-*-*");
  if (!font)
    font = XLoadQueryFont (dpy, "-*-*-*-r-*-*-*-240-*-*-m-*-*-*");
  if (!font)
    {
      fprintf(stderr, "no big fixed-width font like \"%s\"\n", fontname);
      exit(1);
    }

  XGetWindowAttributes (dpy, window, &xgwa);

  gcv.font = font->fid;
  gcv.foreground = get_pixel_resource ("foreground", "Foreground", dpy,
				       xgwa.colormap);
  gcv.background = get_pixel_resource ("background", "Background", dpy,
				       xgwa.colormap);
  gc = XCreateGC (dpy, window, (GCFont | GCForeground | GCBackground), &gcv);

  char_width = (font->per_char
		? font->per_char['n'-font->min_char_or_byte2].rbearing
		: font->min_bounds.rbearing);
  line_height = font->ascent + font->descent + 1;

  columns = (xgwa.width - hspace - hspace) / char_width;
  rows = (xgwa.height - vspace - vspace) / line_height;

  left = 0xFF & (random() % ((columns / 2)+1));
  right = left + (0xFF & (random() % (columns - left - 10)+10));
  x = 0;
  y = 0;

  while (1)
    {
      int word_length = 0;
      const char *s2;
      for (s2 = s; *s2 && *s2 != ' '; s2++)
	word_length++;

      if (break_para ||
	  (*s != ' ' &&
	   (x + word_length) >= right))
	{
	  x = left;
	  y++;

	  if (break_para)
	    y++;

	  if (mode == 1 || mode == 2)
	    {
	      /* 1 = left margin goes southwest; 2 = southeast */
	      left += (mode == 1 ? 1 : -1);
	      if (left >= right - 10)
		{
		  if ((right < (columns - 10)) && (random() & 1))
		    right += (0xFF & (random() % (columns - right)));
		  else
		    mode = 2;
		}
	      else if (left <= 0)
		{
		  left = 0;
		  mode = 1;
		}
	    }
	  else if (mode == 3 || mode == 4)
	    {
	      /* 3 = right margin goes southeast; 4 = southwest */
	      right += (mode == 3 ? 1 : -1);
	      if (right >= columns)
		{
		  right = columns;
		  mode = 4;
		}
	      else if (right <= left + 10)
		mode = 3;
	    }

	  if (y >= rows)	/* bottom of page */
	    {
	      /* scroll by 1-5 lines */
	      int lines = (random() % 5 ? 0 : (0xFF & (random() % 5))) + 1;
	      if (break_para)
		lines++;

	      /* but sometimes scroll by a whole page */
	      if (0 == (random() % 100))
		lines += rows;

	      while (lines > 0)
		{
                  int i;
                  int inc = line_height / 7;
                  int pix_delay = delay / 1000;
                  if (inc <= 0) inc = 1;
                  for (i = 0; i < line_height; i += inc)
                    {
                      if (i > line_height)
                        i = line_height;
                      XCopyArea (dpy, window, window, gc,
                                 0, inc,
                                 xgwa.width, xgwa.height - inc,
                                 0, 0);
                      XSync (dpy, False);
                      if (pix_delay) usleep (pix_delay);
                    }
		  y--;
		  lines--;

                  /* See? It's OK. He saw it on the television. */
                  XClearArea (dpy, window,
                              0, xgwa.height - vspace - line_height,
                              xgwa.width, line_height + vspace + vspace,
                              False);
                  XSync (dpy, False);

                  XGetWindowAttributes (dpy, window, &xgwa);
                  columns = (xgwa.width - hspace - hspace) / char_width;
                  rows = (xgwa.height - vspace - vspace) / line_height;
                  if (y > rows) y = rows-1;
                  if (x > columns) x = columns-2;

                  if (delay) usleep (delay);
		}
	      if (y < 0) y = 0;
	    }

	  break_para = False;
	}

      if (*s != ' ')
	{
	  char c = *s;
	  int xshift = 0, yshift = 0;
	  if (0 == random() % 50)
	    {
	      xshift = random() % ((char_width / 3) + 1);      /* mis-strike */
	      yshift = random() % ((line_height / 6) + 1);
	      if (0 == (random() % 3))
		yshift *= 2;
	      if (random() & 1)
		xshift = -xshift;
	      if (random() & 1)
		yshift = -yshift;
	    }

	  if (0 == (random() % 250))	/* introduce adjascent-key typo */
	    {
	      static const char * const typo[] = {
		"asqw", "ASQW", "bgvhn", "cxdfv", "dserfcx", "ewsdrf",
		"Jhuikmn", "kjiol,m", "lkop;.,", "mnjk,", "nbhjm", "oiklp09",
		"pol;(-0", "redft54", "sawedxz", "uyhji87", "wqase32",
		"yuhgt67", ".,l;/", 0 };
	      int i = 0;
	      while (typo[i] && typo[i][0] != c)
		i++;
	      if (typo[i])
                c = typo[i][0xFF & ((random() % strlen(typo[i]+1)) + 1)];
	    }

	  /* caps typo */
	  if (c >= 'a' && c <= 'z' && (caps || 0 == (random() % 350)))
	    {
	      c -= ('a'-'A');
	      if (c == 'O' && random() & 1)
		c = '0';
	    }

	OVERSTRIKE:
	  XDrawString (dpy, window, gc,
		       (x * char_width) + hspace + xshift,
		       (y * line_height) + vspace + font->ascent + yshift,
		       &c, 1);
	  if (xshift == 0 && yshift == 0 && (0 == (random() & 3000)))
	    {
	      if (random() & 1)
		xshift--;
	      else
		yshift--;
	      goto OVERSTRIKE;
	    }

	  if ((tolower(c) != tolower(*s))
	      ? (0 == (random() % 10))		/* backup to correct */
	      : (0 == (random() % 400)))	/* fail to advance */
	    {
	      x--;
	      s--;
	      XSync (dpy, False);
	      if (delay) usleep (0xFFFF & (delay + (random() % (delay * 10))));
	    }
	}

      x++;
      s++;

      if (0 == random() % 200)
	{
	  if (random() & 1 && s != source)
	    s--;	/* duplicate character */
	  else if (*s)
	    s++;	/* skip character */
	}

      if (*s == 0)
	{
	  sentences++;
	  caps = (0 == random() % 40);	/* capitalize sentence */

	  if (0 == (random() % 10) ||	/* randomly break paragraph */
	      (mode == 0 &&
	       ((0 == (random() % 10)) || sentences > 20)))
	    {
	      break_para = True;
	      sentences = 0;
	      paras++;

	      if (random() & 1)		/* mode=0 50% of the time */
		mode = 0;
	      else
		mode = (0xFF & (random() % 5));

	      if (0 == (random() % 2))	/* re-pick margins */
		{
		  left = 0xFF & (random() % (columns / 3));
		  right = columns - (0xFF & (random() % (columns / 3)));

		  if (0 == random() % 3)	/* sometimes be wide */
		    right = left + ((right - left) / 2);
		}

	      if (right - left <= 10)	/* introduce sanity */
		{
		  left = 0;
		  right = columns;
		}

	      if (right - left > 50)	/* if wide, shrink and move */
		{
		  left += (0xFF & (random() % ((columns - 50) + 1)));
		  right = left + (0xFF & ((random() % 40) + 10));
		}

	      /* oh, gag. */
	      if (mode == 0 &&
		  right - left < 25 &&
		  columns > 40)
		{
		  right += 20;
		  if (right > columns)
		    left -= (right - columns);
		}
	    }
	  s = source;
	}

      XSync (dpy, False);
      if (delay)
	{
	  usleep (delay);
	  if (0 == random() % 3)
	    usleep(0xFFFFFF & ((random() % (delay * 5)) + 1));

	  if (break_para)
	    usleep(0xFFFFFF & ((random() % (delay * 15)) + 1));
	}

      if (paras > 5 &&
	  (0 == (random() % 1000)) &&
	  y < rows-5)
	{
	  int i;
	  int n = random() & 3;
	  y++;
	  for (i = 0; i < n; i++)
	    {
	      /* See also http://catalog.com/hopkins/unix-haters/login.html */
	      const char *n1 =
		"NFS server overlook not responding, still trying...";
	      const char *n2 = "NFS server overlook ok.";
	      while (*n1)
		{
		  XDrawString (dpy, window, gc,
			       (x * char_width) + hspace,
			       (y * line_height) + vspace + font->ascent,
			       n1, 1);
		  x++;
		  if (x >= columns) x = 0, y++;
		  n1++;
		}
	      XSync (dpy, False);
	      usleep (5000000);
	      while (*n2)
		{
		  XDrawString (dpy, window, gc,
			       (x * char_width) + hspace,
			       (y * line_height) + vspace + font->ascent,
			       n2, 1);
		  x++;
		  if (x >= columns) x = 0, y++;
		  n2++;
		}
	      y++;
	      XSync (dpy, False);
	      usleep (500000);
	    }
	}
      screenhack_handle_events (dpy);
    }
}
