/*
 * @(#)picture.c
 *
 * Taken from xlock, many authors...
 *
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and
 * its documentation for any purpose and without fee is hereby granted,
 * provided that the above copyright notice appear in all copies and
 * that both that copyright notice and this permission notice appear in
 * supporting documentation, and that the name of the author not be
 * used in advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.
 *
 * 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.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/Intrinsic.h>
#include <X11/Xutil.h>
#include "file.h"
#include "picture.h"

#if defined(__cplusplus) || defined(c_plusplus)
#define CLASS c_class
#else
#define CLASS class
#endif

Bool
fixedColors(Visual *visual, int depth, Bool install)
{
#ifdef FORCEFIXEDCOLORS
	/* pretending a fixed colourmap */
	return TRUE;
#else
	/* get information about the default visual */
	return (!(depth > 1 && install &&
		(visual->CLASS != StaticGray) &&
		(visual->CLASS != StaticColor) &&
		(visual->CLASS != TrueColor)));
#endif
}

static int
XbmReadFileToImage(char *filename,
		int *width, int *height, char **bits)
{
	FILE *file;
	int c, c1;
	int i, j, k = 0;
	char *pix;
	char line[256], name[256];
	unsigned char hex[256];

	if ((file = fopen(filename, "r")) == NULL) {
		/*(void) fprintf(stderr, "could not read file \"%s\"\n", filename); */
		return BitmapOpenFailed;
	}
	/* read width: skip lines until we hit a #define */
	for (;;) {
		if (!fgets(line, 256, file)) {
			/* not a xbm file */
			(void) fclose(file);
			return BitmapFileInvalid;
		}
		if (strncmp(line, "#define", (size_t) 7) == 0 && sscanf(line,
				"#define %s %d", name, width) == 2 &&
				strcmp(name, "_width"))
			break;
	}

	/* read height: skip lines until we hit another #define */
	for (;;) {
		if (!fgets(line, 256, file)) {
			(void) fclose(file);
			(void) fprintf(stderr, "EOF reached in header info.\n");
			return BitmapFileInvalid;
		}
		if (strncmp(line, "#define", (size_t) 7) == 0 && sscanf(line,
				"#define %s %d", name, height) == 2 &&
				strcmp(name, "_height"))
			break;
	}
	/* scan forward until we see the first '0x' */
	c = getc(file);
	c1 = getc(file);
	while (c1 != EOF && !(c == '0' && c1 == 'x')) {
		c = c1;
		c1 = getc(file);
	}
	if (c1 == EOF) {
		(void) fclose(file);
		(void) fprintf(stderr, "No bitmap data found\n");
		return BitmapFileInvalid;
	}
	if (*width < 1 || *height < 1 || *width > 10000 || *height > 10000) {
		(void) fclose(file);
		(void) fprintf(stderr, "Not an xbm file");
		return BitmapFileInvalid;
	}
	*bits = (char *) calloc((size_t) ((*width + 7) / 8) * (*height),
		(size_t) 8);
	if (!*bits) {
		(void) fclose(file);
		(void) fprintf(stderr, "Could not malloc bits\n");
		return BitmapNoMemory;
	}
	/* initialize the 'hex' array for zippy ASCII-hex -> int conversion */

	for (i = 0; i < 256; i++)
		hex[i] = 255;	/* flag 'undefined' chars */
	for (i = '0'; i <= '9'; i++)
		hex[i] = i - '0';
	for (i = 'a'; i <= 'f'; i++)
		hex[i] = i + 10 - 'a';
	for (i = 'A'; i <= 'F'; i++)
		hex[i] = i + 10 - 'A';

	/* read the image data */

	for (i = 0, pix = *bits; i < *height; i++)
		for (j = 0; j < (*width + 7) / 8; j++, pix++) {
			/* Get next byte from file. We are already positioned at it */
			c = getc(file);
			c1 = getc(file);
			if (c < 0 || c1 < 0) {
				/* EOF: break out of loop */
				c = c1 = '0';
				i = *height;
				j = *width;
				(void) fclose(file);
				(void) fprintf(stderr,
					"The file would appear to be truncated.\n");
				return BitmapFileInvalid;
			}
			if (hex[c1] == 255) {
				if (hex[c] == 255)
					k = 0;	/* no digits after the '0x' ... */
				else
					k = hex[c];
			} else
				k = (hex[c] << 4) + hex[c1];

			/* advance to next '0x' */
			c = getc(file);
			c1 = getc(file);
			while (c1 != EOF && !(c == '0' && c1 == 'x')) {
				c = c1;
				c1 = getc(file);
			}
			*pix = k;
		}
	(void) fclose(file);
	return BitmapSuccess;
}
#ifdef HAVE_XPM
#include <X11/xpm.h>
#endif

static XImage bpicture =
{
	0, 0,			/* width, height */
	0, XYBitmap, 0,		/* xoffset, format, data */
	LSBFirst, 8,		/* byte-order, bitmap-unit */
	LSBFirst, 8, 1		/* bitmap-bit-order, bitmap-pad, depth */
};

