// Copyright 1999
// College of Computer Science
// Northeastern University Boston MA 02115

// This software may be used for educational purposes as long as this copyright
// notice is retained at the top of all files

// Should this software be modified, the words "Modified from Original" must be
// included as a comment below this notice

// All publication rights are retained.  This software or its documentation may
// not be published in any media either in whole or in part.

///////////////////////////////////////////////////////////////////////////////

// SystemBase.cpp

///////////////////////////////////////////////////////////////////////////////

#include "CoreTools.h"				// needed for core tools compile in Win32
#include "SystemBase.h"


///////////////////////////////////////////////////////////////////////
//                   BEGIN PLATFORM DEPENDENT CODE                   //

#if defined(CORE_PLATFORM_WIN32)


HINSTANCE	CoreTools_hInstance;

WNDCLASSEX	CoreTools_wndclass;

char		CoreTools_wndclassName[] = "CoreTools_wndclass";


// define Core Tools Windows callback function "WndProc"

LRESULT CALLBACK CoreTools_WndProc
	(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	// essential variables in general

	GraphicsWindow* gwp;

	// essential variables for WM_SIZE case

	short xsize, ysize;
	static bool toosmall = false;

	// essential variables for WM_MOVE case

	RectData R;
	NativeRectData N;
	short xspot, yspot;
	short x1, y1, x2, y2;
	static bool badshift = false;

	// always start by obtaining the graphics window pointer

	gwp = GraphicsWindow::GWPtr(hwnd);


	switch (message) {

	case (WM_CREATE):

		return 0;


	case (WM_PAINT):
		gwp->DrawListPaint();

		return 0;


	case (WM_SIZE):

		if (SIZE_RESTORED == wParam) {
			// lParam contains client size data

			xsize = LOWORD(lParam);
			ysize = HIWORD(lParam);

			// test if window is too small
			// but avoid doing resize action if already attempted

			if (toosmall)
				toosmall = false;
			else
				toosmall = (xsize < MinimumClientSize) || (ysize < MinimumClientSize);

			if (toosmall) {
				// grow the window

				xsize = (xsize < MinimumClientSize) ? MinimumClientSize : xsize;
				ysize = (ysize < MinimumClientSize) ? MinimumClientSize : ysize;

				gwp->SetClientSize(xsize, ysize);
			}
			else {
				// update the window specs
				gwp->Update_Window_Data_WIN32();
			}
		}

		return 0;


	case (WM_MOVE):
		// lParam contains coordinates of upper left of client window
		// but this is useless since we need to work with full window
		// hence we will get this information directly

		::GetWindowRect(hwnd, &N);			
		R = N;
		R.Get(x1, y1, x2, y2);

		// test if window has been shifted too far off the screen
		// but avoid doing another shift action if already attempted

		if (badshift)
			badshift = false;
		else {
			xspot = x1;
			yspot = y1;
			AdjustWindowSpot(xspot, yspot);
			badshift = (xspot != x1) || (yspot != y1);
		}

		if (badshift) {
			gwp->SetWindowSpot(xspot, yspot);	// change the window spot
		}
		else {
			gwp->Update_Window_Data_WIN32();	// update the window specs
		}

		// force repaint for test code
		// this will not be done in production code!
		N = gwp->GetClientRect().Native();
		InvalidateRect(hwnd, &N, TRUE);

		return 0;


	case (WM_CLOSE):
		gwp->PrepareToCloseWindow();		// delete structures etc

		// now break so DefWindowProc will initiate the standard closing sequence
		break;


	case (WM_DESTROY):
		// send the message that will eventually terminate both window and thread
		PostQuitMessage(0);
		return 0;


	default:

		break;

	}

	return DefWindowProc(hwnd, message, wParam, lParam);
}



// define and register wndclass for Core Tools applications
// choose settings so that:
//    window is repainted on being resized
//    each window has its own device context

