//	Zinc Application Framework - D_IMAGE.CPP
//	COPYRIGHT (C) 1990-1999.  All Rights Reserved.
//	Zinc Software Incorporated.  Pleasant Grove, Utah  USA

#include <z_image.hpp>
#include <z_string.hpp>
#include <z_utils.hpp>

#if defined(ZAF_CURSES) || defined(ZAF_TEXT_ONLY)
void ZafImage::DestroyImageHandle(void) {}
ZafEventType ZafImage::Draw(const ZafEventStruct &, ZafEventType) { return (S_CONTINUE); }
bool ZafImage::LoadImageFromApplication(void) { return (false); }
bool ZafImage::LoadImageFromFile(void) { return (false); }
void ZafImage::OSMapPalette(ZafPaletteStruct &, ZafPaletteType, ZafPaletteState) {}
ZafEventType ZafImage::Event(const ZafEventStruct &event) { return (ZafWindowObject::Event(event)); }
ZafEventType ZafImage::OSEvent(const ZafEventStruct &) { return (S_CONTINUE); }
void ZafImage::OSRegisterObject(void) { ZafWindowObject::OSRegisterObject(); }
#else
#	if defined(ZAF_METAWINDOW)
extern "C"
{
#	include <metawndo.h>
#	include <pcxlab.h>
}

long xTable[256];      /* color translation table */
int XlatePCX(char *filename, long *xltable, void **imageBuffer);
#	endif

// --------------------------------------------------------------------------
// ----- ZafImage ----------------------------------------------------------
// --------------------------------------------------------------------------

void ZafImage::DestroyImageHandle(void)
{
#if defined(ZAF_METAWINDOW)
	if (imageBuffer)
		DestroyBitmap((grafPort *)imageBuffer);
	imageBuffer = ZAF_NULLP(OSImageID);
#endif
}

#if defined(ZAF_DEFAULT_DRAW)
#	if defined(ZAF_METAWINDOW)
ZafEventType ZafImage::Draw(const ZafEventStruct &event, ZafEventType ccode)
#	else
ZafEventType ZafImage::Draw(const ZafEventStruct &, ZafEventType ccode)
#	endif
{
#if defined(ZAF_METAWINDOW)
	if (event.type == S_NON_CURRENT)
		return (true);

	// Compute the actual draw region.
	ZafRegionStruct drawRegion = BeginDraw();

	ZafRegionStruct tRegion;

	ZafRegionStruct clipRegion;
	event.region.Overlap(drawRegion, clipRegion);

	// Draw the border if necessary.
	if (Bordered())
		DrawBorder(drawRegion, ccode);
	if (!imageBuffer)
	{
		DrawBackground(drawRegion, ccode);
		EndDraw();
		return (ccode);
	}

#if defined(ZAF_UNICODE)
	char *mapped = zafCodeSet->ConvertToOSString(pathName);
#	define Mapped mapped
#else
#	define Mapped pathName
#endif

	// Shift region to screen coordinates since display's screenList is in
	// screen coordinates.
	ZafRegionStruct screenRegion = drawRegion;

	// Draw the image on the display.
	for (ZafRegionElement *dRegion = ((ZafScreenDisplay *)display)->drawRegionList.First(); dRegion; dRegion = dRegion->Next())
		if (dRegion->region.Overlap(screenRegion, tRegion))
		{
			ZafRegionStruct tClip;
			tClip.left = ZafMax(clipRegion.left, tRegion.left);
			tClip.top = ZafMax(clipRegion.top, tRegion.top);
			tClip.right = ZafMin(clipRegion.right, tRegion.right);
			tClip.bottom = ZafMin(clipRegion.bottom, tRegion.bottom);
			tClip.coordinateType = clipRegion.coordinateType;

			display->SetClipRegion(tClip);

			// Draw the image.
			int left = drawRegion.left;
			int top = drawRegion.top;
			int drawWidth = zafRegion.Width();
			int drawHeight = zafRegion.Height();

			rect drawRect, srcRect;
			SetRect(&srcRect, 0, 0, width, height);
			grafPort *screenPort;
			GetPort(&screenPort);

			if (scaled)
         	{
				// Scale directly to screen (draws slowly).
            	rect dstRect;
            	SetRect(&dstRect, left, top, left + drawWidth, top + drawHeight);
            	ZoomBlit((grafPort *)imageBuffer, screenPort, &srcRect, &dstRect);

/* START BLOCK COMMENT
**				// Scale to memory and then blast to screen (nothing happens as 
**				// scale is performed, but then draws quickly).
**	            rect dstRect;
**	            SetRect(&dstRect, 0, 0, drawWidth, drawHeight);
**				grafPort *memPort = CreateBitmap(cMEMORY, drawWidth, drawHeight);
**	            ZoomBlit((grafPort *)imageBuffer, memPort, &srcRect, &dstRect);
**	            SetRect(&drawRect, left, top, left + drawWidth, top + drawHeight);
**				CopyBlit(memPort, screenPort, &dstRect, &drawRect);
**				SetPort(screenPort);
**				DestroyBitmap(memPort);
END BLOCK COMMENT */
         	}
			else if (tiled)
			{
				for (int y = top; y < left + drawHeight; y += height)
					for (int x = left; x < top + drawWidth; x += width)
					{
						SetRect(&drawRect, x, y, x + width, y + height);
						CopyBlit((grafPort *)imageBuffer, screenPort, &srcRect, &drawRect);
					}
			}
			else
			{
				SetRect(&drawRect, left, top, left + width, top + height);
				CopyBlit((grafPort *)imageBuffer, screenPort, &srcRect, &drawRect);
			}
		}
#if defined(ZAF_UNICODE)
	delete []mapped;
#endif

	EndDraw();
#else
	ZafRegionStruct drawRegion = BeginDraw();
	if (Bordered())
		DrawBorder(drawRegion, ccode);
	DrawBackground(drawRegion, ccode);
	EndDraw();
#endif
	return (ccode);
}
#endif

