/*

		Screen Saver Window procedure
                             
*/

#include "screensv.h"

int showDateTime = TRUE;				// Show date and time ?
int forcePalette = 0;					// Force palette even if > 8 bit display
int usePalette = 0;						// Should palette be used ?
int currentImage = 0;					// Current image in file list
char *currentFileName = NULL;			// Current slide file name, if any
int currentSound = 0;					// Current sound, if any
char *currentSoundName = NULL;			// Current sound file name, if any
int showFileName = FALSE;				// Show file name ?
int changeTime = 60;					// Change slides after this number of seconds
int shufflePlay = TRUE;					// Show slides in random order ?
int soundMode = SOUND_CONTINUOUS;		// Sound play mode
int soundPause = 15;					// Pause between sounds in seconds
int randomPosition = TRUE;				// Show slides in random screen position ?
TEXTMETRIC tmx;							// Standard font text metric

COLORREF skycolour = RGB(0, 0, 255);	// Colour for legends

char slidePath[MAX_PATH] = "f:/slides";	// Directory containing slides

typedef enum {
	soundIdle,							// No sound playing
	soundPlaying,						// Sound currently playing
	soundWaiting,						// Waiting for start of next sound
	soundSyncWait						// Waiting for next slide
} soundState;

static soundState sstate = soundIdle;	// Current sound state
int soundWait = 0;						// Seconds until start of next sound

#ifdef TRACY
void Blooie(char *s)
{
	RECT rc;
	HDC hDC;

	hDC = GetDC(hWndMain);
	GetClientRect(hWndMain, &rc);
	SetBkMode(hDC, OPAQUE);
	SetTextColor(hDC, RGB(255, 255, 255));
	SetBkColor(hDC, RGB(255, 0, 0));
	SetTextAlign(hDC, TA_NOUPDATECP);
	TextOut(hDC, 10, 10, s, strlen(s));
	ReleaseDC(hWndMain, hDC);
}
#endif

/*	PLAY_SOUND  --  Play a designated sound.  Any in-progress sound
					is terminated.  */

static void play_sound(int which)
{
	/*	SND_NOSTOP keeps chimes from stepping on the theme if
		we're launched around the time for a chime.  */
	PlaySound(MAKEINTRESOURCE(which), hMainInstance,
		SND_ASYNC | SND_RESOURCE | SND_NOSTOP);
}

//  SET_TM_TIME  --  Set time from Windows system or local time

void set_tm_time(struct tm *t, BOOL islocal)
{
#define CtF(tf, sf) t->tf = s.sf

	SYSTEMTIME s;

	if (islocal) {
		GetLocalTime(&s);
	} else {
		GetSystemTime(&s);
	}
	CtF(tm_sec, wSecond);
	CtF(tm_min, wMinute);
	CtF(tm_hour, wHour);
	CtF(tm_mday, wDay);
	CtF(tm_mon, wMonth - 1);
	CtF(tm_year, wYear - 1900);
	CtF(tm_wday, wDayOfWeek);
	//  tm_yday  never used
	//	tm_isdst never used
}

//	SEEDRANDOM  --  Seed the pseudorandom sequence generator

//#define SHOW_SEED
#ifdef SHOW_SEED
unsigned long r_seed;
#endif

static void seedRandom(void)
{
	int i;
	POINT p;
	MEMORYSTATUS ms;
	unsigned long seed;
	
	/*	The following gets all kind of information likely
		to vary from moment to moment and uses it as the initial
		seed for the random number generator.  If any of these
		causes porting problems in the future, just delete them.  */
	
	seed = GetTickCount();
	seed ^= time(NULL);
	seed ^= (long) GetActiveWindow();
	seed ^= GetFreeSpace(0);
	ms.dwLength = sizeof(MEMORYSTATUS);
	GlobalMemoryStatus(&ms);
	seed ^= ms.dwMemoryLoad; 
	seed ^= ms.dwAvailPhys; 
	seed ^= ms.dwAvailPageFile; 
	GetCursorPos(&p);
	seed ^= p.x;
	seed ^= p.y;

#ifdef SHOW_SEED
	r_seed = seed;
#endif

	srandom(seed);
	for (i = 0; i < 11; i++) {
		(void) random();
	}

	i = random() % 23;
	while (i-- > 0) {
		(void) random();
	}
}

//	STARTSOUND  --  Start next sound playing

