//
// YOWFNCS - Yow! PIZZA? no, support functions!
//
// Version: 1.0 Copyright (C) 1991, Lantern Corp.
// Author: Edward Hutchins
// Revisions:
// 09/11/91 removed dependence on file dll - Ed.
//

#include "yow.h"

//
// globals
//

IMPORT HANDLE   hAppInst FROM( yow.c );
IMPORT HWND     hAppWnd FROM( yow.c );
IMPORT CHAR     szAppName[8] FROM( yow.c );
IMPORT CHAR     szDBase[FILENAME_SIZE] FROM( yow.c );
IMPORT CHAR     szEndChar[2] FROM( yow.c );

//
// IsLastLine - return true if this line is marked as the "last" one
//

BOOL NEAR PASCAL IsLastLine( NPSTR npszLine )
{
	if (!szEndChar[0]) return( TRUE );
	while (*npszLine)
	{
		if (*npszLine == szEndChar[0])
		{
			*npszLine = '\0';
			return( TRUE );
		}
		++npszLine;
	}
	return( FALSE );
}

//
// ReadYowLine - get another buffer full of text
//

INT NEAR PASCAL ReadYowLine( INT hFile, NPSTR npszBuff, INT nBuffSize,
							 NPSTR npszLine, INT nLineSize )
{
	NPSTR           npBuff = npszBuff;
	NPSTR           npStart = npszBuff;
	NPSTR           npEnd = npszBuff + nBuffSize - 1;
	INT             nRead = 0;

	while (npBuff <= npEnd) switch (*npBuff)
	{
	case '\0':
		{
			INT nCnt = npEnd - npBuff;
			nCnt = (INT)_lread( hFile, npBuff, nCnt );
			if (nCnt > 0) npBuff[nCnt] = '\0';
		}
		if (*npBuff == '\0') goto Done;
		break;

	case '\x0d':
	case '\x0a':
		++nRead;
		*npBuff++ = *npszLine++ = '\0';
		if (*npBuff == '\x0a' || *npBuff == '\x0d') *npBuff++ = '\0';
		goto Done;

	default:
		if (--nLineSize <= 0) goto Done;
		*npszLine++ = *npBuff++;
		break;
	}

Done:
	while (npBuff <= npEnd) *npStart++ = *npBuff++;
	*npStart = *npszLine = '\0';
	return( nRead );
}

//
// GetYowLine - get a line from the lines database
//

BOOL NEAR PASCAL GetYowLine( NPSTR npBuff, INT nSize )
{
	INT             hFile = _lopen( szDBase, OF_READ );
	LONG            lSize;
	LOCAL LONG      lPos;
	BOOL            bSuccess = FALSE;
	INT             nCnt, nLine;
	CHAR            szLineBuff[256];
	CHAR            szLine[256];

	*npBuff = '\0';
	if (hFile == -1) return( FALSE );
	lSize = _llseek( hFile, 0, 2 );
	if (lSize < 2) goto Abort;

	for (nCnt = 10; nCnt; --nCnt)
	{
		lPos = (lPos << 1) ^ GetCurrentTime() ^ MAKELONG( rand(), rand() );
		lPos %= lSize;
		if (_llseek( hFile, lPos, 0 ) != lPos) break;
		szLineBuff[0] = '\0';
		while (ReadYowLine( hFile, szLineBuff, sizeof(szLineBuff),
							szLine, sizeof(szLine) ) > 0)
		{
			if (IsLastLine( szLine )) break;
		}
		nLine = 0;
		while (ReadYowLine( hFile, szLineBuff, sizeof(szLineBuff),
							szLine, sizeof(szLine) ) > 0)
		{
			NPSTR npCpy = szLine;
			bSuccess = IsLastLine( szLine );
			if (nLine++)
			{
				*npBuff++ = '\x0d';
				*npBuff++ = '\x0a';
				nSize -= 2;
			}
			while (isspace(*npCpy)) ++npCpy;
			while (*npCpy && --nSize > 0) *npBuff++ = *npCpy++;
			*npBuff = '\0';
			if (bSuccess) goto Abort;
		}
	}

Abort:
	if (hFile != -1) _lclose( hFile );
	return( bSuccess );
}

//
// InRect - move a rectangle in by one
//

VOID NEAR PASCAL InRect( NPRECT npRect )
{
	RECT rect = *npRect;
	++rect.top; ++rect.left; --rect.bottom; --rect.right;
	*npRect = rect;
}

//
// TransRect - move a rectangle along the diagonal
//

VOID NEAR PASCAL TransRect( NPRECT npRect, INT nDist )
{
	RECT rect = *npRect;
	rect.top += nDist;
	rect.left += nDist;
	rect.bottom += nDist;
	rect.right += nDist;
	*npRect = rect;
}

//
// ShadowRect - draw a shadow rect
//

VOID NEAR PASCAL ShadowRect( HDC hDC, NPRECT npRect, HPEN hTL, HPEN hBR )
{
	RECT rect = *npRect;
	--rect.bottom; --rect.right;
	SelectObject( hDC, hTL );
	MoveTo( hDC, rect.right, rect.top );
	LineTo( hDC, rect.left, rect.top );
	LineTo( hDC, rect.left, rect.bottom );
	SelectObject( hDC, hBR );
	LineTo( hDC, rect.right, rect.bottom );
	LineTo( hDC, rect.right, rect.top );
	++rect.top; ++rect.left;
	*npRect = rect;
}