void Define_CoreTools_wndclass() {
	CoreTools_wndclass.cbSize			= sizeof(CoreTools_wndclass);
	CoreTools_wndclass.style			= CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
	CoreTools_wndclass.lpfnWndProc		= CoreTools_WndProc;
	CoreTools_wndclass.cbClsExtra		= 0;
	CoreTools_wndclass.cbWndExtra		= 0;
	CoreTools_wndclass.hInstance		= CoreTools_hInstance;
	CoreTools_wndclass.hIcon			= LoadIcon(NULL, IDI_APPLICATION);
	CoreTools_wndclass.hCursor			= LoadCursor(NULL, IDC_ARROW);
	CoreTools_wndclass.hbrBackground	= (HBRUSH) GetStockObject(WHITE_BRUSH);
	CoreTools_wndclass.lpszMenuName		= NULL;
	CoreTools_wndclass.lpszClassName	= CoreTools_wndclassName;
	CoreTools_wndclass.hIconSm			= LoadIcon(NULL, IDI_APPLICATION);

	if (!RegisterClassEx(&CoreTools_wndclass)) {
		MessageBox(NULL, "RegisterClassEx Failure", CoreTools_wndclassName, MB_ICONERROR);

		throw("RegisterClassEx Failure");
	}
}


// entry-point for Windows DLL

int WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved)
{
	switch (fdwReason)
	{
	case DLL_THREAD_ATTACH:
		break;
		
	case DLL_PROCESS_ATTACH:
		CoreTools_hInstance = hInstance;
		Define_CoreTools_wndclass();
		break;
	
	case DLL_THREAD_DETACH:
		break;

	case DLL_PROCESS_DETACH:
		break;

	default:
		// do nothing
		break;
	}

	return TRUE;
}

#endif	// end WIN32 specific

//                    END PLATFORM DEPENDENT CODE                    //
///////////////////////////////////////////////////////////////////////


// initialization of the GraphicsWindowList called GWList

class GWListClass {

	Array<GraphicsWindow*> List;

public:

	// append graphics window pointer

	bool Append(GraphicsWindow* gwp) { return List.Append(gwp); };

	
	// return effective size of the window list

	int Size() const { return List.ValidSize(); };

	
	// return graphics window pointer at the index

	GraphicsWindow* WindowPtr(int index) {
		// handle -1 default used in certain calls

		if (index < 0)
			index = GraphicsWindowIndex();

		// now get pointer if possible

		int size = Size();

		if ((0 <= index) && (index < size))
			return List[index];
		else
			return 0;
	};

	
	// make the pointer at the index be null
	// this allows the window destructor to remove its own pointer in this list

	void MakeNull(int index) {
		int size = List.ValidSize();

		if ((0 <= index) && (index < size))
			List[index] = 0;
	}

	// make sure all graphics window objects are deleted when list dies

	~GWListClass() {
		GraphicsWindow* gwp;

		int size = List.ValidSize();
		int i;

		for (i = 0; i < size; i++) {
			gwp = List[i];

			if (gwp)
				delete gwp;
		}
	};

} GWList;


// access functions for GraphicsWindowList data

int GraphicsWindowListSize() { return GWList.Size(); }


GraphicsWindow* GraphicsWindowPtr(int index) { return GWList.WindowPtr(index); }


// initialization of default graphics window index

int DefaultGraphicsWindowIndex = 0;


int GraphicsWindowIndex() { return DefaultGraphicsWindowIndex; }


void SetGraphicsWindow(int index) {
	GraphicsWindow* gwp = GraphicsWindowPtr(index);

	if (gwp && gwp->IsOpen())
		DefaultGraphicsWindowIndex = index;
}


void SetGraphicsWindow(const GraphicsWindow& G) {
	if (G.IsOpen())
		DefaultGraphicsWindowIndex = G.Index();
}


// get the area of the screen available for windows

RectData GetScreenRect() {

	RectData ScreenRect;

#if defined(CORE_PLATFORM_WIN32)

	NativeRectData NativeScreenRect;

	SystemParametersInfo(SPI_GETWORKAREA, 0, &NativeScreenRect, 0);

	ScreenRect = NativeScreenRect;

#endif	// end WIN32 specific

#if defined(CORE_PLATFORM_MACOS)


#endif	// end MacOS specific

	return ScreenRect;
}


// GraphicsWindow functions

