/* to compile on gnux:   
gcc -o cement_display -Wall -L/usr/X11R6/lib -lX11 -lm cement_display.c */

#include <stdio.h>
#include "cement_display.h"

#include<stdio.h>                                                         
#include<stdlib.h>                                                       
#include<math.h>                                                                  
#include<string.h>                                                                
#include<sys/time.h>                                                         
#include<unistd.h> 

#define BUF_SIZE 1024


/* yes they are all globals! */
Screen     *screen_point;  /* pointer to screen                      */
Display    *display;       /* pointer to the display                 */
Window     win;	           /* window to be used for graphics         */
int        screen_num;     /* screen number                          */
Window     root;           /* root window of display                 */
XSizeHints size_hints;     /* size hint structure for window manager */

GC the_GC;                 /* graphics context to be used            */
GC copyGC;                 /* main GC                                */ 
XGCValues the_GC_values;   /* values in graphics context             */
extern GC lineGC;
			   /* Removed FILE declarations              */
Pixmap buff;               /* Pixmap used as drawing buffer          */
XEvent event;              /* structure to hold an XEvent            */
Pixmap workingDrawing;


/* - - - NEW - - - */
XSetWindowAttributes win_attr;
Colormap cmap;
XColor scolor,ecolor, white, yellow;
Visual *vis,*def_vis;
Visual *visual;
XVisualInfo *p_vis_info, VInfoTmpl;
int ii;
XGCValues gc_attr;
GC gc2, clear_gc;
GC gc24;
int depth;

XImage *picture;

extern GC white_GC;


XImage * pic;
int borderwidth = 10;
/* - - - - - - - - */


/* - - - - New - - - - */
#define INIT_NAME "start_graphics"     /* name of startup module */

#define E_NOOPEN    -1		/* error--unable to open X connection */
#define E_NOFILE    -2		/* error--unable to open data file    */
#define E_NOEVENTS  -3		/* error--no events in file           */
#define BORDER 2		/* size of window border              */

#define TRUE  1
#define FALSE 0


/* ======================================================================== */
int main(int argc, char *argv[])
{
   Display *my_display;       /* display holds information about the root window */
   GC lineGC;
   Pixmap my_drawing;
   char *imagename = NULL;    /* name of the image to be displayed */
   Var *frame;
   int scr_width, scr_height; /* height and width of the screen */
   int im_height, im_width;   /* height and width of the image  */ 
   int corner;                /* corner in which to display the image */
   int end = 0;               /* indicates when to stop the program */
     
   if ((frame = (Var*) malloc(sizeof(Var))) == NULL) {
      fprintf(stderr, "Memory allocation failure: malloc failed in main.\n");
      exit(-1);
   }  
  
   if ((imagename = (char*) malloc(sizeof(char)*50)) == NULL) {
      fprintf(stderr, "Memory allocation failure: malloc failed in main.\n");
      exit(-1);
   }
   start_graphics(&my_display, &lineGC);
   
   /* Get the width of this screen to display an almost-fullscreen window. */
   scr_width = DisplayWidth(my_display, screen_num) - 20;
   scr_height = DisplayHeight(my_display, screen_num) - 50;

   my_drawing = create_display_panel_single("Cement Output", 0, 0, scr_width, scr_height);

   /* display the image and then wait for the next image file name from stdin */
   /* ctrl-D to end */
   end = scanf("%s %d", imagename, &corner);

   while(end!=EOF) {

      /* Initialize and load up the image. */      
      free(frame);   
      if((frame = (Var*) malloc(sizeof(Var))) == NULL){
	fprintf(stderr, "Memory allocation failure: malloc failed in main.\n");
	exit(-1);
      }

      get_image_dimensions(imagename, &im_height, &im_width);
      init_var(frame, "frame", scr_height, scr_width, IMAGE, GREY_SCALE);
      load_image(imagename, frame);

      /* Create a display window that fills the whole screen. */
      update_image_single(frame, my_drawing, scr_width, scr_height, corner);        

      /* Get the name of the next image from stdin. */
      end = scanf("%s %d",imagename, &corner);
   }

   printf("\nHOLD: Press any key in Xwindow to end.\n\n");
   wait_for_any_key_in_Xwindow(my_display,win);

   free(frame);
   return 0;
}


