/* xwd2ri8.c 
 * This program is limited to X11 format XWD files.
 * Original due to Jim Salem of Thinking Machines.
 * Modifications for error reporting by Peter Webb, NCSA.
 */

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

/* X11 include files

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

/* Package include files */

#include "tiff2ri8.h"
#include "types.h"      /* Package-wide type definitions */
#include "error.h"      /* Error codes */
#include "extern.h"     /* External routines and global variables */
#include "reformat.h"   /* Package-wide contants */
#include "window.h"

#define MAXCOLORS 256

/* Static variables */

static int bits_per_item, bits_used, bit_shift, bits_per_pixel, pixel_mask;
static int bit_order, byte_swap;
static byte buf[4];
static byte *byteP;
static short *shortP;
static long *longP;

static short bs_short();
static int bs_int();
static long bs_long();

static byte getpixnum( file )
     FILE *file;
{
  byte p;
  
  if ( bits_used == bits_per_item )
    {
      if ( fread( buf, bits_per_item / 8, 1, file ) != 1 )
	{
	  err_msg(XWD2HDF, BadFileData);
	  return(-1);
	}

      if ( byte_swap )
	switch ( bits_per_item )
	  {
	  case 8:
	    break;
	    
	  case 16:
	    *shortP = bs_short( *shortP );
	    break;
	    
	  case 32:
	    *longP = bs_long( *longP );
	    break;
	    
	  default:
	    err_msg(XWD2HDF, InternalError);
	    return(-1);
	  }
      bits_used = 0;
      
      if ( bit_order == MSBFirst )
	bit_shift = bits_per_item - bits_per_pixel;
      else
	bit_shift = 0;
    }
  
  switch ( bits_per_item )
    {
    case 8:
      p = ( *byteP >> bit_shift) & pixel_mask;
      break;
      
    case 16:
      p = ( *shortP >> bit_shift) & pixel_mask;
      break;
      
    case 32:
      p = ( *longP >> bit_shift) & pixel_mask;
      break;
      
    default:
      err_msg(XWD2HDF, InternalError);
      return(-1);
    }
  
  if ( bit_order == MSBFirst )
    bit_shift -= bits_per_pixel;
  else
    bit_shift += bits_per_pixel;
  bits_used += bits_per_pixel;
  
  return p;
}

/* Extract the image from the dump */

static ErrorCode getimage(file,image,y,x,pad)
     FILE *file;
     byte *image;
     int x,y,pad;
{
  int i,j;

  for (i=0; i<y; i++)
    {
      for (j=0; j<x; j++)
	*(image++) = getpixnum(file);
      for ( j = 0; j < pad; j++ ) 
	(void) getpixnum( file );
    }
  return(AllOk);
}

/* Get the initial information about the X window dump */