void GraphicsWindow::Initialize(short xsize, short ysize, short xspot, short yspot) {
	// before opening any graphics window make sure console window is initialized

	InitializeConsole();


	// WindowIndex will become the position of this window in the graphics window list

	WindowIndex = GWList.Size();

	// insert pointer to this window in the graphics window list

	GWList.Append(this);


	// initialize title bar name

	sprintf(TitleBarName, "Graphics %d", WindowIndex);

	// initialize clientsize object

	ClientSize.SetMinAndDef(MinimumClientSize, DefaultClientSize);
	ClientSize.Set(xsize, ysize);

	// initialize upper left corner location

	WindowSpot.Set(xspot, yspot);

	// open the window

	WindowIsOpen = false;


#if defined(CORE_PLATFORM_WIN32)

	// make sure hwnd variable is null before original window creation in WIN32

	hwnd = 0;

	// also initialize the DrawList critical section

	InitializeCriticalSection(& DrawListCS);

#endif


#if defined(CORE_PLATFORM_MACOS)

#endif	// end MacOS specific


	OpenWindow();
}


void GraphicsWindow::PrepareToOpenWindow() {
	// exit if window is open

	if (WindowIsOpen)
		return;

	WindowIsOpen = true;

	// create internal data structures

}


void GraphicsWindow::OpenWindow() {
	// exit if window is open

	if (WindowIsOpen)
		return;

	// set WindowIsOpen to true
	// create internal data structures

	PrepareToOpenWindow();


	// open window via platform specific methods
	//
	// if the functions fail then they must call:
	//    PrepareToCloseWindow();
	// to delete internal data structures and set WindowIsOpen back to false

#if defined(CORE_PLATFORM_WIN32)

	OpenWindow_WIN32();

#endif	// end WIN32 specific

#if defined(CORE_PLATFORM_MACOS)

	OpenWindow_MACOS();

#endif	// end MacOS specific


	// initialize the graphics state

	if (WindowIsOpen) {
		PrepareGraphics();
		WindowState.StartUp();
		NativeState.SetFirst();
		NativeState.StartUp();
		ReleaseGraphics();
	}
}


void GraphicsWindow::PrepareToCloseWindow() {
	// exit if window is not open

	if (!WindowIsOpen)
		return;

	WindowIsOpen = false;

	// delete internal data structures

	DrawListClear();
}


void GraphicsWindow::CloseWindow() {
	// exit if window is not open

	if (!WindowIsOpen)
		return;

	// set WindowIsOpen to false
	// delete internal data structures

	PrepareToCloseWindow();

	// release the graphics state

	PrepareGraphics();
	NativeState.CleanUp();
	ReleaseGraphics();

	// close window via platform specific methods

#if defined(CORE_PLATFORM_WIN32)

	CloseWindow_WIN32();

#endif	// end WIN32 specific

#if defined(CORE_PLATFORM_MACOS)

	CloseWindow_MACOS();

#endif	// end MacOS specific

}


GraphicsWindow::~GraphicsWindow() {
	// close window will delete the data in this window's DrawList

	CloseWindow();

	// zero pointer to this window object in the GraphicsWindowList

	GWList.MakeNull(WindowIndex);


#if defined(CORE_PLATFORM_WIN32)

	// make sure that any existing hwnd for this object has been closed

	if (hwnd)
		CloseHandle(hwnd);

	// also delete the critical section

	DeleteCriticalSection(& DrawListCS);

#endif	// end WIN32 specific


#if defined(CORE_PLATFORM_MACOS)

#endif	// end MacOS specific

}


void GraphicsWindow::ChangeWindowSpecs() {
	// exit if window is not open

	if (!WindowIsOpen)
		return;

#if defined(CORE_PLATFORM_WIN32)

	Adjust_CoreTools_Window_WIN32();

#endif	// end WIN32 specific

#if defined(CORE_PLATFORM_MACOS)

	Adjust_CoreTools_Window_MACOS();

#endif	// end MacOS specific

}


void GraphicsWindow::DrawListInsert(GraphicsObject* clone) {
	DrawListLock();

	DrawList.Append(clone);				// insert the clone

	DrawListUnlock();
}


void GraphicsWindow::DrawListClear() {
	DrawListLock();

	int size = DrawList.ValidSize();
	int i;

	// step 1: delete clone objects in DrawList

	for (i = 0; i < size; i++) {
		delete DrawList[i];			// delete i-th clone object
	}

	// step 2: empty the DrawList

	DrawList.ValidateNone();

	DrawList.ChangeMemory(1);

	DrawListUnlock();
}


void GraphicsWindow::DrawListLock() {

#if defined(CORE_PLATFORM_WIN32)

	EnterCriticalSection(&DrawListCS);

#endif	// end WIN32 specific


#if defined(CORE_PLATFORM_MACOS)

#endif	// end MacOS specific

}