static void startSound(HWND hWnd)
{
	if ((soundFileListLength > 0) && (sstate != soundPlaying)) {
		char s[512];

		currentSoundName = soundFileList[currentSound]->filename;
		sprintf(s, "open %s/%s type %s alias sound",
			slidePath, soundFileList[currentSound]->filename,
			soundFileList[currentSound]->age == 2 ? "waveaudio" : "sequencer");
		currentSound++;
		if (currentSound >= soundFileListLength) {
			currentSound = 0;
			/*	If we're playing sounds in random order, re-shuffle
				the list before commencing the next pass.  */
			if (shufflePlay) {
				fileListShuffleSounds();
			}
		}
		mciSendString(s, NULL, 0, NULL);
		mciSendString("play sound notify", NULL, 0, hWnd);
		sstate = soundPlaying;
	}
}

/*  SCREENSAVERPROC  --  Screen saver window procedure.  */

WindowProc(ScreenSaverProc)
{
    HDC hDC;
    PAINTSTRUCT ps;         			// holds PAINT information
    RECT cr;							// Client rectangle
    static long cctime;					// Time to display (real or simulated) */
    static long rptime = 0;				// Last time image shifted
    struct tm lt;
    static BOOL repositioned = FALSE;
	static char stdname[128], dstname[128];	// Standard and Daylight time zone names
	char spath[MAX_PATH + 40];			// Wildcard for slide path

    switch (Message) {

        case WM_CREATE:
			_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG);
			_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG);
			_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG);

            hDC = GetDC(hWnd);
			SetCursor(NULL);			// Zap cursor before first draw
            GetIniEntries();
            GetIniSettings();			// Get setup defaults
			usePalette = forcePalette ||
				(GetDeviceCaps(hDC, NUMCOLORS) != -1);
            localtimeformat();
			GetTextMetrics(hDC, &tmx);
            srand((unsigned) (GetTickCount() & 0xFFFF));
			currentSound = 0;
			currentSoundName = 0;
			currentImage = 0;
			currentFileName = 0;
            ReleaseDC(hWnd, hDC);
//#define PNGDEBUG
#ifdef PNGDEBUG
{
	HGLOBAL img = NULL;
	int imx, imy;
	FILE *f;

	f = fopen("f:/sliders/jmini.png", "rb");
	if (f != NULL) {
		loadImage(f, &img, &imx, &imy);
		if (img != NULL) {
			GlobalFree(img);
		}
		fclose(f);
	}

}
#endif

//#define BMPDEBUG
#ifdef BMPDEBUG
{
	HGLOBAL img = NULL;
	int imx, imy;
	FILE *f;

	f = fopen("f:/bslides/pal16.bmp", "rb");
	if (f != NULL) {
		loadImage(f, &img, &imx, &imy);
		if (img != NULL) {
			GlobalFree(img);
		}
		fclose(f);
	}

}
#endif

#ifdef MEM_DEBUG_TEST
{
	char *ptr = malloc(10);
	ptr[11] = 12;
	free(ptr);
}
#endif

			/*	Don't start playing a sound in continuous
				mode until the selected inter-track time has
				elapsed.  This avoids irritating blurps of
				sound when you immediately terminate the
				screen saver.  */

			soundWait = soundPause;
			sstate = soundIdle;
			if (soundMode == SOUND_CONTINUOUS) {
				sstate = soundWaiting;
			}
			if (soundMode == SOUND_SYNC) {
				sstate = soundSyncWait;
			}
			set_tm_time(&lt, TRUE);
			seedRandom();				// Seed the random number generator
			sprintf(spath, "%s/*.*", slidePath);
			if (fileListBuild(spath) > 0) {
//#define DUMP_FILE_LIST
#ifdef DUMP_FILE_LIST
				FILE *f = fopen("d:/tmp/filelist.txt", "w");
#endif
				if (shufflePlay) {
					fileListShuffleImages();
					fileListShuffleSounds();
				} else {
					fileListSort();
				}
#ifdef DUMP_FILE_LIST
				fileListDump(f);
				fclose(f);
#endif
			}
//updviewfrom(hWnd, TRUE);
            SetTimer(hWnd, 1, 1000u, NULL);
            break;

        case WM_DESTROY:
            KillTimer(hWnd, 1);
			if (sstate == soundPlaying) {
				mciSendString("stop sound wait", NULL, 0, hWnd);
				mciSendString("close sound wait", NULL, 0, hWnd);	
			}
			release_image();
			fileListDestroy();
        	break;