static ErrorCode getinit( file, colsP, rowsP, padrightP, palsizeP, hdfpal)
     FILE *file;
     int *colsP, *rowsP, *padrightP, *palsizeP;
     char *hdfpal;
{
/* Assume X11 headers are larger than X10 ones. */

  X11WDFileHeader header; /* [sizeof(X11WDFileHeader)]; */
  X11WDFileHeader *h11P;
  char *ptr, junk[10000];
  int test, i, np, dummy1, dummy2, dummy3;
  unsigned  short minred, maxred;
  X11XColor x11col;
  byte color_buf[X11WD_COLOR_SIZE];
  
  h11P = (X11WDFileHeader *) &header;
  
  if (fread((byte *)&header,sizeof(*h11P),1,file) != 1 )
    return(err_msg(XWD2HDF, BadFileData));

printf("version %ld\n", h11P->file_version);
  if ( h11P->file_version != X11WD_FILE_VERSION )
    {
      byte_swap = 1;
      h11P->header_size = h11P->header_size;
      h11P->file_version = h11P->file_version;
      h11P->pixmap_format = h11P->pixmap_format;
      h11P->pixmap_depth = h11P->pixmap_depth;
      h11P->pixmap_width = h11P->pixmap_width;
      h11P->pixmap_height = h11P->pixmap_height;
      h11P->xoffset = h11P->xoffset;
      h11P->byte_order = h11P->byte_order;
      h11P->bitmap_unit = h11P->bitmap_unit;
      h11P->bitmap_bit_order = h11P->bitmap_bit_order;
      h11P->bitmap_pad = h11P->bitmap_pad;
      h11P->bits_per_pixel = h11P->bits_per_pixel;
      h11P->bytes_per_line = h11P->bytes_per_line;
      h11P->visual_class = h11P->visual_class;
      h11P->red_mask = h11P->red_mask;
      h11P->green_mask = h11P->green_mask;
      h11P->blue_mask = h11P->blue_mask;
      h11P->bits_per_rgb = h11P->bits_per_rgb;
      h11P->colormap_entries = h11P->colormap_entries;
      h11P->ncolors = h11P->ncolors;
      h11P->window_width = h11P->window_width;
      h11P->window_height = h11P->window_height;
      h11P->window_x = h11P->window_x;
      h11P->window_y = h11P->window_y;
      h11P->window_bdrwidth = h11P->window_bdrwidth;
    }

  *colsP = h11P->pixmap_width;
  *rowsP = h11P->pixmap_height;
  *padrightP = h11P->bytes_per_line * 8 / h11P->bits_per_pixel - *colsP;
  *palsizeP = h11P->colormap_entries;
printf("rows = %d\ncols = %d\n", *rowsP, *colsP);

printf("header %ld\n", h11P->header_size);
printf("size %ld\n", sizeof(X11WDFileHeader));
printf("extra %ld\n", h11P->header_size - sizeof(*h11P));
printf("x11x %d\n", sizeof(X11XColor));
  if ( fread( junk, 1, h11P->header_size - sizeof(*h11P), file ) != 
      h11P->header_size - sizeof(*h11P))
    return(err_msg(XWD2HDF, BadFileData));
  
/* Check whether we can handle this dump. */

  if ( h11P->pixmap_depth > 8 )
    return(err_msg(XWD2HDF, ImageTooComplex));

  if ( h11P->bits_per_rgb > 8 )
    return(err_msg(XWD2HDF, ImageTooComplex));

  if ( h11P->ncolors > MAXCOLORS )
    return(err_msg(XWD2HDF, TooManyColors));

  if ( h11P->pixmap_format != ZPixmap )
    return(err_msg(XWD2HDF, BadFileData));

  if ( h11P->bitmap_unit != 8 && h11P->bitmap_unit != 16 && 
      h11P->bitmap_unit != 32 )
    return(err_msg(XWD2HDF, BadFileData));
  
/*
 * Read X11 colormap.
 */

  minred = 65535;
  maxred = 0;

  for ( i = 0; i < h11P->colormap_entries; i++ )
    {
      if ( fread( color_buf, X11WD_COLOR_SIZE, 1, file ) != 1 )
	return(err_msg(XWD2HDF, BadFileData));
      x11col.pixel = LONG(color_buf);
      x11col.red = SHORT(color_buf+4);
      x11col.green = SHORT(color_buf+6);
      x11col.blue = SHORT(color_buf+8);
      if (x11col.pixel < 256)
	{
	  if (minred > x11col.red) minred = x11col.red;
	  if (maxred < x11col.red) maxred = x11col.red;
	  np = x11col.pixel * 3;
	  dummy1 = (unsigned) x11col.red / 256 ;
	  dummy2 = (unsigned) x11col.green / 256 ;
	  dummy3 = (unsigned) x11col.blue / 256 ;
	  hdfpal[np]   = (byte) dummy1;
	  hdfpal[np+1] = (byte) dummy2;
	  hdfpal[np+2] = (byte) dummy3;
	}
      else
{ printf("input color: %d, test: %d\n", (long)(x11col.pixel), test);
	return(err_msg(XWD2HDF, BadInputColor));
}
    }
/*   fprintf(stderr,"minred = %d, maxred = %d\n",minred,maxred); */

/* rest of stuff for getpixnum */

  bits_per_item = h11P->bitmap_unit;
  bits_used = bits_per_item;
  bits_per_pixel = h11P->bits_per_pixel;
  bit_order = h11P->bitmap_bit_order;
  pixel_mask = ( 1 << bits_per_pixel ) - 1;
  
printf("bits/item = %ld\n", bits_per_item);
printf("bits/pixel = %d\n", bits_per_pixel);
  byteP = (byte *) buf;
  shortP = (short *) buf;
  longP = (long *) buf;

  if (h11P->colormap_entries < 1)
    return(err_msg(XWD2HDF, NoColorMap));
  else return(AllOk);
}

/* External routine.  Read the file and return the image buffer. */

ErrorCode XWDToRI8(theName, file_info)
     char *theName;                     /* input file name */
     FileInfo *file_info;
{
  FILE *ifd;
  unsigned int nspace;
  int padright, palsize, i;
  ErrorCode error = AllOk;

/* open input file */

  ifd = fopen(theName, "r");
  if (ifd == NULL)
    return(err_msg(XWD2HDF, CannotOpenFile));

/* get X info from file */

  error = getinit(ifd, &file_info->width, &file_info->height, &padright,
		  &palsize, file_info->palette);
  if (error != AllOk && error != NoColorMap)
    return(error);

/* Put in gray scale palette if there is none in the file */

  if (error == NoColorMap) 
    for (i=0; i<(MAX_PAL/3); i++)
      file_info->palette[i*3] = file_info->palette[i*3+1] =
	file_info->palette[i*3+2] = (unsigned char) 255 - i;

/* alloc image array */

  nspace = file_info->width * file_info->height;
  file_info->image = (unsigned char *)malloc(nspace);
  if (file_info->image == NULL)
    return(err_msg(XWD2HDF, NoMemory));

/* fill image array */

  error = getimage(ifd, file_info->image, file_info->height, file_info->width,
		   padright);

  file_info->values = (BitMask)(CvtImage | CvtPalette);
  return(error);
}




static short bs_short( s )
     short s;
{
  short ss;
  unsigned char *bp, t;
  
  ss = s;
  bp = (unsigned char *) &ss;
  t = bp[0];
  bp[0] = bp[1];
  bp[1] = t;
  return ss;
}

static int bs_int( i )
     int i;
{
  int ii;
  unsigned char *bp, t;
  
  ii = i;
  bp = (unsigned char *) &ii;
  t = bp[0];
  bp[0] = bp[3];
  bp[3] = t;
  t = bp[1];
  bp[1] = bp[2];
  bp[2] = t;
  return ii;
}

static long bs_long( l )
     long l;
{
  return bs_int( l );
}