void GraphicsWindow::DrawListUnlock() {

#if defined(CORE_PLATFORM_WIN32)

	LeaveCriticalSection(&DrawListCS);

#endif	// end WIN32 specific


#if defined(CORE_PLATFORM_MACOS)

#endif	// end MacOS specific

}


void GraphicsWindow::PrepareClip() {

#if defined(CORE_PLATFORM_WIN32)

	if (WindowState.EnableClip)
		SelectClipRgn(hdc, NativeState.ClipRgn);
	else
		SelectClipRgn(hdc, NULL);

#endif	// end WIN32 specific


#if defined(CORE_PLATFORM_MACOS)

#endif	// end MacOS specific

}


void GraphicsWindow::PrepareText() {

#if defined(CORE_PLATFORM_WIN32)

	// text color

	RGBdata Color;

	switch(WindowState.PenMode) {
		case PM_Normal:
		case PM_Invert:			// cannot support invert so do normal

			SetTextColor(hdc, WindowState.TextColor.Native());
			break;

		case PM_White:

			Color.Set(255, 255, 255);
			SetTextColor(hdc, Color.Native());
			break;

		case PM_Black:

			Color.Set(  0,   0,   0);
			SetTextColor(hdc, Color.Native());
			break;

		default:
			break;
	}

	// text align

	SetTextAlign(hdc, NativeState.TextAlign);


	// background mode

	switch (WindowState.BackgroundMode) {

		case BM_Opaque:

			SetBkMode(hdc, OPAQUE);

			// background color if needed
			SetBkColor(hdc, WindowState.BackgroundColor.Native());

			break;

		case BM_Transparent:
		default:

			SetBkMode(hdc, TRANSPARENT);
			break;
	}

#endif	// end WIN32 specific


#if defined(CORE_PLATFORM_MACOS)

#endif	// end MacOS specific

}


void GraphicsWindow::PrepareGraphics() {

	DrawListLock();


#if defined(CORE_PLATFORM_WIN32)

	hdc = GetDC(hwnd);

#endif	// end WIN32 specific


#if defined(CORE_PLATFORM_MACOS)

#endif	// end MacOS specific


	PrepareClip();
}


void GraphicsWindow::ReleaseGraphics() {

#if defined(CORE_PLATFORM_WIN32)

	ReleaseDC(hwnd, hdc);

#endif	// end WIN32 specific


#if defined(CORE_PLATFORM_MACOS)

#endif	// end MacOS specific


	DrawListUnlock();
}


void GraphicsWindow::PreparePainting() {

	DrawListLock();


#if defined(CORE_PLATFORM_WIN32)

	hdc = BeginPaint(hwnd, &ps);

#endif	// end WIN32 specific


#if defined(CORE_PLATFORM_MACOS)

#endif	// end MacOS specific

}


void GraphicsWindow::ReleasePainting() {

#if defined(CORE_PLATFORM_WIN32)

	EndPaint(hwnd, &ps);

#endif	// end WIN32 specific


#if defined(CORE_PLATFORM_MACOS)

#endif	// end MacOS specific


	DrawListUnlock();
}


void GraphicsWindow::DrawListPaint() {
	PreparePainting();

	WindowState.StartUp();
	NativeState.StartUp();

	EraseWindow();

	int size = DrawList.ValidSize();
	int i;

	for (i = 0; i < size; i++)
		DrawList[i]->DrawDetails(*this);


#if defined(CORE_PLATFORM_WIN32)

	// WIN32 test code for repaint operation
	// will not be called in production code
	//
	// TestPaint_WIN32();

#endif	// end WIN32 specific


	ReleasePainting();
}


void GraphicsWindow::MakeForeground() const {
	// exit if window is not open

	if (!WindowIsOpen)
		return;

#if defined(CORE_PLATFORM_WIN32)

	SetForegroundWindow(hwnd);

#endif	// end WIN32 specific


#if defined(CORE_PLATFORM_MACOS)

#endif	// end MacOS specific

}


void GraphicsWindow::SetClientSize(short xsize, short ysize) {

	// set desired client extent
	ClientSize.Set(xsize, ysize);

	// now do platform specific window manipulation
	ChangeWindowSpecs();
}


