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

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

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

void ZafImage::DestroyImageHandle(void)
{
	// Destroy the image handle.
	if (imageBuffer.hBitmap)
		DeleteObject(imageBuffer.hBitmap);
	if (imageBuffer.hPalette)
		DeleteObject(imageBuffer.hPalette);
	imageBuffer.hBitmap = 0;
}

#if defined(ZAF_DEFAULT_DRAW)
ZafEventType ZafImage::Draw(const ZafEventStruct &, ZafEventType ccode)
{
	// Begin the drawing operation.
	ZafRegionStruct drawRegion = supportObject ? parent->BeginDraw() : BeginDraw();

	// Draw the border if necessary.
	if (bordered)
		DrawBorder(drawRegion, ccode);
	else if (editMode)
	{
		display->SetLineStyle(ZAF_LINE_DOTTED);
		display->SetForeground(ZAF_CLR_BLACK);
		display->Rectangle(drawRegion);
	}

	if (!imageBuffer.hBitmap)
	{
		DrawBackground(drawRegion, ccode);
		supportObject ? parent->EndDraw() : EndDraw();
		return (S_ERROR);
	}

	if (wallpaper && !scaled && !tiled)
	{
		ZafRegionStruct imageRegion;
		imageRegion.coordinateType = ZAF_PIXEL;
		imageRegion.left = drawRegion.left + (drawRegion.Width() - width) / 2;
		imageRegion.right = imageRegion.left + width - 1;
		imageRegion.top = drawRegion.top + (drawRegion.Height() - height) / 2;
		imageRegion.bottom = imageRegion.top + height - 1;

		ZafRegionStruct backgroundRegion = drawRegion;
		backgroundRegion.bottom = imageRegion.top - 1;
		DrawBackground(backgroundRegion, ccode);
		backgroundRegion.top = imageRegion.bottom + 1;
		backgroundRegion.bottom = drawRegion.bottom;
		DrawBackground(backgroundRegion, ccode);
		backgroundRegion.right = imageRegion.left - 1;
		backgroundRegion.top = imageRegion.top;
		backgroundRegion.bottom = imageRegion.bottom;
		DrawBackground(backgroundRegion, ccode);
		backgroundRegion.left = imageRegion.right + 1;
		backgroundRegion.right = drawRegion.right;
		DrawBackground(backgroundRegion, ccode);

		drawRegion = imageRegion;
	}

	if (ccode == S_REDISPLAY || S_REDISPLAY_REGION)
	{
		// Get device context handle and set the color palettes.
		HDC hDC = display->DisplayContext();
		HDC hMemDC = CreateCompatibleDC(hDC);
		if (imageBuffer.hPalette)
		{
			SelectPalette(hDC, imageBuffer.hPalette, TRUE);
			SelectPalette(hMemDC, imageBuffer.hPalette, TRUE);
			RealizePalette(hDC);
		}

		// Draw the bitmap.
		int left = drawRegion.left;
		int top = drawRegion.top;
		SelectObject(hMemDC, imageBuffer.hBitmap);
		if (Scaled())
			StretchBlt(hDC, left, top, drawRegion.Width(), drawRegion.Height(), hMemDC, 0, 0, width, height, SRCCOPY);
		else if (Tiled())
		{
			for (int y = top; y < top + drawRegion.Height(); y += height)
				for (int x = left; x < left + drawRegion.Width(); x += width)
				{
					int sizeX = ((x + width) < drawRegion.Width()) ? width : drawRegion.Width() - x + 1;
					int sizeY = ((y + height) < drawRegion.Height()) ? height : drawRegion.Height() - y + 1;
					BitBlt(hDC, x, y, sizeX, sizeY, hMemDC, 0, 0, SRCCOPY);
				}
		}
		else
			BitBlt(hDC, left, top, ZafMin(width,drawRegion.Width()), ZafMin(height, drawRegion.Height()), hMemDC, 0, 0, SRCCOPY);

		// Clean up.
		DeleteDC(hMemDC);
	}

	// End the drawing operation.
	SupportObject() ? parent->EndDraw() : EndDraw();
	return (ccode);
}
#endif

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

	if (ccode == E_OSEVENT)
		return ZafImage::OSEvent(event);
	else switch (ccode)
	{
	case S_COMPUTE_SIZE:
		ccode = ZafWindowObject::Event(event);
		if (autoSize)
		{
			zafRegion.right = zafRegion.left + width - 1;
			zafRegion.bottom = zafRegion.top + height - 1;
		}
		break;

	case N_SIZE:
		if (scaled || (wallpaper && !tiled))
			Redisplay();
		break;

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

	return ccode;
}