/* ======================================================================== */
void start_graphics(Display **p_display,   /* address of display pointer */
		    GC *p_context)         /* pointer to graphics context  */
{
  /* - - - Open display - - - */
  if((display = XOpenDisplay(NULL)) == NULL) 
    {
      printf("%s: can't open display",INIT_NAME);
      exit(E_NOOPEN);
    }
  
  /* - - - Set some useful globals - - - */
  screen_num = DefaultScreen(display);
  root = DefaultRootWindow(display);
  screen_point = XScreenOfDisplay(display,screen_num);

  /* - - - VISUAL STUFF - - - */
  VInfoTmpl.depth = 24;
  VInfoTmpl.screen = screen_num;

  /* note from shawn: using TrueColor rather than DirectColor */
  VInfoTmpl.class = TrueColor;
  
  p_vis_info = XGetVisualInfo(display, (VisualScreenMask | VisualDepthMask |
                                        VisualClassMask), &VInfoTmpl, &ii);

  /* - - - Check if 24 bit visual found - - - */
  if (ii < 1)
    {
      printf("  Could not get 24bit TrueColor Visual.\n");
      printf("  Trying 8 bit.");
      VInfoTmpl.depth = 8;
      p_vis_info = XGetVisualInfo(display,
				  (VisualScreenMask | VisualDepthMask |
				   VisualClassMask), &VInfoTmpl, &ii);      
      if(ii < 1)
	{
	  printf("  Could not get 8 bit TrueColor.\n  EXITING\n");
	  exit(1);
	}
      depth = 8;
    }
  else
    depth = 24;
 
  vis = p_vis_info->visual;
  def_vis = XDefaultVisual(display,screen_num);

  if (vis->visualid == def_vis->visualid)
   cmap = XDefaultColormap(display,screen_num);
  else
   {
     /* note from Shawn: use AllocNone not AllocAll for TrueColor Visuals */
     cmap = XCreateColormap(display, root, vis, AllocNone);
     if(cmap == DefaultColormap(display, screen_num))
       printf("All I got is the crummy DEFAULT CMAP!\n");
     fflush(stdout);
   }
  win_attr.colormap = cmap;
  win_attr.backing_store = Always;
  win_attr.backing_planes = AllPlanes;
  /* - - - - - - - - - - - - - - - - - */

  SetupGC();   /* this creates a GC that is the depth of the ROOT WINDOW !*/

  *p_display = display;
  *p_context = the_GC; 
}


Pixmap create_display_panel_single(char * win_title,
				   int X, int Y, int width, int height)
{
  char * data;
  Pixmap drawing;

  /* ====== Creat XImage structures to hold image data ===== */
  data = (char *)calloc( (height * width), sizeof(unsigned long));
  pic = XCreateImage(display, vis, 
		     depth,  
		     ZPixmap, 0,
		     data,
		     width, height,
		     BitmapPad(display),
		     0);

  /* ==== create window ==== */
  drawing = create_buffered_window((width)+ borderwidth,
				   (height) + borderwidth,
				   win_title, X, Y);
  return drawing;
}


/* ===================================================================== */
int update_image_single(Var * Img, Pixmap drawing, int width, int height, int corner)
{
  int x, y, index, xstart, xend, ystart, yend;
  unsigned long pixel_color;

  if( Img->var_type != IMAGE)
    {
      printf("  Can't display non-character images yet.  RETURNING\n");
      return FALSE;
    }
 
  /* - - Dump Img->image into XImage structure, picture - - */
  index = 0;

  /* Image is in the top left hand corner. */
  if(corner == 1){
     xstart = 0; ystart = 0;
     xend = Img->N; yend = Img->M;
  
  /* Image is in the top right hand corner. */ 
  } else if (corner == 2) {
     xstart = width-Img->N; ystart = 0;
     xend = width; yend = Img->M;        
  
  /* Image is in the bottom left hand corner. */ 
  } else if (corner == 3) {
     xstart = 0; ystart = height-Img->M;
     xend = Img->N; yend = height;

  /* Image is in the bottom right hand corner. */
  } else if (corner == 4) {
     xstart = width-Img->N; ystart = height-Img->M;
     xend = width; yend = height;
  }

  printf("The corner is: %d\n", corner);

  for(y=0; y < height; y++)
    {
      for(x=0; x < width; x++)
	{
          if ((y >= ystart) && (y < yend) && (x >= xstart) && (x < xend)) {

	    pixel_color = ((unsigned long)Img->image[index] << 16) +
	                    ((unsigned long)Img->image[index] << 8) +
			      ((unsigned long)Img->image[index]); 
            index ++;
          }
	  else
            pixel_color = ((unsigned long)255 << 16) +
	                    ((unsigned long)255 << 8) +
			      ((unsigned long)255);

          XPutPixel(pic, x, y, pixel_color);
	}
    }
  /* ==== create a 24 bit dummy GC to use as argument to XPutImage ==== */
  gc24 = XCreateGC(display, drawing, GCFunction, &the_GC_values);
  update_graphics();
  
  /* ==== display XImage in window ===== */
  XPutImage(display, drawing, gc24, pic, 
	      0, 0, 0, 0, width, height);
  update_graphics();

  return TRUE;
}