void GraphicsWindow::SetWindowSpot(short xspot, short yspot) {

	// make sure that window spot is reasonable
	AdjustWindowSpot(xspot, yspot);

	// set window upper left location
	WindowSpot.Set(xspot, yspot);

	// now do platform specific window manipulation
	ChangeWindowSpecs();
}


void GraphicsWindow::SetWindowSpecs(short xsize, short ysize, short xspot, short yspot) {

	// set desired client extent
	ClientSize.Set(xsize, ysize);

	// set window upper left location
	WindowSpot.Set(xspot, yspot);

	// now do platform specific window manipulation
	ChangeWindowSpecs();
}


void GraphicsWindow::PlaceRightConsole() {

	short x1, y1, x2, y2;

	GetConsoleCoordinates(x1, y1, x2, y2);

	SetWindowSpot(x2, y1);
}


void GraphicsWindow::PlaceBelowConsole() {

	short x1, y1, x2, y2;

	GetConsoleCoordinates(x1, y1, x2, y2);

	SetWindowSpot(x1, y2);
}


void GraphicsWindow::PlaceRight(const GraphicsWindow& G) {

	short x1, y1, x2, y2;

	G.GetCoordinates(x1, y1, x2, y2);

	SetWindowSpot(x2, y1);
}


void GraphicsWindow::PlaceBelow(const GraphicsWindow& G) {

	short x1, y1, x2, y2;

	G.GetCoordinates(x1, y1, x2, y2);

	SetWindowSpot(x1, y2);
}


void GraphicsWindow::PlaceRight(int index) {
	GraphicsWindow* gwp = GraphicsWindowPtr(index);

	if(gwp)
		PlaceRight(*gwp);
}


void GraphicsWindow::PlaceBelow(int index) {
	GraphicsWindow* gwp = GraphicsWindowPtr(index);

	if(gwp)
		PlaceBelow(*gwp);
}


void GraphicsWindow::ClearWindow() {
	DrawListClear();
	Refresh();
}


void GraphicsWindow::EraseWindow() {

	DrawListLock();

#if defined(CORE_PLATFORM_WIN32)

	// set up hdc for painting client white
	hdc = GetDC(hwnd);

	// invalidate clip region
	SelectClipRgn(hdc, NULL);

	// get client rect
	NativeRectData Client;
	::GetClientRect(hwnd, &Client);

	// manually paint client rect white
	HBRUSH WhiteBrush = (HBRUSH) GetStockObject(WHITE_BRUSH);
	::FillRect(hdc, &Client, WhiteBrush);

	// release hdc
	ReleaseDC(hwnd, hdc);

#endif	// end WIN32 specific

#if defined(CORE_PLATFORM_MACOS)

#endif	// end MacOS specific

	DrawListUnlock();
}


void GraphicsWindow::Refresh() {

	EraseWindow();

#if defined(CORE_PLATFORM_WIN32)

	::InvalidateRect(hwnd, NULL, false);

#endif	// end WIN32 specific


#if defined(CORE_PLATFORM_MACOS)

#endif	// end MacOS specific

}

///////////////////////////////////////////////////////////////////////
//                   BEGIN PLATFORM DEPENDENT CODE                   //

#if defined(CORE_PLATFORM_WIN32)


GraphicsWindow* GraphicsWindow::GWPtr(HWND hwnd) {
	int size = GraphicsWindowListSize();
	int i;

	GraphicsWindow* gwp;

	for (i = 0; i < size; i++) {
		gwp = GraphicsWindowPtr(i);		// i-th window pointer gwp

		if (gwp && gwp->hwnd == hwnd)	// do hwnd's match?
			return gwp;					// if so return gwp
	}

	return 0;							// otherwise return null
}

	
void GraphicsWindow::OpenWindow_WIN32() {

	// create separate thread to initialize and control window

	CreateThreadAndWindow();

}


void GraphicsWindow::CreateThreadAndWindow() {

	// step 1: create window-is-ready event
	// set initial state to FALSE (not ready)
	// set manual  reset to TRUE  (once set leave event in that state)

	eventWindowIsReady = CreateEvent(NULL, TRUE, FALSE, NULL);

	// step 2: create thread
	// note that: this pointer will be passed to CreateWindowAndLoop when it is called
	// in the new thread

	ThreadHandle = CreateThread(NULL, 0, CreateWindowAndLoop, this, 0, &ThreadID);

	// step 3: block application thread until CreateWindowAndLoop has completed window

	WaitForSingleObject(eventWindowIsReady, INFINITE);

	// step 4: close the handle to eventWindowIsReady since we will not need this

	CloseHandle(eventWindowIsReady);
};


