/*

			Slide Show Screen Saver for Windows

				Image Generation and Display

*/

#include "screensv.h"

int ImageWid = -1;				  			// Width of master image
int ImageHgt = -1;							// Height of master image
static HPALETTE imagePal = NULL;			// Custom colour palette

static struct {
	WORD	palVersion;
	WORD	palNumEntries;
	PALETTEENTRY palPalEntry[256];
} logpal;

static HGLOBAL imageBitmap = NULL;			// Copy of bitmap resource
static currentImageIndex = 0;				// Index of image now on screen
static int DisplayWid, DisplayHgt;			// Image dimensions as displayed

//	PALMAPSTART  --  Create a replacement palette bitmap header.

//static HGLOBAL pmBuf = NULL;
static LPBITMAPINFOHEADER bm = NULL;

static LPBITMAPINFOHEADER palMapStart(LPBITMAPINFOHEADER bh, int *bmode)
{
	WORD i, ncol, offbits;
	unsigned short *palidx;
    
    assert(bm == NULL);
	ncol = (WORD) (bh->biClrUsed == 0 ? (1L << bh->biBitCount) : bh->biClrUsed);
	offbits = (WORD) (bh->biSize + ncol * sizeof(RGBQUAD));
	bm = GlobalAlloc(GPTR, (DWORD) offbits);
//	if (pmBuf != NULL) {
//		bm = GlobalLock(pmBuf);
//	}
	if (bm != NULL) {
		*bmode = DIB_PAL_COLORS;
		_fmemcpy(bm, bh, offbits);
		bm->biClrUsed = bm->biClrImportant = ncol;
		palidx = (unsigned short *) ((LPBITMAPINFO) bm)->bmiColors; 
		for (i = 0; i < ncol; i++) {
			*palidx++ = (unsigned short) i;
		}
		return bm;
	}
	*bmode = DIB_RGB_COLORS;
	return bh;	
}

//	PALMAPEND  --  Release palette map buffer, if any.

static void palMapEnd(void)
{
	if (bm != NULL) {
//		GlobalUnlock(pmBuf);
		GlobalFree(bm);
		bm = NULL;
	}
} 

//  NEEDIMAGEBITMAP  --  Make sure image bitmap and palette are loaded.

void needImageBitmap(void)
{
	int i;
	LPBITMAPINFOHEADER bh;
	FILE *input_file;
	static char spath[MAX_PATH * 2];	// Static so it can be used for loadError

	ImageWid = ImageHgt = -1;
	imageBitmap = NULL;
	imagePal = NULL;

	if (fileListStatus != FL_OK) {
		sprintf(spath, rstring(FL_ERROR_MESSAGES + fileListStatus), slidePath);
		loadError = spath;
		return;
	}

	if (fileListLength == 0) {
		sprintf(spath, rstring(FL_VOID), slidePath);
		loadError = spath;
		return;
	}

	sprintf(spath, "%s/%s", slidePath, fileList[currentImage]->filename);
	currentFileName = fileList[currentImage]->filename;
	currentImageIndex = currentImage;
	currentImage++;
	if (currentImage >= fileListLength) {
		currentImage = 0;
		/*	If we're showing slides in random order, re-shuffle
			the list before commencing the next pass.  */
		if (shufflePlay) {
			fileListShuffleImages();
		}
	}
	input_file = fopen(spath, "rb");
	if (input_file == NULL) {
		return;
	}

	i = loadImage(input_file, &imageBitmap,
					  &ImageWid, &ImageHgt);
	fclose(input_file);
	if (!i) {
		return;
	}

    bh = (LPBITMAPINFOHEADER) imageBitmap;
    if (bh->biBitCount <= 8) {
		int ncol = bh->biClrUsed == 0 ? (1L << bh->biBitCount) : bh->biClrUsed;
	
		/* Extract the colour palette from the DIB file and
		   construct a memory logical palette embodying those colours.  Then
		   register the palette with the system to provide those colours on
		   the display. */
	
		logpal.palVersion = 0x300;
		logpal.palNumEntries = ncol;
		for (i = 0; i < ncol; i++) {
			RGBQUAD q = *((LPRGBQUAD) &((LPBITMAPINFO) bh)->bmiColors[i]);

			logpal.palPalEntry[i].peRed = q.rgbRed;
			logpal.palPalEntry[i].peGreen = q.rgbGreen;
			logpal.palPalEntry[i].peBlue = q.rgbBlue;
			logpal.palPalEntry[i].peFlags = PC_NOCOLLAPSE;
		}
	
		imagePal = CreatePalette((LPLOGPALETTE) &logpal);
	}
}

//  PAINTDIB  --  Paint the image DIB onto the device surface.

static int zstatus, lposX = -1, lposY = -1;