//
// DrawYowBorder - draw a shaded rectangle around the yow display
//

VOID NEAR PASCAL DrawYowBorder( HDC hDC, NPRECT npRect )
{
	HPEN            hHiPen = GetStockObject( WHITE_PEN );
	HPEN            hBlackPen = GetStockObject( BLACK_PEN );
	HPEN            hLoPen = CreatePen( 0, 1, RGB( 128, 128, 128 ) );
	RECT            rect = *npRect;

	SelectObject( hDC, GetStockObject( NULL_BRUSH ) );
	ShadowRect( hDC, &rect, hHiPen, hLoPen );
	ShadowRect( hDC, &rect, hHiPen, hLoPen );
	InRect( &rect ); InRect( &rect );
	ShadowRect( hDC, &rect, hLoPen, hHiPen );
	ShadowRect( hDC, &rect, hBlackPen, hBlackPen );
	ShadowRect( hDC, &rect, hHiPen, hLoPen );
	if (hLoPen) DeleteObject( hLoPen );
}

//
// SwapBitmaps - switch bitmaps around
//

VOID NEAR PASCAL SwapBitmaps( HDC hDC, HDC hdcMem, INT x, INT y, INT cx, INT cy )
{
	BitBlt( hdcMem, 0, 0, cx, cy, hDC, x, y, SRCINVERT );
	BitBlt( hDC, x, y, cx, cy, hdcMem, 0, 0, SRCINVERT );
	BitBlt( hdcMem, 0, 0, cx, cy, hDC, x, y, SRCINVERT );
}

//
// Yow - yow!
//

VOID FAR PASCAL Yow( INT nVirtKey )
{
	HDC             hDC = CreateScreenDC();
	HDC             hdcMem = CreateCompatibleDC( hDC );
	HBITMAP         hbm = NULL, hbmOld = NULL;
	HFONT           hfont = NULL, hfontOld = NULL;
	INT             x, y;
	INT             cx = GetSystemMetrics( SM_CXSCREEN );
	INT             cy = GetSystemMetrics( SM_CYSCREEN );
	RECT            rect;
	BOOL            bSuccess = FALSE;
	CHAR            szBuff[2048];

	// fetch the data
	if (!GetYowLine( szBuff, sizeof(szBuff) ))
	{
		MessageBeep( 0 );
		return;
	}

	hfont = CreateFont( cx / 40, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE,
						ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_STROKE_PRECIS,
						PROOF_QUALITY, DEFAULT_PITCH | FF_ROMAN, "Tms Rmn" );
	if (!hfont) goto Abort;

	// calculate the bounding box
	rect.left = 0; rect.right = cx - 56;
	rect.top = 0; rect.bottom = cy - 20;

	SetBkMode( hDC, TRANSPARENT );
	hfontOld = SelectObject( hDC, hfont );
	DrawText( hDC, szBuff, -1, &rect, YOW_STYLE | DT_CALCRECT );
	SelectObject( hDC, hfontOld );
	hfontOld = HNULL;

	// calculate the width and height, and the centered offset on the screen
	x = (cx - (56 + rect.right - rect.left)) / 2;
	cx = 40 + rect.right - rect.left; // kludge in some padding
	y = (cy - (20 + rect.bottom - rect.top)) / 2;
	cy = 4 + rect.bottom - rect.top;

	// create the offscreen bitmap
	hbm = CreateCompatibleBitmap( hDC, cx + 16, cy + 16 );
	if (!hbm) goto Abort;
	hbmOld = SelectObject( hdcMem, hbm );
	hfontOld = SelectObject( hdcMem, hfont );

	// set up the bitmap background
	rect.top = rect.left = 0;
	rect.right = 16 + cx; rect.bottom = 16 + cy;
	FillRect( hdcMem, &rect, GetStockObject( LTGRAY_BRUSH ) );
	DrawYowBorder( hdcMem, &rect );

	// draw the fancy text
	SetBkMode( hdcMem, TRANSPARENT );
	rect.top = rect.left = 8;
	rect.right = 8 + cx; rect.bottom = 8 + cy;
	TransRect( &rect, 2 );
	SetTextColor( hdcMem, RGB(255,255,255) );
	DrawText( hdcMem, szBuff, -1, &rect, YOW_STYLE );
	TransRect( &rect, -2 );
	SetTextColor( hdcMem, RGB(128,128,128) );
	DrawText( hdcMem, szBuff, -1, &rect, YOW_STYLE );
	TransRect( &rect, 1 );
	SetTextColor( hdcMem, RGB(0,0,0) );
	DrawText( hdcMem, szBuff, -1, &rect, YOW_STYLE );

	// keep the bitmap up till the key is released
	SwapBitmaps( hDC, hdcMem, x, y, 16 + cx, 16 + cy );
	while (GetAsyncKeyState( nVirtKey ) < 0) ;
	SwapBitmaps( hDC, hdcMem, x, y, 16 + cx, 16 + cy );

	bSuccess = TRUE;

Abort:
	if (hbmOld) SelectObject( hdcMem, hbmOld );
	if (hfontOld) SelectObject( hdcMem, hfontOld );
	if (hdcMem) DeleteDC( hdcMem );
	if (hDC) DeleteDC( hDC );
	if (hfont) DeleteObject( hfont );
	if (hbm) DeleteObject( hbm );
	if (!bSuccess) MessageBeep( 0 );
}