// Insert here the static or global routines for window and message loop

DWORD WINAPI GraphicsWindow::CreateWindowAndLoop(LPVOID lp) {

	// step 1: extract the pointer to the graphics window
	
	GraphicsWindow* pThis = (GraphicsWindow*) lp;


	// step 2: create the window in this thread context

	pThis->Create_CoreTools_Window_WIN32();

	// error check on step 3

	if (!(pThis->hwnd)) {
		MessageBox(NULL, "Window Create Failure", pThis->TitleBarName, MB_ICONERROR);

		// delete internal data structures and set WindowIsOpen to false
		pThis->PrepareToCloseWindow();

		// enable event to free application
		SetEvent(pThis->eventWindowIsReady);

		// immediate exit from the thread routine
		return 0;
	}


	// step 3: adjust client area if necessary to best meet caller requirements

	pThis->Adjust_CoreTools_Window_WIN32();


	// step 4: show window and call for initial update operation

	::ShowWindow         (pThis->hwnd, SW_SHOWNORMAL);
	::SetForegroundWindow(pThis->hwnd);
	::UpdateWindow       (pThis->hwnd);


	// step 5: release application thread

	SetEvent(pThis->eventWindowIsReady);


	// step 6: start and run the message loop for this window thread

	return ThreadWindowMessageLoop();
}


DWORD WINAPI GraphicsWindow::ThreadWindowMessageLoop() {
	MSG msg;

	while (true)
	{
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			if (WM_QUIT == msg.message)
				break;
			
			TranslateMessage(&msg);
			DispatchMessage (&msg);
		}
		else
		{
			WaitMessage();	// put the thread to sleep until new message arrives
		}
	}

	return msg.wParam;
}


// Continue with other GraphicsWindow routines

void GraphicsWindow::Create_CoreTools_Window_WIN32() {
	// extract desired client info

	short xsize;
	short ysize;

	short xspot;
	short yspot;

	ClientSize.Get(xsize, ysize);
	WindowSpot.Get(xspot, yspot);

	// make sure that any existing hwnd for this object has been closed

	if (hwnd) {
		CloseHandle(hwnd);
		hwnd = 0;
	}

	// create window

	hwnd = CreateWindow(
		CoreTools_wndclassName,		// wndclass name ties with registered wndlass
		TitleBarName,				// "Graphics xxx"
		WS_OVERLAPPEDWINDOW,		// standard overlapped main window
		xspot,						// x for upperleft corner
		yspot,						// y for upperleft corner
		xsize,						// xsize will initially be of full window
		ysize,						// ysize will initially be of full window
		NULL,						// no parent
		NULL,						// no menu
		CoreTools_hInstance,		// handle to application instance
		NULL						// optional pointer
	);
}


void GraphicsWindow::Adjust_CoreTools_Window_WIN32() {
	// extract desired client info

	short xsize;
	short ysize;

	short xspot;
	short yspot;

	ClientSize.Get(xsize, ysize);
	WindowSpot.Get(xspot, yspot);

	// using existing window information determine extra space needed for borders etc
	// use native rects to get size information from WIN32

	short xExtra;
	short yExtra;

	NativeRectData entire;			// entire window
	NativeRectData client;			// client region normalized with (0,0) at topleft

	::GetWindowRect(hwnd, &entire);
	::GetClientRect(hwnd, &client);

	xExtra = (entire.right - entire.left) - (client.right /* - client.left */);
	yExtra = (entire.bottom - entire.top) - (client.bottom /* - client.top */);

	// now adjust coordinates of entire window to produce desired client window

	int x1 = xspot;
	int y1 = yspot;
	int x2 = xsize + xExtra;
	int y2 = ysize + yExtra;


	// now grow the window

	SetWindowPos(hwnd, HWND_BOTTOM,   x1, y1, x2, y2, SWP_NOZORDER);

	// the parameter  SWP_NOZORDER  says ignore Z-order parameter 2
	//
	// notice that we do not actually show the window in the above call
	// the window is actually opened and brought to the foreground
	// by the subsequent calls in CreateWindowAndLoop
	//
	//    ::ShowWindow         (pThis->hwnd, SW_SHOWNORMAL);
	//    ::SetForegroundWindow(pThis->hwnd);
	//    ::UpdateWindow       (pThis->hwnd);


	// since WIN32 may not have achieved our wishes
	// find out the actual window information
	// and set our internal member data accordingly

	Update_Window_Data_WIN32();
}