bool ZafImage::LoadImageFromApplication(void)
{
	// Try to load the bitmap from the application.
	HRSRC hRsrc;
	if (ZafMSWindowsApp::convertText)
	{
		char *osPathName = zafCodeSet->ConvertToOSString(pathName, ZAF_NULLP(char), false);
#if defined(ZAF_WIN32) && defined(ZAF_UNICODE)
    	hRsrc = FindResourceA(ZafMSWindowsApp::hInstance, osPathName, (LPCSTR)RT_BITMAP);
#else
    	hRsrc = FindResource(ZafMSWindowsApp::hInstance, osPathName, RT_BITMAP);
#endif
	}
#if defined(ZAF_WIN32) || !defined(ZAF_UNICODE)
	else
   		hRsrc = FindResource(ZafMSWindowsApp::hInstance, pathName, RT_BITMAP);
#endif

	if (hRsrc)
	{
		HGLOBAL hGlobal = LoadResource(ZafMSWindowsApp::hInstance, hRsrc);
		BITMAPINFO *bitmapInfo = (BITMAPINFO *)LockResource(hGlobal);

		int colors = 0;
		if (bitmapInfo->bmiHeader.biBitCount <= 8)
			colors = (1 << bitmapInfo->bmiHeader.biBitCount);

		if (((ZafScreenDisplay *)display)->paletteDevice && colors)
		{
			HANDLE hLogPalette = GlobalAlloc (GHND, sizeof (LOGPALETTE) +
				sizeof (PALETTEENTRY) * (colors));
			LOGPALETTE *logPalette = (LOGPALETTE *) GlobalLock (hLogPalette);
			logPalette->palVersion = 0x300;
			logPalette->palNumEntries = (WORD)colors;
			for (int i = 0;  i < colors;  i++)
			{
				logPalette->palPalEntry[i].peRed = bitmapInfo->bmiColors[i].rgbRed;
				logPalette->palPalEntry[i].peGreen = bitmapInfo->bmiColors[i].rgbGreen;
				logPalette->palPalEntry[i].peBlue  = bitmapInfo->bmiColors[i].rgbBlue;
				logPalette->palPalEntry[i].peFlags = 0;
			}
			imageBuffer.hPalette = CreatePalette(logPalette);
			GlobalUnlock (hLogPalette);
			GlobalFree   (hLogPalette);
		}

		HDC hdc = GetDC(NULL);
		if (imageBuffer.hPalette)
		{
			SelectPalette(hdc, imageBuffer.hPalette, TRUE);
			RealizePalette(hdc);
		}
		imageBuffer.hBitmap = CreateDIBitmap(hdc, &bitmapInfo->bmiHeader,
			CBM_INIT, (BYTE *)bitmapInfo + (int)(bitmapInfo->bmiHeader.biSize +
			colors * sizeof(RGBQUAD)), bitmapInfo, DIB_RGB_COLORS );
		ReleaseDC(NULL,hdc);

#if !defined(ZAF_WIN32)
		UnlockResource(hGlobal);
#endif
		FreeResource(hGlobal);
	}

	if (imageBuffer.hBitmap)
	{
		// Get image dimentions.
		BITMAP info;
		::GetObject(imageBuffer.hBitmap, sizeof(BITMAP), (LPSTR)&info);
		height = info.bmHeight;
		width = info.bmWidth;
	}
	return (imageBuffer.hBitmap ? true : false);
}

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

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

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

	// Load the file header.
	BITMAPFILEHEADER bitmapFileHeader;
	file.Read(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1);
	if (bitmapFileHeader.bfType != 0x4D42) // 'BM' format.
	{
		//??? File should be closed here.
		return (false);
	}

	// Load the info header.
   	BITMAPINFO *bitmapInfo = (BITMAPINFO *)new char[sizeof(BITMAPINFOHEADER)+sizeof(RGBQUAD)*256];
	file.Read(&bitmapInfo->bmiHeader, sizeof(BITMAPINFOHEADER), 1);
	width = (int)bitmapInfo->bmiHeader.biWidth;
	height = (int)bitmapInfo->bmiHeader.biHeight;

	// Load the color mapping.
	int colors = 0;
	if (bitmapInfo->bmiHeader.biBitCount <= 8)
		colors = (1 << bitmapInfo->bmiHeader.biBitCount);

	if (colors)
	{
		file.Read(bitmapInfo->bmiColors, sizeof(RGBQUAD), colors);
		if (((ZafScreenDisplay *)display)->paletteDevice)
		{
			HANDLE hLogPalette = GlobalAlloc (GHND, sizeof (LOGPALETTE) +
				sizeof (PALETTEENTRY) * (colors));
			LOGPALETTE *logPalette = (LOGPALETTE *) GlobalLock (hLogPalette);
			logPalette->palVersion = 0x300;
			logPalette->palNumEntries = (WORD)colors;
			for (int i = 0;  i < colors;  i++)
			{
				logPalette->palPalEntry[i].peRed = bitmapInfo->bmiColors[i].rgbRed;
				logPalette->palPalEntry[i].peGreen = bitmapInfo->bmiColors[i].rgbGreen;
				logPalette->palPalEntry[i].peBlue  = bitmapInfo->bmiColors[i].rgbBlue;
				logPalette->palPalEntry[i].peFlags = 0;
			}
			imageBuffer.hPalette = CreatePalette(logPalette);
			GlobalUnlock (hLogPalette);
			GlobalFree   (hLogPalette);
		}
	}

	// Allocate the bitmap data.
	long dibSize = file.Length() - bitmapFileHeader.bfOffBits;
	long tdibSize;