/* ===================================================================== */
int display_image(Var * Img, char * win_title, int X, int Y)
{
  XImage * picture;
  Pixmap drawing;
  char * data;
  int index;
  int x, y;
  unsigned long pixel_color;

  if( Img->var_type != IMAGE)
    {
      printf("  Can't display non-character images yet.  RETURNING\n");
      return FALSE;
    }

  /* - - Creat XImage structure to hold image data - - */
  data = (char *)calloc( (Img->M * Img->N), sizeof(unsigned long));
  picture = XCreateImage(display, vis, 
			 depth,  
                         ZPixmap, 0,
			 data,
			 Img->N, Img->M,
                         BitmapPad(display),
                         0);

  /* - - Dump Img->image into XImage structure, picture - - */
  index = 0;
  for(y=0; y < Img->M; y++)
    {
      for(x=0; x < Img->N; x++, index++)
	{
	  pixel_color = ((long)Img->image[index] << 16) +
	                    ((long)Img->image[index] << 8) +
			      ((long)Img->image[index]); 
	  XPutPixel(picture, x, y, pixel_color);
	}
    }


  /* - - create window - - */
  drawing = create_buffered_window(Img->N, Img->M,
				   win_title, X, Y);
  /* - - create a 24 bit dummy GC to use as argument to XPutImage - - */
  gc24 = XCreateGC(display, drawing, GCFunction, &the_GC_values);

  update_graphics();
  
  /* - - display XImage in window - - */
  XPutImage(display, drawing, gc24, picture, 
	    0, 0, 0, 0, Img->N, Img->M);
  update_graphics();

  return TRUE;
}


/* ===================================================================== */
Pixmap create_buffered_window(unsigned int width,
			      unsigned int height,
			      char * win_name,
			      int posX, int posY)  
{
  SetSizeHints(width,height);
  
  /* N.B. start_graphics must be called before this */
  win = XCreateWindow(display, root, posX, posY,
                      size_hints.width,
                      size_hints.height,
                      BORDER,
                      p_vis_info->depth,
                      InputOutput, vis,
                      CWColormap | CWEventMask |
                      CWBackingStore | CWBorderPixel,
                      &win_attr);

/*  win = XCreateSimpleWindow(display, root, posX, posY,
			    size_hints.width,
			    size_hints.height,
			    BORDER,
			    BlackPixel(display,screen_num),
			    WhitePixel(display,screen_num));

*/

  /* - - - set properties for window manager - - - */
/*  XSetStandardProperties(display, win, win_name, "current frames", None,
			 0,NULL,&size_hints); */
  XSetStandardProperties(display, win, win_name, "current frames", None,
			 NULL,0,&size_hints);
  
  /* - - - select events of interest and map window - - - */
  XSelectInput(display,win,
	       (ExposureMask | ButtonPressMask |
		VisibilityChangeMask | KeyPressMask));
  XMapWindow(display,win);  /* creates window */



  /* - - - create blanked out Pixmap buffer for user - - - */
  workingDrawing =  XCreatePixmap(display, win, width, height,
				  depth);  
  ClearPixmap(workingDrawing, width, height);
  buff = XCreatePixmap(display, win, width, height,
		       depth);
  ClearPixmap(buff, width, height);
  XSynchronize(display, TRUE); /* Allow other processes to share CPU */

  XNextEvent(display, &event);
  return(buff); 
}

void ClearPixmap(p, w, h)
     Pixmap p;
     int w, h;
{
  GC gc;
  
  gc = XCreateGC(display, win, None, &the_GC_values);
  XSetGraphicsExposures(display, gc, False);
  XSetForeground(display, gc, WhitePixel(display, screen_num));
  XFillRectangle(display, p, gc, 0, 0, w,  h);
}

void update_graphics(void)
{
  XCopyArea(display, buff, win, gc24, 0, 0,
	    size_hints.width,size_hints.height, 0, 0);
  XFlush(display);

} 

