/* x_show_face.c -- The way to display a face bitmap in
   the X window system. */

/* Copyright (C) 1988, 1990, 1992  Free Software Foundation, Inc.

   This file is part of GNU Finger.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */


#include <config.h>

#if defined (PrintWidget)
#include <stdio.h>
#endif

#if !defined (HAVE_X11)
x_show_face () {}
#else

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/StringDefs.h>
#include <X11/Intrinsic.h>
#include <X11/Shell.h>

#ifdef X11R4
#include <X11/Xaw/Viewport.h>
#include <X11/Xaw/Box.h>
#include <X11/Xaw/Command.h>
#include <X11/Xaw/Label.h>
#include <X11/Xaw/Dialog.h>
#include <X11/Xaw/AsciiText.h>
#include <X11/Xaw/Text.h>
#include <X11/Xaw/Form.h>
#else
#include <X11/Viewport.h>
#include <X11/Scroll.h>
#include <X11/Box.h>
#include <X11/Command.h>
#include <X11/Label.h>
#include <X11/Dialog.h>
#include <X11/AsciiText.h>
#include <X11/Text.h>
#include <X11/Form.h>
#endif

#define XtVisual(x) DefaultVisualOfScreen (XtScreen (x))
#include "x_shface.h"
#include <bitmap.h>

#define savestring(s) (char *)strcpy ((char *)xmalloc (1 + strlen (s)), (s))

BITMAP *the_face;
char *the_user;

/* Main display interface. */

/* The X display. */
Display *the_display;

/* The graphic context that we are using. */
GC image_GC = (GC)NULL;

/* Widgets and friends. */

Widget toplevel;
Widget   quit_button;		/* Calls Quit (). */
Widget   image_model_frame;
Widget     image_frame;
Widget       image_port;
Widget         image_box = NULL;
Widget     name_button = NULL;	/* Calls display_long_info (). */
Widget	   save_button = NULL;	/* Calls Save (). */
#if defined (PrintWidget)
Widget	   print_button = NULL;	/* Calls Print (). */
#endif

XImage	   *face_image;

#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))

DEFBUTTON ("name", name, do_nothing);
DEFBUTTON ("Quit", quit, Quit);
DEFBUTTON ("Save", save, Save);

#if defined (PrintWidget)
DEFBUTTON ("Print", print, Print);
#endif

static Arg im_frame_args[] =
{
  XtNwidth, (XtArgVal) 0,
  XtNheight, (XtArgVal) 0,
  XtNx, (XtArgVal) 100,
  XtNy, (XtArgVal) 100
};

static Arg image_frame_args[] =
{
  XtNwidth, (XtArgVal) 0,
  XtNheight, (XtArgVal) 0,
  XtNborderWidth, (XtArgVal) 3,
  XtNfromVert, (XtArgVal) 0
};

static Arg image_port_args[] =
{
  XtNwidth, (XtArgVal) 0,
  XtNheight, (XtArgVal) 0,
  XtNforceBars, (XtArgVal) False,
  XtNallowHoriz, (XtArgVal) False,
  XtNallowVert, (XtArgVal) False
  };

Arg image_args[] =
{
  XtNwidth, (XtArgVal) 0,
  XtNheight, (XtArgVal) 0,
  XtNbackgroundPixmap, (XtArgVal) NULL
};


XtCallbackProc
handleExposure (widg, ignore, event)
     Widget widg;
     caddr_t ignore;
     XEvent *event;
{
  XExposeEvent *expEvent = (XExposeEvent *)event;

  XPutImage (XtDisplay (widg), expEvent->window, image_GC, face_image,
	     expEvent->x, expEvent->y,
	     expEvent->x, expEvent->y,
	     expEvent->width, expEvent->height);
}

void
Quit ()
{
  exit (0);
}
void
do_nothing ()
{}

void
Save (w, client_data, call_data)
     Widget w;
     caddr_t client_data, call_data;
{
  int site_save_face ();

  if (site_save_face (the_user, the_face))
    perror (the_user);
}

#if defined (PrintWidget)
void
Print (w, client_data, call_data)
     Widget w;
     caddr_t client_data, call_data;
{
  print_image (the_user, the_face->bits, the_face->width,
	       the_face->height, 1, "lp");
}
#endif /* PrintWidget */

