/*************************************************************************
**
**    OLE 2 Sample Code
**
**    debug2.c
**
**    This file contains various debug / subclass routines for the
**    ABOUT dialog
**
**    (c) Copyright Microsoft Corp. 1992 - 1996 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 (*(UNALIGNED WORD *)lpWalk != (WORD)0x0000)
                {
                    if (*lpWalk != (char)0x00)
                          *((UNALIGNED WORD *)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;
}
