/*************************************************************************
**
**    OLE 2 Sample Code
**
**    debug2.c
**
**    This file contains various debug / subclass routines for the
**    ABOUT dialog
**
**    (c) Copyright Microsoft Corp. 1992 - 1993 All Rights Reserved
**
*************************************************************************/

#include "outline.h"
#include <stdlib.h>
#include <time.h>

extern LPOUTLINEAPP g_lpApp;

LONG CALLBACK EXPORT DebugAbout(HWND hWnd, unsigned uMsg, WORD wParam, LONG lParam);
void RandomizeStars(HDC hDC);
BOOL InitStrings(void);
BOOL DrawString(int iCount, HDC hDC, LPRECT rcDrawIn);

static FARPROC lpRealAboutProc = 0L;
static int width, height;
static RECT rc;
static HANDLE hStrBlock = NULL;
static LPSTR lpStrings = NULL;
static WORD       wLineHeight;


/* TraceDebug
 * ----------
 *
 * Called once when our About Box's gets the INITDIALOG message.  Subclasses
 * dialog.
 */

void TraceDebug(HWND hDlg, int iControl)
{

	// Load strings, if the strings aren't there, then don't subclass
	// the dialog
	if (InitStrings() != TRUE)
		return;

	// Subclass the dialog
	lpRealAboutProc = (FARPROC)(LONG)GetWindowLong(hDlg, GWL_WNDPROC);
	SetWindowLong(hDlg, GWL_WNDPROC, (LONG)(FARPROC)DebugAbout);

	// Get rect of control in screen coords, and translate to our dialog
	// box's coordinates
	GetWindowRect(GetDlgItem(hDlg, iControl), &rc);
	MapWindowPoints(NULL, hDlg, (LPPOINT)&rc, 2);

	width  = rc.right - rc.left;
	height = rc.bottom - rc.top;
}

/* DebugAbout
 * ----------
 *
 * The subclassed About dialog's main window proc.
 */

LONG CALLBACK EXPORT DebugAbout(HWND hWnd, unsigned uMsg, WORD wParam, LONG lParam)
{
	RECT              rcOut;
	static BOOL       bTimerStarted = FALSE;
	static int        iTopLocation;
	HDC               hDCScr;
	static HDC        hDCMem;
	static HBITMAP    hBitmap;
	static HBITMAP    hBitmapOld;
	static RECT       rcMem;
	static HFONT      hFont;

	switch (uMsg)
	{

	/*
	 * If we get a LBUTTONDBLCLICK in the upper left of
	 * the dialog, fire off the about box effects
	 */

	case WM_LBUTTONDBLCLK:
		if ((wParam & MK_CONTROL) && (wParam & MK_SHIFT)
			&& LOWORD(lParam) < 10 && HIWORD(lParam) < 10 &&
			bTimerStarted == FALSE)
			{
			if (SetTimer ( hWnd, 1, 10, NULL ))
				{
				LOGFONT lf;
				int i;

				bTimerStarted = TRUE;

				// "Open up" the window
				hDCScr = GetDC ( hWnd );
				hDCMem = CreateCompatibleDC     ( hDCScr );

				hBitmap = CreateCompatibleBitmap(hDCScr, width, height);
				hBitmapOld = SelectObject(hDCMem, hBitmap);

				// Blt from dialog to memDC
				BitBlt(hDCMem, 0, 0, width, height,
				hDCScr, rc.left, rc.top, SRCCOPY);

				for (i=0;i<height;i+=1)
				{
					BitBlt(hDCScr, rc.left, rc.top + i + 1, width, height-i-1, hDCMem, 0, 0, SRCCOPY);
					PatBlt(hDCScr, rc.left, rc.top + i, width, 1, BLACKNESS);
				}

				SelectObject(hDCMem, hBitmapOld);
				DeleteObject(hBitmap);

				// Set up memory DC with default attributes
				hBitmap   = CreateCompatibleBitmap(hDCScr, width, height);
				ReleaseDC(hWnd, hDCScr);

				hBitmapOld = SelectObject(hDCMem, hBitmap);

				SetBkMode(hDCMem, TRANSPARENT);
				SetBkColor(hDCMem, RGB(0,0,0));

				// Create font
				memset(&lf, 0, sizeof(LOGFONT));
				lf.lfHeight = -(height / 7); // Fit 7 lines of text in box
				lf.lfWeight = FW_BOLD;
				strcpy(lf.lfFaceName, "Arial");
				hFont = CreateFontIndirect(&lf);

				// If we can't create the font, revert and use the standard
				// system font.
				if (!hFont)
					GetObject(GetStockObject(SYSTEM_FONT), sizeof(LOGFONT), &lf);

				wLineHeight = abs(lf.lfHeight) + 5; // 5 pixels between lines

				// Set location of top of banner at bottom of the window
				iTopLocation = height + 50;

				SetRect(&rcMem, 0, 0, width, height);
				}
			}
			// Call our real window procedure in case they want to
			// handle LBUTTONDOWN messages also
			goto Default;

	case WM_TIMER:
		{
		int iCount;
		HFONT hfold;

		/*
		 * On each timer message, we are going to construct the next image
		 * in the animation sequence, then bitblt this to our dialog.
		 */

		// Clear out old bitmap and place random star image on background
		PatBlt(hDCMem, rcMem.left, rcMem.top, rcMem.right, rcMem.bottom, BLACKNESS);
		RandomizeStars(hDCMem);

		// Set initial location to draw text
		rcOut = rcMem;
		rcOut.top = 0 + iTopLocation;
		rcOut.bottom = rcOut.top + wLineHeight;

		iCount = 0;
		if (hFont) hfold = SelectObject(hDCMem, hFont);

		SetTextColor(hDCMem, RGB(0,255,0));
		while (DrawString(iCount, hDCMem, &rcOut) == TRUE)
			{
			rcOut.top    += wLineHeight;
			rcOut.bottom += wLineHeight;
			iCount++;
			}
		if (hFont) SelectObject(hDCMem, hfold);

		// Now blt the memory dc that we have just constructed
		// to the screen
		hDCScr = GetDC(hWnd);
		BitBlt(hDCScr, rc.left, rc.top, rc.right, rc.bottom,
			hDCMem, 0, 0, SRCCOPY);
		ReleaseDC(hWnd, hDCScr);

		// For the next animation sequence, we want to move the
		// whole thing up, so decrement the location of the top
		// of the banner

		iTopLocation -= 2;

		// If we've gone through the banner once, reset it
		if (iTopLocation < -(int)(wLineHeight * iCount))
			iTopLocation = height + 50;
		}
		// Goto default
		goto Default;

	case WM_NCDESTROY:
		{
		LONG defReturn;

		/*
		 * We're being destroyed.  Clean up what we created.
		 */

		if (bTimerStarted)
		{
			KillTimer(hWnd, 1);
			SelectObject (hDCMem, hBitmapOld);
			DeleteObject (hBitmap);
			DeleteDC (hDCMem);
			if (hFont) DeleteObject(hFont);
			bTimerStarted = FALSE;
		}

		if (lpStrings)
			UnlockResource(hStrBlock), lpStrings = NULL;
		if (hStrBlock)
			FreeResource(hStrBlock), hStrBlock = NULL;

		// Pass the NCDESTROY on to our real window procedure.  Since
		// this is the last message that we are going to be getting,
		// we can go ahead and free the proc instance here.

		defReturn = CallWindowProc((WNDPROC)lpRealAboutProc, hWnd,
					   uMsg, wParam, lParam);
		return defReturn;
		}

	Default:
	default:
		return CallWindowProc(
				(WNDPROC)lpRealAboutProc, hWnd, uMsg, wParam, lParam);
	}
	return 0L;
}


