#ifdef PM
#define INCL_WIN
#define INCL_GPI
#include <os2.h>
INT _acrtused = 0;
#endif

#ifdef WIN
#include <windows.h>
#include <port1632.h>
#endif

#include "std.h"
#include "scrsave.h"


#ifdef WIN
LONG mppenrgb [] =
	{
	RGB(0x00, 0x00, 0x00),
	RGB(0x00, 0x00, 0x80),
	RGB(0x00, 0x80, 0x00),
	RGB(0x00, 0x80, 0x80),
	RGB(0x80, 0x00, 0x00),
	RGB(0x80, 0x00, 0x80),
	RGB(0x80, 0x80, 0x00),
	RGB(0x80, 0x80, 0x80),
	RGB(0xc0, 0xc0, 0xc0),
	RGB(0x00, 0x00, 0xff),
	RGB(0x00, 0xff, 0x00),
	RGB(0x00, 0xff, 0xff),
	RGB(0xff, 0x00, 0x00),
	RGB(0xff, 0x00, 0xff),
	RGB(0xff, 0xff, 0x00),
	RGB(0xff, 0xff, 0xff)
	};

#define CLR_BLACK	0
#define CLR_BACKGROUND	0
#endif


#define MultDiv(a, b, c)  ((int) (((long) (a) * (long) (b)) / (c)))


INT dxScreen, dyScreen;

#define dxcMax 80
#define dycMax 50

typedef UCHAR GRID [dxcMax][dycMax];

GRID gridOld, gridNew;
BOOL fRedraw;
INT csecRandomize;


VOID SetCell(CVS hps, GRID grid, INT dxc, INT dyc, INT clr);
VOID Redraw(CVS hps);
Randomize();
Animate(CVS hps);
ClrAverage(GRID grid, INT dxc, INT dyc);


BOOL EXPENTRY ScrSaveProc(INT ssm, LPVOID l1, LONG_PTR l2, LONG_PTR l3)
	{
	CHAR FAR * lpsz;
	CHAR FAR * lpch;
	
	switch (ssm)
		{
	default:
		return FALSE;
		
	case SSM_OPEN:
		lpsz = (PSZ) l1;
		lpch = "Life";
		while ((*lpsz++ = *lpch++) != '\0')
			;
		
		lpsz = (PSZ) l2;
		lpch = "Life in Color\n\nby Brad Christian";
		while ((*lpsz++ = *lpch++) != '\0')
			;
		
#ifdef PM
		dxScreen = WinQuerySysValue(HWND_DESKTOP, SV_CXSCREEN);
		dyScreen = WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN);
#endif
#ifdef WIN
		dxScreen = GetSystemMetrics(SM_CXSCREEN);
		dyScreen = GetSystemMetrics(SM_CYSCREEN);
#endif
		// FALL THROUGH
			
	case SSM_SECOND:
		if (--csecRandomize < 0)
			{
			Randomize();
			csecRandomize = 60 + WRand(120);
			}
		break;
		
	case SSM_BLANK:
		Randomize();
		fRedraw = TRUE;
		break;
		
	case SSM_ANIMATE:
		{
		static INT foo;
		if (foo++ & 1)
			return TRUE;
		}
		
		if (GetInputState())
			return TRUE;
		
		if (fRedraw)
			{
			Redraw((CVS) l1);
			if (fRedraw)
				return TRUE;
			}
		
		Animate((CVS) l1);
		break;
		}
	
	return TRUE;
	}


Randomize()
	{
	INT dxc, dyc;
	
	for (dyc = 0; dyc < dycMax; dyc += 1)
		{
		for (dxc = 0; dxc < dxcMax; dxc += 1)
			{
			gridOld[dxc][dyc] = CLR_BACKGROUND << 4;
			}
		}
	
	for (dyc = 0; dyc < dycMax; dyc += 1)
		{
		for (dxc = 0; dxc < dxcMax; dxc += 1)
			{
			if (WRand(10) < 5)
				SetCell(NULL, gridOld, dxc, dyc, WRand(16));
			}
		}
	
	fRedraw = TRUE;
	}


