#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>

#include <stdio.h>

#include "/afs/sipb.mit.edu/user/tlyu/bitmaps/wosat"
#define BITMAPDEPTH 1

/* This stuff gets used quite often in a given program; make them global. */
Display *display;
int screen;

#define SMALL 1
#define OK 0

void main(int argc, char **argv)
{
  Window win;
  unsigned int width, height;
  int x = 0, y = 0;
  unsigned int border_width = 4;
  unsigned int display_width, display_height;
  char *window_name = "Basic Not-so-splefty WIndow Program";
  char *icon_name = "basicWin";
  Pixmap icon_pixmap;
  XSizeHints size_hints;
  XEvent report;
  GC gc;
  XFontStruct *font_info;
  char *display_name = NULL;
  int window_size = 0;

  /* This connects to the X server */
  if((display = XOpenDisplay(display_name)) == NULL){
    (void)fprintf(stderr,
		  "basicwin: cannot connect to X server %s\n",
		  XDisplayName(display_name));
    exit(-1);
  }

  /* get screen size from display structure macro */
  screen = DefaultScreen(display);

  /* size window with enough room for text */
  display_width = DisplayWidth(display, screen);
  display_height = DisplayHeight(display, screen);

  width = display_width/3, height = display_height/4;

  /* create opauqe window */
  win = XCreateSimpleWindow(display, RootWindow(display, screen),
			    x, y, width, height, border_width,
			    BlackPixel(display, screen),
			    WhitePixel(display, screen));

  /* Create pixmap of depth 1 (bitmap) for icon */
  icon_pixmap = XCreateBitmapFromData(display, win, wosat_bits,
				      wosat_width, wosat_height);

  /* initialize size hint property for windowmanager */
  size_hints.flags = PPosition | PSize | PMinSize;
  size_hints.x = x;
  size_hints.y = y;
  size_hints.width = width;
  size_hints.height = height;
  size_hints.min_width = 350;
  size_hints.min_height = 250;

  /* set properties for windowmanager (always before mapping) */
  XSetStandardProperties(display, win, window_name, icon_name,
			 icon_pixmap, argv, argc, &size_hints);

  /* select event types wanted */
  XSelectInput(display, win, ExposureMask | KeyPressMask |
	       ButtonPressMask | StructureNotifyMask);

  load_font(&font_info);

  /* create GC for text and drawing */
  get_GC(win, &gc, font_info);

  /* Display window */
  XMapWindow(display, win);

  /* get events, use first Expose to display text and graphics;
     ConfigureNotify to indicate a resize; ButtonPress or KeyPress
     to exit */
  while(1){
    XNextEvent(display, &report);
    switch(report.type){
    case Expose:
      /* get rid of all other Expose events on the queue */
      while(XCheckTypedEvent(display, Expose, &report));
      if(window_size == SMALL)
	/* if the window was resized too small to use */
	TooSmall(win, gc, font_info);
      else{
	/* place text in window */
	draw_text(win, gc, font_info, width, height);
	draw_graphics(win, gc, width, height);
      }
      break;
    case ConfigureNotify:
      /* window resized, change width and height to
	 send to draw_text and draw_graphics in next
	 Expose */
      width = report.xconfigure.width;
      height = report.xconfigure.height;
      if((width < size_hints.min_width) ||
	 (height < size_hints.min_height))
	window_size = SMALL;
      else
	window_size = OK;
      break;
    case ButtonPress:
      /* trickle down into KeyPress (no break) */
    case KeyPress:
      XUnloadFont(display, font_info->fid);
      XFreeGC(display, gc);
      XCloseDisplay(display);
      exit(1);
    default:
      /* all events sleected by StructureNotifyMask
	 except ConfigureNotify are thrown away here,
	 since nothing useful is done with them */
      break;
    }
  }
}

