/*******************************************************************************
 * Copyright (c) 2000, 2003 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials 
 * are made available under the terms of the Common Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/

#include "NgCommon.h"
#include "NgImageData.h"
#include "NgImage.h"


/**
 * Return the nbr of entries in the default color palette
 */
int getNbrColorsXPalette(Display *xDisplay)
{
	Visual *visual = XDefaultVisual (xDisplay, XDefaultScreen(xDisplay));
	return visual->map_entries;
}

/**
 * Return the RGB codes of the default palette
 * palette: buffer of size numColors * 3, holding the RGB values
 */
ng_err_t getXPalette (Display *xDisplay, int numColors, char* palette)
{
	XColor color;
	int i;
	int index = 0;
	int colormap = XDefaultColormap (xDisplay, XDefaultScreen(xDisplay));
	for (i = 0; i < numColors; i++)
	{
		color.pixel = i;
		XQueryColor (xDisplay, colormap, &color);
		palette[index++] = ((color.red >> 8) & 0xFF);
		palette[index++] = ((color.green >> 8) & 0xFF);
		palette[index++] = ((color.blue >> 8) & 0xFF);
	}
	return ERR_OK;
}

/**
 * Put a device-independent image of any depth into a drawable of the same size, 
 */
ng_err_t putImage(ng_bitmap_image_t *image, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, 
	Display *xDisplay, Visual *visual, int screenDepth, 
	int drawable) 
{

	XImage *xImagePtr;
	int bufSize;
	int destRedMask = 0, destGreenMask = 0, destBlueMask = 0;
	BYTE1 screenDirect;
	UBYTE1 *srcData = NgBitmapImageImageData(image);
	UBYTE4 srcDepth = NgBitmapImageBitCount(image);
	BYTE4 sbpp, dbpp;
	GC tempGC;
	int numColors;
	
	/* We only support image depth of 24 bits */
	if (srcDepth != 24) return NgError (ERR_NG, "Error unsupported depth - only support 24 bit");
	if (screenDepth <= 8) 
	{
		numColors = getNbrColorsXPalette (xDisplay);
		if (numColors == 0)
			return NgError (ERR_NG, "Error pseudo-color mode detected, no colors available");
		screenDirect = 0;
	} else
	{
		destRedMask = visual->red_mask;
		destGreenMask = visual->green_mask;
		destBlueMask = visual->blue_mask;
		screenDirect = 1;
	}
	
	xImagePtr = XCreateImage(xDisplay, visual, screenDepth, ZPixmap, 0, 0, srcWidth, srcHeight, 32, 0);
	if (xImagePtr == NULL) return NgError (ERR_NG, "Error XCreateImage failed");
	bufSize = xImagePtr->bytes_per_line * srcHeight;

	xImagePtr->data = (char*) XtMalloc (bufSize);
	sbpp = NgBitmapImageBytesPerRow(image);
	dbpp = xImagePtr->bytes_per_line;
	
	if (screenDirect)
	{
		/* 24 bit source to direct screen destination */
		NgBitmapImageBlitDirectToDirect(srcData, sbpp, srcWidth, srcHeight,
			(UBYTE1*)xImagePtr->data, xImagePtr->bits_per_pixel, dbpp, 0,
			destRedMask, destGreenMask, destBlueMask);
	} else
	{
		/* 24 bit source to palette screen destination */
		char *palette = (char*) NgMalloc (numColors * 3);
		getXPalette (xDisplay, numColors, palette);
		NgBitmapImageBlitDirectToPalette(srcData, sbpp, srcWidth, srcHeight,
			(UBYTE1*)xImagePtr->data, xImagePtr->bits_per_pixel, dbpp, 0,
			(UBYTE1*)palette, numColors);
		NgFree (palette);
	}
	
	tempGC = XCreateGC (xDisplay, drawable, 0, NULL);	
	XPutImage(xDisplay, drawable, tempGC, xImagePtr, 0, 0, 0, 0, srcWidth, srcHeight);
	
	XDestroyImage (xImagePtr);
	XFreeGC (xDisplay, tempGC);
	return ERR_OK;
}

ng_err_t init(ng_bitmap_image_t *image, Display *xDisplay, int screenDepth, int drawable, Pixmap *pixmap) 
{
	ng_err_t err;
	int width = (int)NgBitmapImageWidth(image);
	int height = (int)NgBitmapImageHeight(image);
	
	Visual *visual = XDefaultVisual(xDisplay, XDefaultScreen(xDisplay));
	*pixmap = XCreatePixmap(xDisplay, drawable, width, height, screenDepth);
	if (*pixmap == 0) 
	{
		return NgError (ERR_NG, "Error XCreatePixmap failed");
	}
	err = putImage(image, 0, 0, width, height, 0, 0, xDisplay, visual, screenDepth, *pixmap);
	if (err != ERR_OK) 
	{
		XFreePixmap (xDisplay, *pixmap);
		return NgError (err, "Error putImage failed");
	}
	
	return ERR_OK;
}

/**
 * loadBMPImage
 * Create a pixmap representing the given BMP file, for the specified display and screen.
 *
 * display: connection to X server
 * screen: the screen to create the pixmap for
 * bmpPathname: absolute path and name to the bmp file
 *
 * returned value: the pixmap newly created if successful. 0 otherwise.
 */
Pixmap loadBMPImage (Display *display, Screen *screen, char *bmpPathname) {
	Window drawable = XDefaultRootWindow (display);
	ng_stream_t in;
	ng_bitmap_image_t image;
	ng_err_t err = ERR_OK;
	int screenDepth = XDefaultDepthOfScreen (screen);
	Pixmap pixmap;

	NgInit();
		
	if (NgStreamInit (&in, bmpPathname) != ERR_OK)
	{
		NgError (ERR_NG, "Error can't open BMP file");
		return 0;
	}	
	NgBitmapImageInit (&image);
	err = NgBmpDecoderReadImage (&in, &image);
	NgStreamClose (&in);
	
	if (err != ERR_OK)
	{
		NgBitmapImageFree (&image);
		return 0;
	}
		
	err = init (&image, display, screenDepth, drawable, &pixmap);
	NgBitmapImageFree (&image);
	
	return err == ERR_OK ? pixmap : 0;
}

const char *getBMPErrorMessage ()
{
	return NgGetLastErrorMsg ();
}