Widget
StartWidget (name, class, parent)
     BUTTON *name;
     int class;
     Widget parent;
{
  Widget result;

  result = 
    XtCreateManagedWidget (name->widget_label, class, parent,
			   name->args, name->arglen);
  return (result);
}

#define WIDE 1024
#define TALL 600

x_show_face (display_name, face)
     char *display_name;
     BITMAP *face;
{
  int one = 1;
  char *empty_array[2];

  int width = face->width;
  int height = face->height;

  int uwidth = min (width, WIDE);
  int uheight = min (height, TALL);

  the_face = face;
  the_user = display_name;

  if (width > uwidth)
      image_port_args[2].value = image_port_args[3].value = (XtArgVal)True;
  
  if (height > uheight)
      image_port_args[2].value = image_port_args[4].value = (XtArgVal)True;
      
  image_args[0].value = width;
  image_args[1].value = height;
  image_args[2].value = (XtArgVal) (Pixmap)NULL;
 
  im_frame_args[0].value = uwidth;
  im_frame_args[1].value = uheight + 40;

  image_frame_args[0].value = uwidth;
  image_frame_args[1].value = uheight + 30;

  image_port_args[0].value = uwidth;
  image_port_args[1].value = uheight;

  empty_array[0] = display_name;
  empty_array[1] = (char *)NULL;

  toplevel = XtInitialize ("xdisplay", "XDisplay", NULL, 0,
			   &one, empty_array);

  image_model_frame =
    XtCreateManagedWidget ("topframe", formWidgetClass,
			   toplevel, im_frame_args, XtNumber (im_frame_args));

  quit_button = StartWidget (&quit, (int)commandWidgetClass,
			     image_model_frame);

  image_frame =
    XtCreateManagedWidget ("image_frame", boxWidgetClass,
			   image_model_frame, image_frame_args,
			   XtNumber (image_frame_args));

  image_port =
    XtCreateManagedWidget ("image_port", viewportWidgetClass,
			   image_frame, image_port_args,
			   XtNumber (image_port_args));

  the_display = XtDisplay (image_port);

  {
    unsigned long fg_color = WhitePixelOfScreen (XtScreen (image_port));
    unsigned long bg_color = BlackPixelOfScreen (XtScreen (image_port));

    image_box =
      XtCreateManagedWidget ("image_picture", boxWidgetClass,
			     image_port, image_args, XtNumber (image_args));
  

    XtAddEventHandler (image_box, ExposureMask, False, handleExposure, NULL);

    XtRealizeWidget (toplevel);

    face_image = XCreateImage (the_display, XtVisual (image_box), 1,
			       XYBitmap, 0, (char *)face->bits, width, height,
			       16, 0);

    face_image->byte_order = LSBFirst;
    face_image->bitmap_bit_order = LSBFirst;

    image_GC =
      XCreateGC (the_display, XtWindow (image_box), (long)0,
		 (XGCValues *)NULL);

    XSetFillStyle (the_display, image_GC, FillSolid);
    XSetFunction (the_display, image_GC, GXcopy);
    XSetTSOrigin (the_display, image_GC, 0, 0);
    XSetPlaneMask (the_display, image_GC, AllPlanes);
    XSetForeground (the_display, image_GC, fg_color);
    XSetBackground (the_display, image_GC, bg_color);
  }

  /* Well, a couple of hacks, just for fun... */
  if ((strcmp (display_name, "bfox") == 0) ||
      (strcmp (display_name, "Brian Fox") == 0) ||
      (xstrnicmp (display_name, "dante", 5) == 0))
    display_name = savestring ("Ugly Implementor");

  if (strcmp (display_name, "cshackers") == 0)
    display_name = savestring ("Ugly Hacking Crew");

  if ((xstricmp (display_name, "ebg") == 0) ||
      (xstricmp (display_name, "Edward B. Gamble") == 0))
    display_name = savestring ("Ugly Friend of Ugly Implementor");
  
  SET_BUTTON_LABEL (name, display_name);
  name_button = StartWidget (&name, (int)commandWidgetClass, image_frame);
  save_button = StartWidget (&save, (int)commandWidgetClass, image_frame);
#if defined (PrintWidget)
  print_button = StartWidget (&print, (int)commandWidgetClass, image_frame);
#endif
  
  XtMainLoop ();
}