VOID Redraw(CVS hps)
	{
	INT dxc, dyc;
		
	for (dyc = 0; dyc < dycMax; dyc += 1)
		{
		if (GetInputState())
			return;
		
		for (dxc = 0; dxc < dxcMax; dxc += 1)
			{
#ifdef PM
			RECTL rectl;
			LONG clr;
			
			rectl.xLeft = MultDiv(dxc, dxScreen, dxcMax);
			rectl.xRight = MultDiv(dxc + 1, dxScreen, dxcMax);
			rectl.yBottom = MultDiv(dyc, dyScreen, dycMax);
			rectl.yTop = MultDiv(dyc + 1, dyScreen, dycMax);
			
			if ((clr = gridOld[dxc][dyc] >> 4) == CLR_BACKGROUND)
				clr = CLR_BLACK;
			WinFillRect(hps, &rectl, clr);
#endif
#ifdef WIN
			HANDLE hT;
			LOGBRUSH lbrush;
			INT clr, x, y;
			
			if ((clr = gridOld[dxc][dyc] >> 4) == CLR_BACKGROUND)
				clr = CLR_BLACK;
			lbrush.lbStyle = BS_SOLID;
			lbrush.lbColor = mppenrgb[clr];
			lbrush.lbHatch = 0;
			hT = SelectObject(hps,
				CreateBrushIndirect(&lbrush));
			x = MultDiv(dxc, dxScreen, dxcMax);
			y = MultDiv(dyc, dyScreen, dycMax);
			PatBlt(hps, x, y, 
				MultDiv(dxc + 1, dxScreen, dxcMax) - x,
				MultDiv(dyc + 1, dyScreen, dycMax) - y,
				PATCOPY);
			DeleteObject(SelectObject(hps, hT));
#endif
			}
		}

	fRedraw = FALSE;
	}


Animate(CVS hps)
	{
	INT dxc, dyc;
	INT c;
	
	for (dyc = 0; dyc < dycMax; dyc += 1)
		{
		for (dxc = 0; dxc < dxcMax; dxc += 1)
			{
			gridNew[dxc][dyc] = gridOld[dxc][dyc];
			}
		}
	
	for (dyc = 0; dyc < dycMax; dyc += 1)
		{
		for (dxc = 0; dxc < dxcMax; dxc += 1)
			{
			c = gridOld[dxc][dyc] & 0x0f;
			
			if ((gridOld[dxc][dyc] >> 4) != CLR_BACKGROUND)
				{
				if (c < 2 || c > 3)
					{
					SetCell(hps, gridNew, dxc, dyc, 
						CLR_BACKGROUND);
					}
				}
			else
				{
				if (c == 3)
					{
					SetCell(hps, gridNew, dxc, dyc, 
						ClrAverage(gridOld, dxc, dyc));
					}
				}
			}
		}
	
	for (dyc = 0; dyc < dycMax; dyc += 1)
		{
		for (dxc = 0; dxc < dxcMax; dxc += 1)
			{
			gridOld[dxc][dyc] = gridNew[dxc][dyc];
			}
		}
	}



// Boy, this is ugly!  Find the average color around cell [dxc][dyc]...