/* RandomizeStars
 * --------------
 *
 * Paints random stars on the specified hDC
 *
 */

void RandomizeStars(HDC hDC)
{
	int             i;

	// Seed the random number generator with current time.  This will,
	// in effect, only change the seed every second, so our
	// starfield will change only every second.
	srand((unsigned)time(NULL));

	// Generate random white stars
	for (i=0;i<20;i++)
		PatBlt(hDC, getrandom(0,width), getrandom(0,height), 2, 2, WHITENESS);
}

/* InitStrings
 * --------------
 *
 * Reads strings from stringtable.  Returns TRUE if it worked OK.
 *
 */

BOOL InitStrings()
{
	HRSRC hResStrings;
	LPSTR lpWalk;

	// Load the block of strings
	if ((hResStrings = FindResource(
			g_lpApp->m_hInst,
			MAKEINTRESOURCE(9999),
			RT_RCDATA)) == NULL)
		return FALSE;
	if ((hStrBlock = LoadResource(g_lpApp->m_hInst, hResStrings)) == NULL)
		return FALSE;
	if ((lpStrings = LockResource(hStrBlock)) == NULL)
		return FALSE;

	if (lpStrings && *(lpStrings+2)!=0x45)
		{
		lpWalk = lpStrings;
		while (*(LPWORD)lpWalk != (WORD)0x0000)
			{
			if (*lpWalk != (char)0x00)
				*lpWalk ^= 0x98;
			lpWalk++;
			}
		}
	return TRUE;
}

/* DrawString
 * ----------
 *
 * Draws the next string on the specified hDC using the
 * output rectangle.  If iCount == 0, reset to start of list.
 *
 * Returns: TRUE to contine, FALSE if we're done
 */

BOOL DrawString(int iCount, HDC hDC, LPRECT rcDrawIn)
{
	static LPSTR lpPtr = NULL;

	if (iCount == 0)
		// First time, reset pointer
		lpPtr = lpStrings;

	if (*lpPtr == '\0') // If we've hit a NULL string, we're done
		return FALSE;

	// If we're drawing outside of visible box, don't call DrawText
	if ((rcDrawIn->bottom > 0) && (rcDrawIn->top < height))
		DrawText(hDC, lpPtr, -1, rcDrawIn, DT_CENTER);

	// Advance pointer to next string
	lpPtr += lstrlen(lpPtr) + 1;

	return TRUE;
}