#if defined (PrintWidget)
/* Inches to Points conversion. */
#define	ITOP(in)	((int)((in) * 72.0))

/* LaserWriter X bias. */
#define	XTRANS_FUDGE	(1.0 / 16.0)

/* Width and height of the paper. */
#define w_page 8.5
#define h_page 11.0

#define	HI_NIBBLE(i)	(((i) >> 4) & 0xf)
#define	LO_NIBBLE(i)	((i) & 0xf)
#define ADJUST 0.932
#define x_error(arg1, arg2)

print_image (name, image, width, height, depth, printer_name)
     char *name;
     byte *image;
     int width, height, depth;
     char *printer_name;
{
  FILE *output_stream;
  char *filename;
  register int i, *hexp;
  int hexdig[16];
  int error = 0;
  double aspect;
  double w_img = 7.5, h_img = 9.0;

  aspect = width;
  aspect /= height;

  /*
   * Adjust PostScript image size to preserve aspect ratio.
   */
  if (aspect >= w_img / h_img)
    h_img = w_img / aspect;
  else
    w_img = h_img * aspect;

  switch (depth)
    {
    case 1:
      if (width % 16 != 0)
	width += 16 - width % 16;
      break;

    case 8:
      width += width & 1;
      break;
    }

  /* We have to reverse the image bit ordering as well as invert the bits.
     The fast way is with a translation table. */

  hexdig[15] = 'f';
  hexdig[14] = '7';
  hexdig[13] = 'b';
  hexdig[12] = '3';
  hexdig[11] = 'd';
  hexdig[10] = '5';
  hexdig[9]  = '9';
  hexdig[8]  = '1';
  hexdig[7]  = 'e';
  hexdig[6]  = '6';
  hexdig[5]  = 'a';
  hexdig[4]  = '2';
  hexdig[3]  = 'c';
  hexdig[2]  = '4';
  hexdig[1]  = '8';
  hexdig[0]  = '0';

  /* Create a temporary filename for postscript output. */
  filename = (char *)xmalloc (20 + strlen (name));
  sprintf (filename, "/tmp/%s-face", name);
  output_stream = fopen (filename, "w");

  if (!output_stream)
    {
      x_error ("Couldn't open temp file `%s'", filename);
      return;
    }

  /* Prepare to print the file. */

  /* Postscript header. */
  fprintf (output_stream, "%%!\n");

  /* Center the image on the output page. */
  fprintf (output_stream, "%d %d translate\n",
	   ITOP (((w_page - w_img) / 2.0) + XTRANS_FUDGE),
	   ITOP ((h_page - h_img) / 2.0));

  fprintf (output_stream, "%d %d scale\n",
	   ITOP (w_img), ITOP (ADJUST * h_img));

  /* I/O buffer for readhexstring. */
  fprintf (output_stream, "/linebuf %d string def\n", width / (8 / depth));

  /* Say how big the image is. */
  fprintf (output_stream, "%ld %ld %d\n", width, height, depth);
  fprintf (output_stream, "[%ld 0 0 -%ld 0 %ld]\n", width, height, height);
  fprintf (output_stream, "{currentfile linebuf readhexstring pop}\n");

  /* Print the image data itself. */
  fprintf (output_stream, "image\n");

  hexp = hexdig;

  if (fflush (output_stream) == EOF)
    error = 1;

  for (i = 0; !error && i < (width * height * depth) / 8; i++)
    {
      if ((putc (hexp[LO_NIBBLE (image[i])], output_stream) == EOF) ||
	  (putc (hexp[HI_NIBBLE (image[i])], output_stream) == EOF))
	error = 1;
    }

  /* Show the page. */
  if (!error)
    fprintf (output_stream, "showpage\n");

  fclose (output_stream);

  /* Call the printer. */
  {
    char *command;

    command = (char *)xmalloc (20 + strlen (filename) + strlen (printer_name));

    sprintf (command, "lpr -P%s %s 2>1", printer_name, filename);
    system (command);
    free (command);
  }
  unlink (filename);
  free (filename);
}
#endif /* PrintWidget */

#endif /* HAVE_X11 */