bool ZafImage::LoadImageFromApplication(void)
{
	return (false);
}

bool ZafImage::LoadImageFromFile(void)
{
#if defined(ZAF_METAWINDOW)
	if (!pathName || !pathName[0])
		return (false);

	// Open the file.
	ZafIChar _pathName[ZAF_MAXPATHLEN];
	strcpy(_pathName, pathName);
	ZafDiskFileSystem::ChangeExtension(_pathName, ZAF_ITEXT(".pcx"));
	ZafDiskFile file(_pathName, ZAF_FILE_READ | ZAF_FILE_BINARY);

	if (file.Error())
		return (false);

	if (pathName)
		delete []pathName;
	pathName = strdup(_pathName);
	pcxHead pcxHdr;

	// Translate PCX palette colors to closest match on hardware palette.
	palData RGBInfo[256];
	palData RsvdRGBInfo[16];  // Buffer for holding reserved RGB values.

#if defined(ZAF_UNICODE)
	char *mapped = zafCodeSet->ConvertToOSString(_pathName);
	int success = ReadPCXInfo(mapped, &pcxHdr, RGBInfo);
	delete []mapped;
#else
	int success = ReadPCXInfo(_pathName, &pcxHdr, RGBInfo);
#endif

	if (!success)
		return (false);

   // Record the pcx palette entries that are in reserved locations.
   int i;
   for(i = 0; i < 16; i++ ) 
		RsvdRGBInfo[i] = RGBInfo[i];

	// Replace the reserved palette data with the current palette settings.
	ReadPalette( 0, 0, 15, RGBInfo );
//	ReadPalette(0, (int)QueryColors(), (int)QueryColors(), &RGBInfo[(int)QueryColors()]);

	// Set palette, based on palette in PCX file.
	WritePalette( 0, 0, (int) QueryColors(), RGBInfo );

	// start with a 1:1 translation table
	for( i = 0; i < 256; i ++ ) 
    	xTable[i] = (long) i;

	// Translate the reserved colors of the pcx file to others
	// in the current displays palette.
	for( i = 0; i < 16; i++ ) 
    	xTable[i] = (long) FindClosestRGB( &RsvdRGBInfo[i], RGBInfo );

#if defined(ZAF_UNICODE)
	mapped = zafCodeSet->ConvertToOSString(pathName);
	if (XlatePCX(mapped, xTable, &imageBuffer) < 0)
	{
		delete []mapped;
		return (false);
	}
	delete []mapped;
#else
	if (XlatePCX(pathName, xTable, &imageBuffer) < 0)
		return (false);
#endif
	width = pcxHdr.pcxWndo.Xmax - pcxHdr.pcxWndo.Xmin + 1;
	height = pcxHdr.pcxWndo.Ymax - pcxHdr.pcxWndo.Ymin + 1;
#endif
	
	return (true);
}

ZafEventType ZafImage::Event(const ZafEventStruct &event)
{
	ZafEventType ccode = LogicalEvent(event);

	switch (ccode)
	{
	case S_COMPUTE_SIZE:
		ZafWindowObject::Event(event);
		if (AutoSize())
		{
			zafRegion.right = zafRegion.left + width - 1;
			zafRegion.bottom = zafRegion.top + height - 1;
			if (Bordered())
			{
				zafRegion.right += ZAF_BORDER_SIZE * 2;
				zafRegion.bottom += ZAF_BORDER_SIZE * 2;
			}
		}
		break;

	default:
		ccode = ZafWindowObject::Event(event);
		break;
	}
	return (ccode);
}

ZafError ZafImage::SetPathID(int )
{
	return (ZAF_ERROR_NONE);
}