static void paintDIB(HWND hWnd, HDC hDC)
{
	LPBITMAPINFOHEADER obmap, bp;
	WORD ncol, offbits;
	int bmode = DIB_RGB_COLORS;
	unsigned char *opixel;
	HPALETTE opal;
	RECT rc;
	int posx, posy;
	int tdo = ((showDateTime || showFileName) && !fChildPreview) ? (tmx.tmHeight + 1) : 0;

	DisplayWid = ImageWid;
	DisplayHgt = ImageHgt;
	GetClientRect(hWnd, &rc);

	if (DisplayWid > rc.right) {
		DisplayWid = rc.right;
		DisplayHgt = (int) (ImageHgt * (((double) DisplayWid) / ImageWid));
	}
	if (DisplayHgt > (rc.bottom - tdo)) {
		DisplayHgt = (rc.bottom - tdo);
		DisplayWid = (int) (ImageWid * (((double) DisplayHgt) / ImageHgt));
	}

	if (randomPosition) {
		posx = (rc.right == DisplayWid) ? 0 : (random() % (rc.right - DisplayWid));
		posy = (rc.bottom == DisplayHgt) ? 0 : ((random() % (rc.bottom - DisplayHgt)) + tdo);
	} else {
		posx = (rc.right - DisplayWid) / 2;
		posy = (rc.bottom - DisplayHgt) / 2;
		if (posx < 0) {
			posx = 0;
		}
		if (posy < tdo) {
			posy = tdo;
		}
	}
	lposX = posx;
	lposY = posy;
	obmap = (LPBITMAPINFOHEADER) imageBitmap;
	ncol = (WORD) ((imagePal != NULL) ? (obmap->biClrUsed == 0 ? (1L << obmap->biBitCount)
												   : obmap->biClrUsed)
						  : 0);
	bp = obmap;
	if (imagePal != NULL) {
		bp = palMapStart(obmap, &bmode);
	}
	offbits = (WORD) (obmap->biSize + ncol * sizeof(RGBQUAD));
	opixel = ((unsigned char *) (obmap)) + offbits;
	if (imagePal != NULL) {
		opal = SelectPalette(hDC, imagePal, FALSE);
		RealizePalette(hDC);
	}

	FillRect(hDC, &rc, (HBRUSH) GetStockObject(BLACK_BRUSH));

	if (ImageWid == DisplayWid && ImageHgt == DisplayHgt) {
		zstatus = SetDIBitsToDevice(hDC, posx, posy, ImageWid, ImageHgt,
			0, 0, 0, ImageHgt,
			opixel, (LPBITMAPINFO) bp, bmode);
	} else {
		SetStretchBltMode(hDC, COLORONCOLOR);
		zstatus = StretchDIBits(hDC, posx, posy, DisplayWid, DisplayHgt,
			0, 0, ImageWid, ImageHgt,
			opixel, (LPBITMAPINFO) bp, bmode, SRCCOPY);
	}

	if (imagePal != NULL) {
		palMapEnd();
		SelectPalette(hDC, opal, FALSE);
	}
}

//	PAINTDATE  --  Paint date and time title above image, if requested

static void paintDate(HDC hDC, struct tm lt)
{
	if ((showDateTime || showFileName) && !fChildPreview && (lposX >= 0)) {
		RECT cr;

		cr.left = lposX;
		cr.top = lposY - (tmx.tmHeight + 1);
		cr.right = cr.left + DisplayWid;
		cr.bottom = cr.top + (tmx.tmHeight + 1) * 2;
		if (cr.top < 0) {
			cr.bottom -= cr.top;
			cr.top = 0;
		}
		ssavetime(hDC, lt, cr, skycolour);	// Update local and universal time
	}
}

//  UPDVIEWFROM  --  Update image on screen

void updviewfrom(HWND hWnd, BOOL repositioned)
{
	HDC hDC;
    struct tm lt;
	static int changeMode = -1;

	jyear(faketime, &lt.tm_year, &lt.tm_mon, &lt.tm_mday);
	lt.tm_year -= 1900;
	lt.tm_mon--;
	jhms(faketime, &lt.tm_hour, &lt.tm_min, &lt.tm_sec);

	hDC = GetDC(hWnd);

	//	Paint the date and time above the image

	if ((showDateTime || showFileName) && !fChildPreview && (lposX >= 0) && !repositioned) {
		paintDate(hDC, lt);
	}

	if (!repositioned) {
		return;
	}

	//	Okay, it's time for a new image.  Load and display it.

	needImageBitmap();
	if (imageBitmap == NULL) {
		char s[256];
		RECT cr;
		int ryp;

		GetClientRect(hWnd, &cr);
		sprintf(s, "Cannot load image[%d] %s:",
			currentImageIndex,
			(currentFileName == NULL) ? "(none)" : currentFileName);
		FillRect(hDC, &cr, (HBRUSH) GetStockObject(BLACK_BRUSH));
		SetBkMode(hDC, OPAQUE);
		SetTextColor(hDC, RGB(255, 0, 0));
		SetBkColor(hDC, RGB(0, 0, 0));
		SetTextAlign(hDC, TA_CENTER | TA_BASELINE | TA_NOUPDATECP);
		//	Set error text at random position vertically to avoid burn-in
		ryp = (cr.bottom / 8) +
			  ((random() & RAND_MAX) * ((cr.bottom * 6) / 8)) / RAND_MAX;
		TextOut(hDC, cr.right / 2, ryp, s, strlen(s));
		ryp += tmx.tmHeight;
		sprintf(s, "%s.", (loadError == NULL) ? "Unknown error" : loadError);
		TextOut(hDC, cr.right / 2, ryp, s, strlen(s));
		lposX = -1;
	} else {
		paintDIB(hWnd, hDC);
		paintDate(hDC, lt);
		release_image();
	}
	ReleaseDC(hWnd, hDC);
}

//	RELEASE_IMAGE  --  Release storage associated with the image

void release_image(void)
{
	if (imageBitmap != NULL) {
		GlobalFree(imageBitmap);
		imageBitmap = NULL;
	}
	if (imagePal != NULL) {
		DeleteObject(imagePal);
		imagePal = NULL;
	}
}