Bool
getImage(Display *display, Window window, Visual *visual, Colormap colormap,
	int depth, XImage **picture, char *filename, Bool install,
	int *graphicsFormat, Colormap *ncm)
{
	static char *bitmap_local = (char *) NULL;
	Bool rtn = True;

#ifdef HAVE_XPM
	XpmAttributes attrib;

#endif
	if (!fixedColors(visual, depth, install)) {
		*ncm = XCreateColormap(display, window, visual, AllocNone);
	} else {
		*ncm = None;
	}
#ifdef HAVE_XPM
	attrib.visual = visual;
	if (*ncm == None) {
		attrib.colormap = colormap;
	} else {
		attrib.colormap = *ncm;
	}
	attrib.depth = depth;
	attrib.closeness = 40000;
	attrib.valuemask = XpmVisual | XpmColormap | XpmDepth | XpmCloseness;
#endif
	*graphicsFormat = 0;

	if (bitmap_local != NULL) {
		free(bitmap_local);
		bitmap_local = (char *) NULL;
	}
	if (filename && strlen(filename)) {
		if ((bitmap_local = (char *) malloc(256)) == NULL) {
			(void) fprintf(stderr , "no memory for \"%s\"\n" ,
				filename);
			return False;
		}
		(void) strncpy(bitmap_local, filename, 256);
	}
	bitmap_local = findFile(bitmap_local);
	if (bitmap_local && strlen(bitmap_local)) {
#ifdef HAVE_XPM
#ifndef USE_MONOXPM
		if (depth > 1)
#endif
		{
			if (*graphicsFormat <= 0) {
				if (XpmSuccess == XpmReadFileToImage(display,
						bitmap_local, picture,
						(XImage **) NULL, &attrib))
					*graphicsFormat = IS_XPMFILE;
			}
		}
#endif
		if (*graphicsFormat <= 0) {
			if (!bpicture.data) {
				if (BitmapSuccess == XbmReadFileToImage(findFile(bitmap_local),
						&bpicture.width, &bpicture.height,
						&bpicture.data)) {
					bpicture.bytes_per_line = (bpicture.width + 7) / 8;
					*graphicsFormat = IS_XBMFILE;
					*picture = &bpicture;
				}
			} else {
				*graphicsFormat = IS_XBMDONE;
				*picture = &bpicture;
			}
		}
		if (*graphicsFormat <= 0) {
			(void) fprintf(stderr,
				"\"%s\" is in an unrecognized format or not compatible with screen\n",
				bitmap_local);
			rtn = False;
		}
		if (bitmap_local != NULL) {
			free(bitmap_local);
			bitmap_local = (char *) NULL;
		}
	}
	return rtn;
}

void
destroyImage(XImage **picture, int *graphicsFormat)
{
	switch (*graphicsFormat) {
	case IS_XBM:
		bpicture.data = (char *) NULL;
		break;
	case IS_XBMFILE:
		if (bpicture.data) {
			free(bpicture.data);
			bpicture.data = (char *) NULL;
		}
		break;
	case IS_XPM:
	case IS_XPMFILE:
	case IS_RASTERFILE:
	case IS_MAGICKFILE:
		if (picture && *picture)
			(void) XDestroyImage(*picture);
		break;
	}
	*graphicsFormat = -1;
	*picture = (XImage *) NULL;
}

#if 0
static void
pickPixmap(Display * display, Drawable drawable, char *name,
	int default_width, int default_height, unsigned char *default_bits,
	int *width, int *height, Pixmap * pixmap,
	int *graphicsFormat) {
	int x_hot, y_hot; /* dummy */

	name = findFile(name);
	if (name && *name) {
		if (BitmapSuccess == XReadBitmapFile(display, drawable, name,
				(unsigned int *) width,
				(unsigned int *) height,
				pixmap, &x_hot, &y_hot)) {
			*graphicsFormat = IS_XBMFILE;
		}
		if (*graphicsFormat <= 0)
			(void) fprintf(stderr,
				"\"%s\" not xbm format\n", name);
	}
	if (*graphicsFormat <= 0) {
		*width = default_width;
		*height = default_height;
		*graphicsFormat = IS_XBM;
		*pixmap = XCreateBitmapFromData(display, drawable,
			(char *) default_bits, *width, *height);
	}
}

static void
getPixmap(Display *display, Drawable drawable, char *filename,
	int default_width, int default_height, unsigned char *default_bits,
	int *width, int *height, Pixmap * pixmap, int *graphicsFormat)
{
	static char *bitmap_local = (char *) NULL;

	if (bitmap_local) {
		free(bitmap_local);
		bitmap_local = (char *) NULL;
	}
	if (filename && strlen(filename)) {
		if ((bitmap_local = (char *) malloc(256)) == NULL) {
			(void) fprintf(stderr , "no memory for \"%s\"\n" ,
				filename);
			return;
		}
		(void) strncpy(bitmap_local, filename, 256);
	}
	pickPixmap(display, drawable, bitmap_local,
		default_width, default_height, default_bits,
		width, height, pixmap, graphicsFormat);
}
#endif