ZafError ZafImage::SetPathName(const ZafIChar *newPathName)
{
	// Reset the path name.
	if (pathName)
		delete pathName;
	pathName = newPathName ? strdup(newPathName) : ZAF_NULLP(ZafIChar);

	// Make sure image gets resized.
	if (!newPathName || *newPathName == 0)
		width = height = 0;

	// Re-read the image information.
	DestroyImageHandle();

	// Level 1 - Load the image from the application resources.
	if (!LoadImageFromApplication())

	// Level 2 - Load the image from the native file.
		if (!LoadImageFromFile())
			error = ZAF_ERROR_INVALID_NAME;

	// Return the error status.
	return (Error());
}

ZafEventType ZafImage::OSEvent(const ZafEventStruct &event)
{
	ZafEventType ccode = event.type;
	return (ccode);
}

void ZafImage::OSRegisterObject(void)
{
	ZafWindowObject::OSRegisterObject();
}

#if defined(ZAF_METAWINDOW)
/*
XlatePCX: read and translate a pcx file to be compatible with current display
          format and translate colors per xltable.  This function adapted
          from a MetaWINDOW example program.

Returns;
    1 = file read correctly
    0 = error reading PCX file
   -1 = not a valid pcx file
   -2 = can't allocate image buffer
   -3 = can't allocate translation buffer

*/

int XlatePCX(char  *filename, long *xltable, void **imageBuffer)

{
	int      i, height, width, y;
	pcxHead  pcxHeader;        // Buffer for reading the files PCX header.
	unsigned imsize;           // Size of required image buffer.
	image    *srcImage;        // Pointer to allocated source image buffer.
	image    *dstImage;        // Pointer to allocated dest image buffer.
	grafPort *myport;          // Pointer to current port.
	rect     dstR;             // Rectangle used to WriteImage() raster.
//	rect     destRect;

//	DupRect(&dstR, &destRect);

	// Open the PCX File and read header information.
	imsize = OpenPCX(filename, &pcxHeader, NULL);
	if( imsize == 0)
    	return (-1);     // Not a valid pcx file.

	GetPort(&myport);

	// File needs to be color translated.
	// Each raster will be processed individually.

	// Allocate an image buffer large enough for one raster of the pcx file.
	srcImage = new unsigned char[imsize];
	if(srcImage == NULL)
	{
    	ClosePCX(&pcxHeader);
    	return (-2);    // Can't allocate.
	}

   // Calculate a translation image buffer size based on source image.

   // Compute number of rasters in the pcx file.
   height = pcxHeader.pcxWndo.Ymax - pcxHeader.pcxWndo.Ymin + 1;
   width = pcxHeader.pcxWndo.Xmax - pcxHeader.pcxWndo.Xmin + 1;

	// Get a 'taste' of the pcx files image format.
	NextPCX(&pcxHeader, srcImage);
	imsize  = (unsigned) XlateImage(srcImage, NULL, myport->portMap->pixBits,
                                	myport->portMap->pixPlanes, NULL); 
	dstImage = new unsigned char[imsize];
	if(dstImage == NULL)
	{
    	delete []srcImage;
    	ClosePCX(&pcxHeader);
    	return (-3);     // Can't allocate.
	}

	// Reset file to beginning.
	ClosePCX(&pcxHeader);
	OpenPCX(filename, &pcxHeader, NULL);

	grafPort *screenPort;
	GetPort(&screenPort);
	*imageBuffer = CreateBitmap(cMEMORY, width, height);
	rect scrnR;
	ScreenRect(&scrnR);
	ClipRect(&scrnR);

   // Draw something in the reserved colors.
/* START BLOCK COMMENT
**	   MoveTo(10, scrnR.Ymax - 50);
**	   for(int r = 0; r < 16; r++)
**		{
**	      PenColor(r);
**	      DrawChar('A' + r);
**	   }
END BLOCK COMMENT */

	// Set up a rectangle for the destination WriteImage().
	ScreenRect(&dstR);
	dstR.Ymax = dstR.Ymin + 1;

	// Process each pcx file raster individually.
	for(y = 0; y < height; y++)
	{
    	// Read next raster into an image buffer.
    	i = NextPCX(&pcxHeader, srcImage);

    	if(i == False)
		{
        	ClosePCX(&pcxHeader);
        	delete []srcImage;
        	delete []dstImage;
        	return (0);      // Error reading.
    	}

    	// Translate the image to destination format and colors.
    	XlateImage(srcImage, dstImage, myport->portMap->pixBits,
                	myport->portMap->pixPlanes, xltable); 
    
    	// Put the translated image on the current bitmap.
    	WriteImage(&dstR, dstImage);

    	// Advance destination rectangle to next raster.
    	OffsetRect(&dstR, 0, 1);

	} // for Y

	SetPort(screenPort);

	// Close the PCX file opened via OpenPCX.
	ClosePCX(&pcxHeader);

	// Free the image buffers.
	delete []srcImage;
	delete []dstImage;

	return (1);
}
#endif
#endif