void GraphicsWindow::Update_Window_Data_WIN32() {
	// use native rects to get size information from WIN32

	NativeRectData entire;			// entire window
	NativeRectData client;			// client region normalized with (0,0) at topleft

	// find client rect and entire window rect

	::GetClientRect(hwnd, &client);
	::GetWindowRect(hwnd, &entire);

	// save this information

	ClientRect = client;
	ClientSize.Set(client.right, client.bottom);

	WindowRect = entire;
	WindowSpot.Set(entire.left,  entire.top   );
}


void GraphicsWindow::CloseWindow_WIN32() {
	// a window in another thread can only be closed via SendMessage

	SendMessage(hwnd, WM_CLOSE, 0, 0);
}


void GraphicsWindow::WIN32_Show_Region(HRGN Region, HBRUSH NormalBrush) {

	HBRUSH SpecialBrush;

	switch (WindowState.PenMode) {
		case PM_Normal:
			FillRgn(hdc, Region, NormalBrush);
			break;

		case PM_White:
			SpecialBrush = (HBRUSH) GetStockObject(WHITE_BRUSH);
			FillRgn(hdc, Region, SpecialBrush);
			break;

		case PM_Black:
			SpecialBrush = (HBRUSH) GetStockObject(BLACK_BRUSH);
			FillRgn(hdc, Region, SpecialBrush);
			break;

		case PM_Invert:
			InvertRgn(hdc, Region);
			break;

		default:
			break;
	}
}


HRGN GraphicsWindow::WIN32_Line_Region(const PointData& P, const PointData& Q) {

	// grab coordinates

	int x1, y1, x2, y2;
	P.Get(x1, y1);
	Q.Get(x2, y2);

	int xpen = NativeState.xpen;
	int ypen = NativeState.ypen;

	// define region variable

	HRGN LinePixels;

	// vertical line

	if (x1 == x2) {

		if (y1 <= y2)
			LinePixels = CreateRectRgn(x1, y1, x2 + xpen, y2 + ypen);
		else
			LinePixels = CreateRectRgn(x1, y2, x2 + xpen, y1 + ypen);

		return LinePixels;
	}

	// horizontal line

	if (y1 == y2) {

		if (x1 <= x2)
			LinePixels = CreateRectRgn(x1, y1, x2 + xpen, y2 + ypen);
		else
			LinePixels = CreateRectRgn(x2, y1, x1 + xpen, y2 + ypen);

		return LinePixels;
	}

/*	The six sided polygon method is reasonably fast but creates artifacts
	We will use a more painstaking approach below.

	// slant line ... create region as six-sided polygon
	// define point array to set up miniature polygon region

	PointData Points[6];

	if (x1 < x2) {
		if (y1 < y2) {
			Points[0].Set(x1, y1);
			Points[1].Set(x1 + xpen, y1);
			Points[2].Set(x2 + xpen, y2);
			Points[3].Set(x2 + xpen, y2 + ypen);
			Points[4].Set(x2, y2 + ypen);
			Points[5].Set(x1, y1 + ypen);
		} else {
			Points[0].Set(x1, y1);
			Points[1].Set(x2, y2);
			Points[2].Set(x2 + xpen, y2);
			Points[3].Set(x2 + xpen, y2 + ypen);
			Points[4].Set(x1 + xpen, y1 + ypen);
			Points[5].Set(x1, y1 + ypen);
		}
	}
	else {
		if (y1 < y2) {
			Points[0].Set(x2, y2);
			Points[1].Set(x1, y1);
			Points[2].Set(x1 + xpen, y1);
			Points[3].Set(x1 + xpen, y1 + ypen);
			Points[4].Set(x2 + xpen, y2 + ypen);
			Points[5].Set(x2, y2 + ypen);
		} else {
			Points[0].Set(x2, y2);
			Points[1].Set(x2 + xpen, y2);
			Points[2].Set(x1 + xpen, y1);
			Points[3].Set(x1 + xpen, y1 + ypen);
			Points[4].Set(x1, y1 + ypen);
			Points[5].Set(x2, y2 + xpen);
		}
	}

	const NativePointData* polygonpointer = (const NativePointData*) (&Points);

	LinePixels = CreatePolygonRgn(polygonpointer, 6, ALTERNATE);

*/

	// Initialize LinePixels to an empty region

	// Set up Bresenham algorithm 
	// See Foley, van Dam, Feiner, Hughes: Computer Graphics, 2nd Ed., Page 78

	short decision;		// decision variable: along axes versus along diagonal

	short x = x1;		// initial point to plot
	short y = y1;		// initial point to plot

	short dx = AbsValue(x2 - x1);
	short dy = AbsValue(y2 - y1);

	short dmax;			// max of dx and dy
	short dmin;			// min of dx and dy

	short xa;			// increment of x if along axes
	short ya;			// increment of y if along axes
	short ia;			// increment of decision if along axes

	short xd;			// increment of x if along diagonal
	short yd;			// increment of y if along diagonal
	short id;			// increment of decision if along diagonal

	if (dy <= dx) {
		dmax = dx;
		dmin = dy;

		// along x axis
		xa = (x1 < x2) ? 1 : -1;
		ya = 0;

		// along diagonal
		xd = xa;
		yd = (y1 < y2) ? 1 : -1;
	}
	else {
		dmax = dy;
		dmin = dx;
	
		// along y axis
		xa = 0;
		ya = (y1 < y2) ? 1 : -1;

		// along diagonal
		xd = (x1 < x2) ? 1 : -1;
		yd = ya;
	}

	decision = dmin + dmin - dmax;
	ia = decision + dmax;			// dmin + dmin
	id = decision - dmax;			// dmin + dmin - dmax - dmax

	LinePixels = CreateRectRgn(0, 0, 0, 0);
	HRGN Next  = CreateRectRgn(x1, y1, x1 + xpen, y1 + ypen);

	for (short i = 0; i <= dmax; i++) {
		// add the next rectangular block to the line region
		CombineRgn(LinePixels, LinePixels, Next, RGN_OR);
		
		// do Bresenham update
		if (decision <= 0) {
			decision += ia;
			OffsetRgn(Next, xa, ya);
		}
		else {
			decision += id;
			OffsetRgn(Next, xd, yd);
		}
	}

	DeleteObject(Next);

	return LinePixels;
}


