#include <stdlib.h>

#include <X11/Xlib.h>
#include <X11/XWDFile.h>

// Code mostly copied from xwd source code

typedef unsigned long Pixel;
#define lowbit(x) ((x) & (~(x) + 1))

#include "image.h"

void XGetHeader(XWindowAttributes *win_info, XImage *image, int ncolors, XWDFileHeader *header, int headerSize) {
	unsigned long swaptest = 1;
	
	header->header_size = (CARD32)headerSize;
	header->file_version = (CARD32)XWD_FILE_VERSION;
	header->pixmap_format = (CARD32)image->format;
	header->pixmap_depth = (CARD32)image->depth;
	header->pixmap_width = (CARD32)image->width;
	header->pixmap_height = (CARD32)image->height;
	header->xoffset = (CARD32)image->xoffset;
	header->byte_order = (CARD32)image->byte_order;
	header->bitmap_unit = (CARD32)image->bitmap_unit;
	header->bitmap_bit_order = (CARD32)image->bitmap_bit_order;
	header->bitmap_pad = (CARD32)image->bitmap_pad;
	header->bits_per_pixel = (CARD32)image->bits_per_pixel;
	header->bytes_per_line = (CARD32)image->bytes_per_line;
	header->visual_class = (CARD32)win_info->visual->class;
	header->red_mask = (CARD32)win_info->visual->red_mask;
	header->green_mask = (CARD32)win_info->visual->green_mask;
	header->blue_mask = (CARD32)win_info->visual->blue_mask;
	header->bits_per_rgb = (CARD32)win_info->visual->bits_per_rgb;
	header->colormap_entries = (CARD32)win_info->visual->map_entries;
	header->ncolors = ncolors;
	header->window_width = (CARD32)win_info->width;
	header->window_height = (CARD32)win_info->height;
	header->window_x = 0;
	header->window_y = 0;
	header->window_bdrwidth = (CARD32)win_info->border_width;
	
	if (*(char *)&swaptest) {
		_swaplong((char *)header, sizeof(*header));
	}
	
	((char *)header)[headerSize / sizeof(char) - 1] = '\0'; // Empty window filename
}

int XReadColors(Display *display, XWindowAttributes *win_info, XColor **colors) {
	int i;
	unsigned long swaptest = 1;
	
	Colormap cmap = win_info->colormap;
	if (!cmap) return 0;
	
	Visual *vis = win_info->visual;
	int ncolors = vis->map_entries;

	if ((*colors = (XColor *)malloc(sizeof(XColor) * ncolors)) == NULL) {
		return -1;
	}
	
	if (vis->class == DirectColor || vis->class == TrueColor) {
		Pixel red, green, blue, red1, green1, blue1;
	
		red = green = blue = 0;
		red1 = lowbit(vis->red_mask);
		green1 = lowbit(vis->green_mask);
		blue1 = lowbit(vis->blue_mask);
		
		for (i = 0; i < ncolors; i++) {
			(*colors)[i].pixel = red | green | blue;
			(*colors)[i].pad = 0;
			red += red1;
			if (red > vis->red_mask) red = 0;
			green += green1;
			if (green > vis->green_mask) green = 0;
			blue += blue1;
			if (blue > vis->blue_mask) blue = 0;
		}
	}
	else {
		for (i = 0; i < ncolors; i++) {
			(*colors)[i].pixel = i;
			(*colors)[i].pad = 0;
		}
	}

	XQueryColors(display, cmap, *colors, ncolors);
	
	if (*(char *)&swaptest) {
		for (i = 0; i < ncolors; i++) {
			_swaplong((char *)&(*colors)[i].pixel, sizeof(CARD32));
			_swapshort((char *)&(*colors)[i].red, 3 * sizeof(short));
		}
	}
	
	return ncolors;
}

void XGetXWDColor(XColor **colors, XWDColor *xwdColor, int i) {
	xwdColor->pixel = (*colors)[i].pixel;
	xwdColor->red = (*colors)[i].red;
	xwdColor->green = (*colors)[i].green;
	xwdColor->blue = (*colors)[i].blue;
	xwdColor->flags = (*colors)[i].flags;
}

void XFreeReadColors(XColor **colors) {
	free(*colors);
	*colors = NULL;
}

void _swapshort(register char *bp, register unsigned n) {
    register char c;
    register char *ep = bp + n;

    while (bp < ep) {
		c = *bp;
		*bp = *(bp + 1);
		bp++;
		*bp++ = c;
    }
}

void _swaplong(register char *bp, register unsigned n) {
	register char c;
	register char *ep = bp + n;

	while (bp < ep) {
		c = bp[3];
		bp[3] = bp[0];
		bp[0] = c;
		c = bp[2];
		bp[2] = bp[1];
		bp[1] = c;
		bp += 4;
	}
}