void SetSizeHints(width, height)
     int width, height;
{
  size_hints.x = 200;   
  size_hints.y = 50;
  size_hints.min_width = width;
  size_hints.min_height = height;
  size_hints.max_width = width; 
  size_hints.max_height = height;
  size_hints.width = width;
  size_hints.height = height;
  size_hints.flags = USSize|USPosition|PMinSize|PMaxSize;
}

void SetupGC()
{
  the_GC_values.foreground = BlackPixel(display, screen_num);
  the_GC_values.function = GXcopy; /* GXequiv this is for black on white */
  the_GC_values.line_width = 4;
  the_GC_values.cap_style = CapRound;

  the_GC = XCreateGC(display, root,
		     (GCFunction | GCForeground | GCLineWidth | GCCapStyle),
		     &the_GC_values);

  copyGC = XCreateGC(display, root, None, &the_GC_values);


  XSetGraphicsExposures(display, the_GC, False);
  XSetGraphicsExposures(display, copyGC, False);
}

/* =========================================================================
*/
int load_image(char * filenameA, Var * frameA)                                 
{                                                                              
  int val;  

  /* The image data from the named file is read into the Var struct.
   * It will be read in to the "image" field if the image is a pnm, or
   * directly into the "data" field if the image is a plm.
   */
  val = load_ppm_file(filenameA, frameA);                        
                                                                           
  /* - - if successful - - */           
  if(val == 0)                                         
    {                                    
      printf("Loaded the image successfully: %d\n", frameA->var_type);
      return(1);                               
    }                                                                    
  else                                                       
    {                                                                          
      printf("\n load_image failed to read in image\nExiting\n\n");                          free(frameA->image);                                                       
      exit(1);                                                                   
    }                                                                 
  return(0);                                                          
}               

/* =========================================================================
 */
int load_ppm_file(char * file_name, Var * image)
{
  int magic_number;
  int c;                        /* for reading comments */
  unsigned int garbage;
  int width, height, depth;

  unsigned int pixelr;
  FILE * file_ptr;
  unsigned int row_index;
  int num_got;
  char *string1, *string2;

  file_ptr = fopen(file_name, "r");
  if(file_ptr == NULL)
    {
      printf("\n  ERROR: file not found\n");
      return -1;
    }

  /* - - GET INFO FROM PPM HEADER - - */
  fscanf(file_ptr,"P%d", &magic_number);

  /* - kill a comment - */
  c = getc(file_ptr);
  while (1)
    {
      if (c == '#')
	{
	  while (1)
	    {
	      c = getc(file_ptr);
	      if (c == '\n' || c == EOF)
		break;    
	    }
	}
      if (c==EOF)
	return 0;
      if (c>='0' && c<='9')
	{
	  /* rewind file pointer 1 char position */
	  fseek(file_ptr, -1, SEEK_CUR); 
	  break;  
	}
      /* see if we are getting garbage (non-whitespace) */
      if (c!=' ' && c!='\t' && c!='\r' && c!='\n' && c!=',')
	garbage=1;
      
      c = getc(file_ptr);
    }

  printf("Magic number: %d\n", magic_number);
  
  fscanf(file_ptr,"%d %d %d", &width, &height, &depth);

  /* - - set Variable properties M,N, image_type, var_type and name - - */
  image->M = height;
  image->N = width;
  image->max_value = depth;

  if(magic_number == 2 || magic_number == 5)
    image->image_type = GREY_SCALE;
  else if(magic_number == 3 || magic_number == 6)
    image->image_type = RGB24;
  else if(magic_number == 1)
    image->image_type = PLM;
    image->var_type = INTERNAL_VARIABLE;

  image->var_type = IMAGE;

  /* set matrix name from input file name */
  /* . in file name incorrectly sets the name */
  string1 = (char *)calloc(200, sizeof(char));
  string2 = (char *)calloc(200, sizeof(char));
  
  fflush(stdout);

  string1 = strrchr(file_name, '/');
  if(string1 == NULL)
    {
      /* cut off .pgm and set name */
      strcpy(string2, file_name);
      sscanf(string2, "%[^.]s", string2);
      image->name = string2;
    }
  else
    {
      sscanf(string1, "/%s.pgm", string2);
      sscanf(string2, "%[^.]s", string2);
/*      fflush(stdout);*/

      image->name = string2;
    }
  /* - - freeing string1 caused an error REMOVED 11/2/94 - - */

  /* - - - alloc memory for picture - - - */
  if(magic_number == 6 || magic_number == 3)  /* 24 bit RGB */
    image->image = (unsigned char *) calloc(width*height*3, sizeof(unsigned char));
  else if(magic_number == 5 || magic_number == 2) /* 8 bit grey scale */
    image->image = (unsigned char *) calloc(width*height, sizeof(unsigned char));
  else if (magic_number == 1)
    image->data = (Data *) calloc(width*height, sizeof(Data));

  if(magic_number <= 3) /* read ascii text in */
    {
      if(magic_number == 2)
	{
	  for(row_index = 0; row_index < height*width; row_index++)
	    {
	      fscanf(file_ptr,"%c", &image->image[row_index]);
	    }
	}
      else if (magic_number == 1)
        {
	  for(row_index = 0; row_index < height*width; row_index++)
	    {
	      fscanf(file_ptr,"%f", &image->data[row_index]);
              return 0;
	    }
        }       
    }
  else if( (magic_number > 3) && (magic_number < 7))
    {
      fread(&pixelr, 1, 1, file_ptr);   /* cut off 10 that keeps showing up */
      if(magic_number == 6)
	{
	  num_got = fread(image->image, 1, width*height*3, file_ptr); 
	  fclose(file_ptr);
/*	  printf("  DONE\n  read %d bytes of data.\n", num_got);*/
	  return 0;	  
	}
      else if(magic_number == 5)
	{
	  num_got = fread(image->image, 1, width*height, file_ptr);
	  fclose(file_ptr);
	  /* printf("  DONE\n  read %d bytes of data.\n", num_got);*/
	  return 0;
	}
    }
  return -1;
}