ClrAverage(GRID grid, INT dxc, INT dyc)
	{
	INT c, clr, clrT;
	
	c = clr = 0;
	
	if (dxc > 0)
		{
		if ((clrT = grid[dxc - 1][dyc] >> 4) != 0)
			{
			clr += clrT;
			c += 1;
			}
		
		if (dyc > 0)
			{
			if ((clrT = grid[dxc - 1][dyc - 1] >> 4) != 0)
				{
				clr += clrT;
				c += 1;
				}
			}
		
		if (dyc < dycMax - 1)
			{
			if ((clrT = grid[dxc - 1][dyc + 1] >> 4) != 0)
				{
				clr += clrT;
				c += 1;
				}
			}
		}
	
	if (dyc > 0)
		{
		if ((clrT = grid[dxc][dyc - 1] >> 4) != 0)
			{
			clr += clrT;
			c += 1;
			}
		}
	
	if (dyc < dycMax - 1)
		{
		if ((clrT = grid[dxc][dyc + 1] >> 4) != 0)
			{
			clr += clrT;
			c += 1;
			}
		}
	
	if (dxc < dxcMax - 1)
		{
		if ((clrT = grid[dxc + 1][dyc] >> 4) != 0)
			{
			clr += clrT;
			c += 1;
			}
		
		if (dyc > 0)
			{
			if ((clrT = grid[dxc + 1][dyc - 1] >> 4) != 0)
				{
				clr += clrT;
				c += 1;
				}
			}
		
		if (dyc < dycMax - 1)
			{
			if ((clrT = grid[dxc + 1][dyc + 1] >> 4) != 0)
				{
				clr += clrT;
				c += 1;
				}
			}
		}
	
	return clr / c;
	}



VOID SetCell(CVS hps, GRID grid, INT dxc, INT dyc, INT clr)
	{
	INT d, clrPrev;
	
	clrPrev = grid[dxc][dyc] >> 4;
	
	if (clr == clrPrev)
		return;
	
	grid[dxc][dyc] &= 0x0f;
	grid[dxc][dyc] |= clr << 4;
	
	if (hps != NULL)
		{
#ifdef PM
		RECTL rectl;
		
		rectl.xLeft = MultDiv(dxc, dxScreen, dxcMax);
		rectl.xRight = MultDiv(dxc + 1, dxScreen, dxcMax);
		rectl.yBottom = MultDiv(dyc, dyScreen, dycMax);
		rectl.yTop = MultDiv(dyc + 1, dyScreen, dycMax);
		
		WinFillRect(hps, &rectl, 
			(clr == CLR_BACKGROUND ? CLR_BLACK : (LONG) clr));
#endif
#ifdef WIN
		HANDLE hT;
		LOGBRUSH lbrush;
		INT x, y;
		
		if (clr == CLR_BACKGROUND)
			clr = CLR_BLACK;
		lbrush.lbStyle = BS_SOLID;
		lbrush.lbColor = mppenrgb[clr];
		lbrush.lbHatch = 0;
		hT = SelectObject(hps,
			CreateBrushIndirect(&lbrush));
		x = MultDiv(dxc, dxScreen, dxcMax);
		y = MultDiv(dyc, dyScreen, dycMax);
		PatBlt(hps, x, y,
			MultDiv(dxc + 1, dxScreen, dxcMax) - x,
			MultDiv(dyc + 1, dyScreen, dycMax) - y,
			PATCOPY);
		DeleteObject(SelectObject(hps, hT));
#endif
		}

	if ((clrPrev == CLR_BACKGROUND) == (clr == CLR_BACKGROUND))
		return;
	
	d = clr > 0 ? 1 : -1;
	
	if (dxc > 0)
		{
		grid[dxc - 1][dyc] += (UCHAR)d;
		
		if (dyc > 0)
			grid[dxc - 1][dyc - 1] += (UCHAR)d;
		
		if (dyc < dycMax - 1)
			grid[dxc - 1][dyc + 1] += (UCHAR)d;
		}
	
	if (dyc > 0)
		grid[dxc][dyc - 1] += (UCHAR)d;
	
	if (dyc < dycMax - 1)
		grid[dxc][dyc + 1] += (UCHAR)d;
	
	if (dxc < dxcMax - 1)
		{
		grid[dxc + 1][dyc] += (UCHAR)d;
		
		if (dyc > 0)
			grid[dxc + 1][dyc - 1] += (UCHAR)d;
		
		if (dyc < dycMax - 1)
			grid[dxc + 1][dyc + 1] += (UCHAR)d;
		}
	}