void GraphicsWindow::TestPaint_WIN32() const {
	// test paint code
	// see Petzold's HelloWin program for use of DrawText

	short xsize, ysize;			// client size
	short x1, y1, x2, y2;		// window bounds

	RectData R;					// temporary variable
	NativeRectData N;			// temporary variable

	char buffer[81];			// temporary variable for strings to paint

	// get client size

	GetClientSize(xsize, ysize);

	// get window bounds

	R = GetWindowRect();
	R.Get(x1, y1, x2, y2);

	// output the window's specs on two lines 1/3 and 2/3 way down in window
	// in test mode this will overwrite other window contents

	sprintf(buffer, "%d %d", xsize, ysize);			// buffer = client size

	R.Set(0, 0, xsize, (2 * ysize) / 3);			// R is upper two thirds
	N = R.Native();									// N is native R

	DrawText(hdc, buffer, -1, &N,
		DT_SINGLELINE | DT_CENTER | DT_VCENTER);	// center string in N

	sprintf(buffer, "%d %d %d %d", x1, y1, x2, y2);	// buffer = window bounds

	R.Set(0, ysize / 3, xsize, ysize);				// R is lower two thirds
	N = R.Native();									// N is native R

	DrawText(hdc, buffer, -1, &N,
		DT_SINGLELINE | DT_CENTER | DT_VCENTER);	// center string in N

}

#endif	// end WIN32 specific

///////////////////////////////////////////////////////////////////////

#if defined(CORE_PLATFORM_MACOS)

// GraphicsWindow member functions

void GraphicsWindow::OpenWindow_MACOS() {

}


void GraphicsWindow::CloseWindow_MACOS() {

}


void GraphicsWindow::Create_CoreTools_Window_MACOS() {

}


void GraphicsWindow::Adjust_CoreTools_Window_MACOS() {

}


void GraphicsWindow::Update_Window_Data_MACOS() {

}

#endif	// end MacOS specific

//                    END PLATFORM DEPENDENT CODE                    //
///////////////////////////////////////////////////////////////////////

