/* Magnetic poetry xscreensaver hack.
   Insert copyright notice.
   Written by Adam Sampson, largely copy-and-pasted from xjack. */

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

#define XPADDING 8
#define YPADDING 4

char *progclass = "Magnetic";

char *defaults [] = {
  ".background: white",
  ".foreground: black",
  "Magnetic.font: -*-times-medium-r-*-*-*-180-*-*-*-*-*-*",
  0
};

XrmOptionDescRec options [] = {
  { 0, 0, 0, 0 }
};

typedef struct tile {
  const char *text;
  int len;
  short twidth;
  short x;
  short y;
  short w;
  short h;
  int being_moved;
} tile;

GC gc;
XWindowAttributes xgwa;
XFontStruct *font;
int font_ascent, font_descent;

tile **tiles;
int ntiles;

void *
evil_malloc (size_t size)
{
  void *m = malloc (size);
  if (!m)
    {
      fprintf (stderr, "out of memory\n");
      exit (1);
    }
  return m;
}

void
draw_tile (Display *dpy, Window window, int i)
{
  short x = tiles[i]->x, y = tiles[i]->y;
  short tw = tiles[i]->w - 1, th = tiles[i]->h - 1;
  XPoint pts[8];

  pts[0].x = x;
  pts[0].y = y + th;
  pts[1].x = x;
  pts[1].y = y;
  pts[2].x = x + tw;
  pts[2].y = y;
  pts[3].x = x + tw;
  pts[3].y = y + th;
  pts[4].x = x;
  pts[4].y = y + th;
  pts[5].x = x + 1;
  pts[5].y = y + th + 1;
  pts[6].x = x + tw + 1;
  pts[6].y = y + th + 1;
  pts[7].x = x + tw + 1;
  pts[7].y = y + 1;
  XDrawLines (dpy, window, gc, pts, 8, CoordModeOrigin);

  XDrawString (dpy, window, gc,
	       tiles[i]->x + XPADDING,
	       tiles[i]->y + YPADDING + font_ascent + font_descent,
	       tiles[i]->text, tiles[i]->len);
}

void
draw_tiles (Display *dpy, Window window)
{
  int i;

  XClearWindow (dpy, window);
  for (i = 0; i < ntiles; i++)
    {
      draw_tile (dpy, window, i);
    }
  XSync (dpy, False);
}

int
collides (int al, int ar, int bl, int br, int fuzz)
{
  al -= fuzz;
  ar += fuzz;
  bl -= fuzz;
  br += fuzz;

  return (al >= bl && al <= br)
    || (ar >= bl && ar <= br)
    || (al <= bl && ar >= br);
}

int
collide (tile *a, tile *b)
{
  return collides (a->x, a->x + a->w, b->x, b->x + b->w, 0)
    && collides (a->y, a->y + a->h, b->y, b->y + b->h, 0);
}

void
move_tile_to_gap (int n)
{
  int i, found_collisions;
  do
    {
      tiles[n]->x = random() % (xgwa.width - tiles[n]->w);
      tiles[n]->y = random() % (xgwa.height - tiles[n]->h);
      found_collisions = 0;
      for (i = 0; i < ntiles; i++)
	{
	  if (i != n && collide (tiles[n], tiles[i])) found_collisions = 1;
	}
    }
  while (found_collisions);
}

void
move_tile (Display *dpy, Window window, int n, short x, short y)
{
  int i;

  draw_tile (dpy, window, n);

  tiles[n]->x = x;
  tiles[n]->y = y;
  for (i = 0; i < ntiles; i++)
    {
      if (i != n && collide (tiles[n], tiles[i]))
	{
	  draw_tile (dpy, window, i);
	  move_tile_to_gap (i);
	  draw_tile (dpy, window, i);
	}
    }

  draw_tile (dpy, window, n);
}

void
randomise_tiles (Display *dpy, Window window)
{
  int i;
  XClearWindow (dpy, window);
  for (i = 0; i < ntiles; i++) move_tile_to_gap (i);
  draw_tiles (dpy, window);
}

int
random_sign (int range)
{
  if (range == 0) return 0;
  return (random() % (range * 2)) - range;
}

void
yield (Display *dpy, int time)
{
  usleep (time);
  screenhack_handle_events (dpy);
}

void
screenhack (Display *dpy, Window window)
{
  XGCValues gcv;

  char *tile_names = strdup ("they were married in the old churchyard and they promised to be true to each other no matter how hard their lives might be but like meteors that fell through moments parallel they were soon to cross and on different plots of earth they both did fall though their lives had really not been hard at all oh my what a shame no-one's to blame it just happened that way and there's nothing you can say when two people say goodbye" /*" two brothers promised they'd return when the war that they were fighting was over but only one lived to keep his vow and the story I was told said he lived to be quite old before time ran out someone asked if he knew what they'd both fought for but he could not recall he'd ever been to war"*/);
  char *fontname = get_string_resource ("font", "Font");
  char *p;
  int i;
  int dir;
  XCharStruct cs;

  font = XLoadQueryFont (dpy, fontname);
  if (!font)
    {
      fprintf (stderr, "font \"%s\" not found\n", fontname);
      exit (1);
    }

  XTextExtents (font, "X", 1, &dir, &font_ascent, &font_descent, &cs);

  XGetWindowAttributes (dpy, window, &xgwa);
  gcv.function = GXinvert;
  gcv.line_width = 1;
  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,
		  (GCFunction | GCLineWidth | GCFont | GCForeground | GCBackground),
		  &gcv);

  ntiles = 0;
  for (p = tile_names; *p != '\0'; p++)
    {
      if (*p == ' ')
	{
	  *p = '\0';
	  ntiles++;
	}
    }
  tiles = evil_malloc (ntiles * sizeof *tiles);

  p = tile_names;
  for (i = 0; i < ntiles; i++)
    {
      tiles[i] = evil_malloc (sizeof *tiles[i]);
      tiles[i]->text = p;
      tiles[i]->len = strlen (p);
      p += tiles[i]->len + 1;
      tiles[i]->w = XTextWidth (font, tiles[i]->text, tiles[i]->len)
	+ 2 * XPADDING + 1;
      tiles[i]->h = font_ascent + 2 * font_descent + 2 * YPADDING + 1;
    }

  while (1)
    {
      int i, y = 0;

      for (i = 0; i < ntiles; i++) {
	int x;
	if (i == 0) {
	  randomise_tiles (dpy, window);
	  x = 20 + random_sign(20);
	  y = 100 + random_sign(20);
	} else {
	  x = tiles[i - 1]->x + tiles[i - 1]->w + 4 + random_sign(3);
	}
	if (x > (xgwa.width * 3) / 4) {
	  x = 20 + random_sign(20);
	  y += tiles[i]->h + 10;
	}
	move_tile (dpy, window, i, x, y + random_sign(3));
	yield (dpy, 200000);
      }
      yield (dpy, 1000000);
    }
}