get_GC(Window win, GC *gc, XFontStruct *font_info)
{
  unsigned long valuemask = 0; /* ignore XCGvalues and use
				  defaults */
  XGCValues values;
  unsigned int line_width = 6;
  int line_style = LineOnOffDash;
  int cap_style = CapRound;
  int join_style = JoinRound;
  int dash_offset = 0;
  static char dash_list[] = {
    12, 24};
  int list_length = 2;

  /* create default graphics context */
  *gc = XCreateGC(display, win, valuemask, &values);

  /* specify font */
  XSetFont(display, *gc, font_info->fid);

  /* specify black foreground since default may be white on white */
  XSetForeground(display, *gc, BlackPixel(display, screen));

  /* set line attributes */
  XSetLineAttributes(display, *gc, line_width, line_style, cap_style,
		     join_style);

  /* set dashes to be line-width in length */
  XSetDashes(display, *gc, dash_offset, dash_list, list_length);
}

load_font(XFontStruct **font_info)
{
  char *fontname = "9x15";

  /* access the font */
  if((*font_info = XLoadQueryFont(display, fontname)) == NULL){
    (void)fprintf(stderr, "basicwin: cannot open 9x15 font\n");
    exit(-1);
  }
}

draw_text(Window win, GC gc, XFontStruct *font_info,
	  unsigned int win_width, unsigned int win_height)
{
  int y = 2
; /* offset from corner of window */
  char *string1 = "Hi! I'm a window, who the hell are you?";
  char *string2 = "Hit any key or button while in this";
  char *string3 = "window to terminate...";
  char *string4 = "Screen Dimensions:";
  int len1, len2, len3, len4;
  int width1, width2, width3;
  char cd_height[50], cd_width[50], cd_depth[50];
  int font_height;
  int initial_y_offset, x_offset;

  /* need length for both XTextWidth and XDrawString */
  len1 = strlen(string1);
  len2 = strlen(string2);
  len3 = strlen(string3);

  /* get string widths for centering */
  width1 = XTextWidth(font_info, string1, len1);
  width2 = XTextWidth(font_info, string2, len2);
  width3 = XTextWidth(font_info, string3, len3);

  /* output text, centered on each line */
  XDrawString(display, win, gc, (win_width - width1)/2,
	      y, string1, len1);
  XDrawString(display, win, gc, (win_width - width2)/2,
	      (int)(win_height - 35), string2, len2);
  XDrawString(display, win, gc, (win_width - width3)/2,
	      (int)(win_height - 15), string3, len3);

  /* copy numbers into string variables */
  (void)sprintf(cd_height, " Height - %d pixels",
		DisplayHeight(display, screen));
  (void)sprintf(cd_width, " Width - %d pixels",
		DisplayWidth(display, screen));
  (void)sprintf(cd_depth, " Depth - %d plane(s)",
		DefaultDepth(display, screen));

  /* reuse these for same purpose */
  len4 = strlen(string4);
  len1 = strlen(cd_height);
  len2 = strlen(cd_width);
  len3 = strlen(cd_depth);

  font_height = font_info->max_bounds.ascent +
    font_info->max_bounds.descent;

  initial_y_offset = win_height/2 - font_height -
    font_info->max_bounds.descent;
  x_offset = (int)win_width/4;
  XDrawString(display, win, gc, x_offset, (int)initial_y_offset,
	      string4, len4);
  XDrawString(display, win, gc, x_offset, (int)initial_y_offset +
	      font_height, cd_height, len1);
  XDrawString(display, win, gc, x_offset, (int)initial_y_offset +
	      2 * font_height, cd_width, len2);
  XDrawString(display, win, gc, x_offset, (int)initial_y_offset +
	      3 * font_height, cd_depth, len3);
}

draw_graphics(Window win, GC gc, unsigned int window_width,
	      unsigned int window_height)
{
  int x, y;
  unsigned int width, height;

  height = window_height/2;
  width = 3 * window_width/4;
  x = window_width/2 - width/2; /* center */
  y = window_height/2 - height/2;
  XDrawRectangle(display, win, gc, x, y, width, height);
}

TooSmall(Window win, GC gc, XFontStruct *font_info)
{
  char *string1 = "Too Small";
  int y_offset, x_offset;

  y_offset = font_info->max_bounds.ascent + 2;
  x_offset = 2;

  /* output text, centered on each line */
  XDrawString(display, win, gc, x_offset, y_offset, string1,
	      strlen(string1));
}