#ifdef CLIPPO

		/*	If CLIPPO is defined, right mouse button copies screen
			to the clipboard.  Why?  So we can make screen shots to
			illustrate the program.  */

		case WM_RBUTTONDOWN:
            {
                HDC hdcMem;
                HBITMAP hBitmap;

                hDC = GetDC(hWnd);
                hdcMem = CreateCompatibleDC(hDC);
				GetClientRect(hWnd, &cr);
                hBitmap = CreateCompatibleBitmap(hDC,
                            cr.right, cr.bottom);
                if (hBitmap) {
                    SelectObject(hdcMem, hBitmap);
                    BitBlt(hdcMem, 0, 0, cr.right, cr.bottom,
                            hDC, 0, 0, SRCCOPY);
                    OpenClipboard(hWnd);
                    EmptyClipboard();
                    SetClipboardData(CF_BITMAP, hBitmap);
                    CloseClipboard();
                    DeleteDC(hdcMem);
                    ReleaseDC(hWnd, hDC);
                }
            }
			return 0;
#endif

		case WM_KEYDOWN:
			/*	If we're in "sound by key" mode, start the next sound
				when the return key is pressed.  */
			if ((wParam == VK_RETURN) && (soundFileListLength > 0) &&
				(soundMode == SOUND_KEY)) {
				if (sstate != soundPlaying) {
					startSound(hWnd);
				}
				return 0;
			}

			//	The Escape key always terminates a playing sound
			if ((wParam == VK_ESCAPE)  && (soundFileListLength > 0)) {
				if (sstate == soundPlaying) {
					mciSendString("stop sound wait notify", NULL, 0, hWnd);	
				}
				return 0;	
			}
			break;

		case MM_MCINOTIFY:
			mciSendString("close sound wait", NULL, 0, NULL);
			currentSoundName = NULL;
			if (soundMode == SOUND_CONTINUOUS) {
				sstate = soundWaiting;
				soundWait = soundPause;
			} else if (soundMode == SOUND_SYNC) {
				sstate = soundSyncWait;
			} else {
				sstate = soundIdle;
			}
			break;
        
        case WM_TIMER:
			/*  If we're in continuous sound play mode, count down
				the inter-track timer and when it goes to zero start
				the next sounc.  */
			if (soundMode == SOUND_CONTINUOUS && sstate == soundWaiting &&
				(soundFileListLength > 0)) {
				if (soundWait-- <= 0) {
					startSound(hWnd);
				}
			}
			InvalidateRect(hWnd, NULL, FALSE);
        	break;

		case WM_ERASEBKGND:
			hDC = (HDC) wParam;
		    GetClientRect(hWnd, &cr);
		    FillRect(hDC, &cr, (HBRUSH) GetStockObject(BLACK_BRUSH));
			repositioned = TRUE;
			return 0;

        case WM_PAINT:
			time(&cctime);        
            memset(&ps, 0, sizeof(PAINTSTRUCT));
            hDC = BeginPaint(hWnd, &ps);
			set_tm_time(&lt, TRUE);
			faketime = jtime(&lt);
            
            /* If it's time, randomly reposition the image within the
               rectangular window.  This prevents burning in the phosphor by
               the constant parts of the map.  One additional subtlety
			   is that if we're synchronising slides with sounds, we
			   have to wait until the current sound ends before swapping
			   to the new slide.  */

			if (!((soundMode == SOUND_SYNC) && (sstate == soundPlaying))) {            
				if ((cctime - rptime) > changeTime) {
            		rptime = cctime;
            		repositioned = TRUE;
				}
			}

			updviewfrom(hWnd, repositioned);

			if (repositioned && (soundMode == SOUND_SYNC) && (soundFileListLength > 0)) {
				startSound(hWnd);
			}

			repositioned = FALSE;

#ifdef SHOW_SEED
			{
				char tbuf[256];
				int obkm;
				COLORREF obkc, otxc;
				UINT otxa;
				RECT tr;

				tr.left = cr.left;
				tr.right = cr.right;
				tr.top = cr.bottom / 2;
				tr.bottom = tr.top + tmx.tmHeight;
				otxa = SetTextAlign(hDC, TA_NOUPDATECP);
				obkm = SetBkMode(hDC, OPAQUE);
				obkc = SetBkColor(hDC, RGB(0, 0, 0));
				otxc = SetTextColor(hDC, skycolour);
				sprintf(tbuf, "Seed: %lu", r_seed);
				DrawText(hDC, tbuf, -1, &tr, DT_LEFT | DT_TOP | DT_NOCLIP | DT_SINGLELINE);
				SetTextAlign(hDC, otxa);
				SetBkMode(hDC, obkm);
				SetBkColor(hDC, obkc);
				SetTextColor(hDC, otxc);
			}			
#endif

            ValidateRgn(hWnd, NULL);

            /* Inform Windows painting is complete */
            EndPaint(hWnd, &ps);
            return 0;
    }
	return DefScreenSaverProc(hWnd, Message, wParam, lParam);
}