#if defined(ZAF_WIN32)
	HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, dibSize);
	BYTE *dib = (BYTE *)GlobalLock(hGlobal);
	BYTE *tdib = dib;
#else
	HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, dibSize);
	BYTE huge *dib = (BYTE huge *)GlobalLock(hGlobal);
	BYTE huge *tdib = dib;
#endif

	// Read the bitmap data.
	file.Seek(bitmapFileHeader.bfOffBits, ZAF_SEEK_START);
	while (dibSize)
	{
		// Read size must be such that segment will never wrap during read.
		// (GlobalAlloc is guaranteed to return memory aligned on a 16 byte boundary.)
		tdibSize = dibSize <= 8192 ? dibSize : 8192;
		file.Read(tdib, sizeof(BYTE), (int)tdibSize);
		dibSize -= tdibSize;
		tdib += tdibSize;
	}

	// Create the image.
	HDC hDC = GetDC(NULL);
	if (imageBuffer.hPalette)
	{
		SelectPalette(hDC, imageBuffer.hPalette, TRUE);
		RealizePalette(hDC);
	}
	imageBuffer.hBitmap = CreateDIBitmap(hDC, &bitmapInfo->bmiHeader, CBM_INIT,
		dib, (LPBITMAPINFO)bitmapInfo, DIB_RGB_COLORS);
	ReleaseDC(NULL, hDC);

	// Clean up.
	GlobalUnlock(hGlobal);
	GlobalFree(hGlobal);
   	delete []bitmapInfo;

	return (imageBuffer.hBitmap ? true : false);
}

ZafError ZafImage::SetPathID(int)
{
	// Windows resources are looked up by name (pathName) not value (pathID).
	return (ZAF_ERROR_INVALID_SOURCE);
}

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());
}

// ----- OS Specific Functions ----------------------------------------------

ZafEventType ZafImage::OSEvent(const ZafEventStruct &event)
{
	if (editMode && event.InputType() == E_MOUSE)
		return (ZafWindowObject::Event(event));

	ZafEventType ccode = event.osEvent.message;

	switch (ccode)
	{
	case WM_PALETTECHANGED:
 		if ((HWND)event.osEvent.wParam == screenID)
			return (0);
		// else continue.
	case WM_QUERYNEWPALETTE:
		if (imageBuffer.hPalette)
		{
			HDC hDC = GetDC(screenID);
			SelectPalette(hDC, imageBuffer.hPalette, (BOOL)event.rawCode);
			int i = RealizePalette(hDC);
			ReleaseDC(screenID, hDC);
			if (i)
				InvalidateRect(screenID, NULL, TRUE);
			ccode = i;
		}
		else
			ccode = FALSE;
		break;

	default:
		ccode = ZafWindowObject::OSEvent(event);
	}

	return ccode;
}

void ZafImage::OSRegisterObject(void)
{
	if (SupportObject())
		screenID = parent->screenID;
	else
		ZafWindowObject::OSRegisterObject();
}