/* ============================================================ */
int get_image_dimensions(char *filename, int *width, int *height)
{
  int magic_number;
  int c;                        /* for reading comments */
  unsigned int garbage;

  FILE * file_ptr;

  file_ptr = fopen(filename, "r");
  if(file_ptr == NULL)
    {
      printf("\n  ERROR:  Could not open file to find dimensions\n");
      return(-1);
    }

  /* - - GET INFO FROM PPM HEADER - - */
  fscanf(file_ptr,"P%d", &magic_number);

  /* - kill a comment - */
  c = getc(file_ptr);
  while (1)
    {
      if (c == '#')
	{
	  while (1)
	    {
	      c = getc(file_ptr);
	      if (c == '\n' || c == EOF)
		break;    
	    }
	}
      if (c==EOF)
	return 0;
      if (c>='0' && c<='9')
	{
	  /* rewind file pointer 1 char position */
	  fseek(file_ptr, -1, SEEK_CUR); 
	  break;  
	}
      /* see if we are getting garbage (non-whitespace) */
      if (c!=' ' && c!='\t' && c!='\r' && c!='\n' && c!=',')
	garbage=1;
      
      c = getc(file_ptr);
    }

  return(0);
}

/* ===================================================================== */
void init_var(Var * variable, char *name,
	       int M, int N, int var_type, int image_type)
{
  variable->name = name;
  variable->M = M;
  variable->N = N;

  variable->image_type = image_type;
  variable->var_type = var_type;
  
  if(var_type == IMAGE)
    variable->image =
      (unsigned char *)calloc(M*N*image_type, sizeof(unsigned char));
  else if(var_type == INTERNAL_VARIABLE)
    variable->data2 = (Data2 *)calloc(M*N*image_type, sizeof(Data2)); 
  else if(var_type == DATA_VARIABLE)
    variable->data = (Data *)calloc(M*N*image_type, sizeof(Data)); 
}


/*
bacon (e.g. had some trouble with order of arguments, might have been
wrong all along and never noticed on the hp735)
*/
/* find . -type f -print | xargs fgrep -i xsetstandard
./X11/Xutil.h:extern XSetStandardProperties(
./X11/Xutil.h:extern void XSetStandardColormap(
*/

void wait_for_any_key_in_Xwindow(Display *display, Window win)
{
  while(1) {
    XEvent event;

    XNextEvent( display, &event );
    if( event.type == KeyPress ) {
      char buffer_return[10];
      KeySym keysym_return;
      int len;

      for( len=0; len<10; len++ ) buffer_return[len] = '\0';
      len = XLookupString((XKeyEvent*)&event,
			  buffer_return, 10, 
			  &keysym_return, 
			  NULL );			    /* &status_in_out ); */
	  
      fprintf(stderr, "len:%d buff:%s keysym:%d \n", 
	      len, buffer_return, (int)keysym_return );

      return;
    }
  }
}  


