#include "munga.h"
#pragma hdrstop

#include "graph2d.h"
#include "notation.h"
#include "namelist.h"

//#define DEBUG

struct PCXHeader
{
	Byte manufacturer;
	Byte version;
	Byte encoding;
	Byte bitsPerPixel;
	Word xMin, yMin;
	Word xMax, yMax;
	Word hRes, vRes;
	Byte palette16[48];
	Byte reserved;
	Byte colorPlanes;
	Word bytesPerLine;
	Word paletteType;
	Byte filler[58];
};

Logical
	PCXRead(
		const char *file_name,
		PCXHeader *header,
		Byte **body_ptr,
		Byte *palette_ptr
	)
{
	FILE
		*fp;

	Check_Pointer(file_name);
	Verify(header != NULL);

	fp = fopen(file_name, "rb");
	if (fp == NULL)
	{
		Tell( "PCXRead: Cannot open file '" << file_name << "'\n");
		return False;
	}
	if (fread (header, sizeof(PCXHeader), 1, fp) != 1)
	{
		Tell("PCXRead: Cannot read header from '" << file_name << "'\n");
		fclose(fp);
		return False;
	}
	if (
			(header->manufacturer != 0x0A) ||		// flags 'PCX'-type file
			(header->bitsPerPixel != 8) ||			// we require standard 8-bit
			(header->encoding != 1) ||					// PCX is always encoded
			(header->colorPlanes != 1)					// 256-color ONLY!!!
		)
	{
		Tell("PCXRead: '" << file_name << 
			" 'either not a PCX file, or the wrong type!\n");
		fclose(fp);
		return False;
	}

	int
		data_width(header->bytesPerLine),
		width(header->xMax-header->xMin+1),
		height(header->yMax-header->yMin+1),
		x,
		x2,
		y,
		count;

	Byte
		*dest;

	Byte
		c;


	if (body_ptr == NULL)
	{
		fseek(fp, -769, SEEK_END);						// seek to beginning of palette
	}
	else
	{
		if (*body_ptr != NULL)
		{
      	Unregister_Pointer(*body_ptr);
			delete *body_ptr;
			*body_ptr = NULL;
		}
		*body_ptr = new Byte[width*height];
		Register_Pointer(*body_ptr);

		dest = *body_ptr;
		for(y=height; y>0; --y)
		{
			x2 = width;
			for (x=data_width; x > 0; )
			{
				c = (Byte) fgetc(fp);

				if (ferror(fp))
				{
					Tell("PCXRead: File error reading '" << file_name << "'\n");
					fclose(fp);
               Unregister_Pointer(*body_ptr);
					delete *body_ptr;
					*body_ptr = NULL;
					return False;
				}

				if ((c & 0xC0) == 0xC0)
				{
					count = c & 0x3F;
					x -= count;
					c = (Byte) fgetc(fp);

					while (count--)
					{
						if (x2 > 0)
						{
							--x2;
							*dest++ = c;
						}
					}
				}
				else
				{
					if (x2 > 0)
					{
						--x2;
						*dest++ = c;
					}
					--x;
				}
			}
		}
	}

	if (palette_ptr != NULL)
	{
		Byte
			identifier;

		if (fread (&identifier, 1, 1, fp) != 1)
		{
			Tell("PCXRead: Cannot read palette from '" << file_name << "'\n");
			fclose(fp);
			return False;
		}

		if (identifier != 0x0C)
		{
			Tell("PCXRead: Invalid palette in '" << file_name << "'\n");
			fclose(fp);
			return False;
		} 

		if (fread (palette_ptr, (256*3), 1, fp) != 1)
		{
			Tell("PCXRead: Cannot read palette from '" << file_name << "'\n");
			fclose(fp);
			Check_Fpu();
			return False;
		}
	}

	fclose(fp);
	Check_Fpu();
	return True;
}


//########################################################################
//################################ BitMap ################################
//########################################################################
BitMap *
	BitMap::Make(const char *name)
{
	Check_Pointer(name);

	char
		file_name[80];

	Str_Copy(file_name, "gauge\\", sizeof(file_name));
	Str_Cat(file_name, name, sizeof(file_name));

	BitMap
		*bitmap = new BitMap(file_name);
	Check_Pointer(bitmap);
	if (bitmap->Data.MapPointer == NULL)
	{
		delete bitmap;
		bitmap = NULL;
	}
	else
	{
		Check(bitmap);
	}
	Check_Fpu();
	return bitmap;
}

BitMap *
	BitMap::Make(ResourceDescription::ResourceID /*new_resource_id*/)
{
	Check_Fpu();
	return NULL;		// not yet 'resourcified'
}

BitMap::BitMap(int width, int height)
{
	Check_Pointer(this);
	Verify(width >0);
	Verify(height >0);

	Data.WidthInWords = (width+15)>>4;

	Data.MapPointer = new Word[Data.WidthInWords*height];
	Register_Pointer(Data.MapPointer);
	Data.Size.x = width;
	Data.Size.y = height;
	Check_Fpu();
}

BitMap::BitMap(int width, int height, Word *body)
{
	Check_Pointer(this);
	Check_Pointer(body);

	Data.WidthInWords = 0;
	Data.Size.x = 0;
	Data.Size.y = 0;

	if (width > 0)
	{
		if (height > 0)
		{
			int
				word_width = (width+15)>>4;

			Data.MapPointer = new Word[word_width * height];
			Register_Pointer(Data.MapPointer);
			Data.WidthInWords = word_width;
			Data.Size.x = width;
			Data.Size.y = height;

			Mem_Copy(
				Data.MapPointer,
				body,
				word_width * height * sizeof(Word),
				word_width * height * sizeof(Word)
			);
		}
#		if DEBUG_LEVEL > 0
		else
		{
			Warn("BitMap::BitMap has illicit height");
		}
#		endif
	}
#	if DEBUG_LEVEL > 0
	else
	{
		Warn("BitMap::BitMap has illicit width");
	}
#	endif
	Check_Fpu();
}

BitMap::BitMap(const char *file_name)
{
	Check_Pointer(file_name);

	PCXHeader
		header;
	Byte
		*temp(NULL);

	Data.WidthInWords = 0;
	Data.Size.x = 0;
	Data.Size.y = 0;
	Data.MapPointer = NULL;

	if (PCXRead(file_name, &header, &temp, NULL) == True)
	{
		if (temp != NULL)
		{
			Data.Size.x = header.xMax - header.xMin + 1;
			Data.Size.y = header.yMax - header.yMin + 1;
			Data.WidthInWords = (Data.Size.x + 15) >> 4;
			Data.MapPointer = new Word[Data.WidthInWords * Data.Size.y];
			Check_Pointer(Data.MapPointer);
			Register_Pointer(Data.MapPointer);

			Byte
				*source(temp);
			Word
				*dest(Data.MapPointer);
			Word
				bits(0),
				bitmask(0x8000);
			int
				x,
				y;
			for (y=Data.Size.y; y>0; --y)
			{
				for (x=Data.Size.x; x>0; --x)
				{
					if (*source++ != 0)
					{
						bits |= bitmask;
					}
					bitmask >>= 1;
					if (bitmask == 0)
					{
						*dest++ = bits;
						bitmask = 0x8000;
						bits = 0;
					}
				}

				if (bitmask != 0x8000)
				{
					*dest++ = bits;
					bitmask = 0x8000;
					bits = 0;
				}
			}
         Unregister_Pointer(temp);
			delete temp;
		}
	}
	Check_Fpu();
}

BitMap::BitMap(NotationFile *notation_file, const char *page_name)
{
	Check_Pointer(this);
	Check(notation_file);
	Check_Pointer(page_name);

	int
		x,
		y,
		width_in_words;

	//-----------------------------------------
	// Render bitmap benign in case of failure
	//-----------------------------------------
	Data.Size.x = 0; 
	Data.Size.y = 0; 
	Data.WidthInWords = 0;
	Data.MapPointer = NULL;
	//--------------------------------------------
	// Attempt to read the x, y, and width values
	//--------------------------------------------
	if (!notation_file->GetEntry(page_name, "x", &x))
	{
		Warn("BitMap::BitMap notation file: 'x' not found!\n");
		return;
	}
	Verify(x > 0);

	if (!notation_file->GetEntry(page_name, "y", &y))
	{
		Warn("BitMap::BitMap notation file: 'y' not found!\n");
		return;
	}
	Verify(y > 0);

	if (!notation_file->GetEntry(page_name, "width", &width_in_words))
	{
		Warn("BitMap::BitMap notation file: 'width' not found!\n");
		return;
	}
	//Tell("BitMap::BitMap x=" << x << ", y=" << y << ", width_in_words=" << width_in_words << "\n");
	Verify(x <= (width_in_words << 4));

	//--------------------------------------------
	// Attempt to allocate the map
	//--------------------------------------------
	Data.MapPointer = new Word[width_in_words * y];
	Register_Pointer(Data.MapPointer);
	//--------------------------------------------
	// So far, so good! Save the values
	//--------------------------------------------
	Data.Size.x = x;
	Data.Size.y = y;
	Data.WidthInWords = width_in_words;
	//--------------------------------------------
	// Read the map data
	//--------------------------------------------
	NameList *lines =
		notation_file->MakeEntryList(page_name);
	Check(lines);
	Register_Object(lines);

	NameList::Entry
		*line_entry;
	const char
		*text;
	int
		collect = 0,
		nybble,
		nybble_count = 4;
	Word
		*destination = Data.MapPointer;

	//--------------------------------------------
	// Collect all the entries in the page
	//--------------------------------------------
	for (
			line_entry = lines->GetFirstEntry();
			line_entry != NULL;
			line_entry = line_entry->GetNextEntry()
		 )
	{
		//--------------------------------------------
		// Get the entry name
		//--------------------------------------------
		if ((text=line_entry->GetName()) != NULL)
		{
			//--------------------------------------------
			// Process only if it's a 'bitmap' entry
			//--------------------------------------------
			if (strcmp(text, "bitmap") == 0)
			{
				//--------------------------------------------
				// Get the entry text
				//--------------------------------------------
				text=line_entry->GetChar();
				Verify(text != NULL);
				//--------------------------------------------
				// Parse the 'bitmap' entry
				//--------------------------------------------
				for ( ; *text != '\0'; ++text)
				{
					//--------------------------------------------
					// Convert char to hex nybble
					//--------------------------------------------
					nybble = toupper(*text);
					Verify(isxdigit(nybble));

					if (nybble > '9')
					{
						nybble -= ('A' - '9' - 1);
					}
					nybble -= '0';
					//--------------------------------------------
					// Collect in a word
					//--------------------------------------------
					collect = (collect << 4) + nybble;
					//--------------------------------------------
					// If a full word is collected, save it
					//--------------------------------------------
					--nybble_count;
					if (nybble_count <= 0)
					{
						//--------------------------------------------
						// Verify that we've not gone too far
						//--------------------------------------------
						if ((destination-Data.MapPointer) >= (width_in_words*y))
						{
							Tell("BitMap::BitMap(notation file) attempted overrun");
							break;
						}
						//--------------------------------------------
						// Save it, set up for next word
						//--------------------------------------------
						*destination++ = (Word) collect;
						nybble_count = 4;
						collect = 0;
					}
				}
			}
		}
	}
	Unregister_Object(lines);
	delete lines;
	//--------------------------------------------
	// Make sure we collected the proper
	// amount of data-
	//--------------------------------------------
//	Verify(nybble_count == 4);
//	Verify(word_count == 0);
	Check_Fpu();
}

BitMap::~BitMap()
{
	Check(this);

	if (Data.MapPointer != NULL)
	{
		Unregister_Pointer(Data.MapPointer);
		delete Data.MapPointer;
		Data.Size.x = 0;				// safety code
		Data.Size.y = 0;
		Data.MapPointer = NULL;
	}
	Check_Fpu();
}

Logical
	BitMap::TestInstance() const
{
	Verify(Data.Size.x > 0);
	Verify(Data.Size.x < 8192);		// unreasonably large
	Verify(Data.Size.y > 0);
	Verify(Data.Size.y < 8192);		// unreasonably large
	Verify(Data.WidthInWords == ((Data.Size.x + 15) >> 4));
	Check_Pointer(Data.MapPointer);
	Check_Fpu();
	return True;
}

void
	BitMap::ShowInstance(char * indent)
{
	Check(this);

	std::cout << indent << "BitMap:\n";
	char
		temp[80];

	Str_Copy(temp, indent, 80);
	Str_Cat(temp, "...", 80);

	std::cout << temp << "Size        =" << Data.Size << "\n";
	std::cout << temp << "WidthInWords=" << Data.WidthInWords << "\n";
	std::cout << temp << "MapPointer  =" << (void *) Data.MapPointer << "\n";
	std::cout << std::flush;

	if (Data.MapPointer != NULL)
	{
		int
			x,
   		y,
   		bits,
			bitmask,
			bitwidth;

   	Word
   		*source((Word *) Data.MapPointer);

   	for(y=Data.Size.y; y>0; y--)
   	{
			std::cout << temp;
			bitwidth=0;
			for(x=Data.WidthInWords; x>0; --x)
			{
				bits = *source++;

				for(bitmask=0x8000; bitmask!=0; bitmask>>=1)
				{
					if (++bitwidth > Data.Size.x)
					{
						std::cout << "-";
					}
					else if (bits & bitmask)
					{
						std::cout <<"#";
					}
					else
					{
						std::cout <<" ";
					}
				}
			}
			std::cout << "\n";
		}
		std::cout << "\n";
	}
	Check_Fpu();
}

//########################################################################
//############################### Palette8 ###############################
//########################################################################

//
//------------------------------------------------------------------------
// Output stream operator
//------------------------------------------------------------------------
//
std::ostream& operator<<(std::ostream& Stream, const PaletteTriplet& p)
{
	Check_Fpu();
	return Stream << std::hex <<
		(((int) p.Red)   & 0xFF) << ':' <<
		(((int) p.Green) & 0xFF) << ':' << 
		(((int) p.Blue)  & 0xFF) << std::dec;
}

Palette8::Palette8()
{
	Check_Pointer(this);
	Valid = False;
	Check_Fpu();
}

Palette8 *
	Palette8::Make(const char *name)
{
	Check_Pointer(name);

	char
		file_name[80];

	Str_Copy(file_name, "gauge\\", sizeof(file_name));
	Str_Cat(file_name, name, sizeof(file_name));


	Palette8
		*palette = new Palette8(file_name);
	Check_Pointer(palette);
	if (!palette->Valid)
	{
		delete palette;
		palette = NULL;
	}
	Check_Fpu();
	return palette;
}

Palette8 *
	Palette8::Make(ResourceDescription::ResourceID /*new_resource_id*/)
{
	Check_Fpu();
	return NULL;		// not yet 'resourcified'
}



Palette8::Palette8(int count, PaletteTriplet *source)
{
	Check_Pointer(this);

	int
		i;

	Check_Pointer(source);
	Verify(count > 0);
	Verify(count <= 256);

	for(i=0; i<count; ++i)
	{
		Color[i] = *source++;
	}
	Valid = True;
	Check_Fpu();
}

Palette8::Palette8(const char *file_name)
{
	Check_Pointer(this);
	Check_Pointer(file_name);

	PCXHeader
		header;

	Valid = PCXRead(file_name, &header, NULL, (Byte *) Color);
	Check_Fpu();
}

Palette8::~Palette8()
{
	Check(this);
	Check_Fpu();
}

void
	Palette8::SetColorRange(int start, int end, PaletteTriplet new_color)
{
	Check(this);

	Verify(start >= 0);
	Verify(end < 256);
	Verify(start <= end);

	for( ; start <= end; ++start)
	{
		Color[start] = new_color;
	}
	Check_Fpu();
}

void
	Palette8::BuildColorRange(
		int start,
		int end,
		PaletteTriplet first_color,
		PaletteTriplet last_color
	)
{
	Check(this);

	int
		range,
		rate_red,
		rate_green,
		rate_blue,
		accum_red,
		accum_green,
		accum_blue;

	Verify(start >= 0);
	Verify(end < 256);
	Verify(start <= end);

	range = end - start;

	if (range == 0)
	{
		Color[start] = first_color;
	}
	else
	{
		rate_red   = (((int)(last_color.Red   - first_color.Red)  ) << 5)/range;
		rate_green = (((int)(last_color.Green - first_color.Green)) << 5)/range;
		rate_blue  = (((int)(last_color.Blue  - first_color.Blue) ) << 5)/range;

		accum_red   = ((int)first_color.Red)   << 5;
		accum_green = ((int)first_color.Green) << 5;
		accum_blue  = ((int)first_color.Blue)  << 5;

		for( ; start<=end; ++start)
		{
			Color[start].Red   = (Byte) (accum_red   >> 5);
			Color[start].Green = (Byte) (accum_green >> 5);
			Color[start].Blue  = (Byte) (accum_blue  >> 5);

			accum_red   += rate_red;
			accum_green += rate_green;
			accum_blue  += rate_blue;
		}
	}
	Check_Fpu();
}

Logical
	Palette8::TestInstance() const
{
	return True;
}

void
	Palette8::ShowInstance(char *indent)
{
	Check(this);

	char
		temp[80],
		buffer[32];
	PaletteTriplet
		*source;
	int
		x,
		y;

	std::cout << indent << "Palette8:\n";

	Str_Copy(temp, indent, 80);
	Str_Cat(temp, "...", 80);

	std::cout << temp << "Valid =" << Valid << "\n";

	if (Valid)
	{
		source = Color;
		for(y=0; y<256; y+=4)
		{
			sprintf(buffer, "%02X= ", y);
			std::cout << temp << buffer;

			for(x=4; x>0; --x)
			{
				std::cout << *source++ << " "; 
			}
			std::cout << "\n";
		}
	}
	Check_Fpu();
}

//########################################################################
//############################## PixelMap8 ###############################
//########################################################################
PixelMap8 *
	PixelMap8::Make(const char *name)
{
	Check_Pointer(name);

	char
		file_name[80];

	Str_Copy(file_name, "gauge\\", sizeof(file_name));
	Str_Cat(file_name, name, sizeof(file_name));

	PixelMap8
		*pixelmap = new PixelMap8(file_name);
	Check_Pointer(pixelmap);
	if (pixelmap->Data.MapPointer == NULL)
	{
		delete pixelmap;
		pixelmap = NULL;
	}
	else
	{
		Check(pixelmap);
	}
	Check_Fpu();
	return pixelmap;
}

PixelMap8 *
	PixelMap8::Make(ResourceDescription::ResourceID /*new_resource_id*/)
{
	Check_Fpu();
	return NULL;		// not yet 'resourcified'
}

PixelMap8::PixelMap8(int width, int height)
{
	Check_Pointer(this);
	Verify(width >0);
	Verify(height >0);

	Data.Size.x = width;
	Data.Size.y = height;
	Data.MapPointer = new Byte[width*height];
	Register_Pointer(Data.MapPointer);
	Check_Fpu();
}

PixelMap8::PixelMap8(const char *file_name)
{
	Check_Pointer(file_name);
	PCXHeader
		header;

	Data.MapPointer = NULL;

	if (PCXRead(file_name, &header, &Data.MapPointer, NULL) == True)
	{
		if (Data.MapPointer == NULL)
		{
			Data.Size.x = 0;
			Data.Size.y = 0;
		}
		else
		{
			//Data.MapPointer already registered by PCXRead()
			Data.Size.x = header.xMax - header.xMin + 1;
			Data.Size.y = header.yMax - header.yMin + 1;
		}
	}
	Check_Fpu();
}

PixelMap8::~PixelMap8()
{
	Check(this);

	if (Data.MapPointer != NULL)
	{
		Unregister_Pointer(Data.MapPointer);
		delete Data.MapPointer;
		Data.Size.x = 0;					// safety code
		Data.Size.y = 0;
		Data.MapPointer = NULL;
	}
	Check_Fpu();
}

Logical
	PixelMap8::TestInstance() const
{
	Verify (Data.Size.x >0);
	Verify (Data.Size.x < 8192);	// an unreasonably large image!
	Verify (Data.Size.y >0);
	Verify (Data.Size.y < 8192);	// an unreasonably large image!
	Check_Pointer(Data.MapPointer);
	Check_Fpu();
	return True;
}

void
	PixelMap8::ShowInstance(char *indent)
{
	Check(this);

	char
		temp[80],
		buffer[32];
	Byte
		*source;
	int
		x,
		y;

	std::cout << indent << "PixelMap8:\n";

	Str_Copy(temp, indent, 80);
	Str_Cat(temp, "...", 80);

	std::cout << temp << "Size        =" << Data.Size << "\n";
	std::cout << temp << "MapPointer  =" << (void *) Data.MapPointer << "\n";

	source = Data.MapPointer;
	if (source != NULL)
	{
		for(y=Data.Size.y; y>0; --y)
		{
			std::cout << temp;
			for(x=Data.Size.x; x>0; --x)
			{
				sprintf(buffer, "%02X ",*source++);
				std::cout << buffer;
			}
			std::cout << "\n";
		}
	}
	Check_Fpu();
}

//########################################################################
//############################## PixelMap16 ##############################
//########################################################################
PixelMap16::PixelMap16(int width, int height)
{
	Check_Pointer(this);
	Verify(width >0);
	Verify(height >0);

	Data.Size.x = width;
	Data.Size.y = height;
	Data.MapPointer = new Word[width*height];
	Register_Pointer(Data.MapPointer);

	Check_Fpu();
}

PixelMap16::PixelMap16(char *)
{
	Fail("PixelMap16::PixelMap16(char *) UNIMPLEMENTED\n");
	Data.MapPointer = NULL;
	Data.Size.x = 0;
	Data.Size.y = 0;
	Check_Fpu();
}

PixelMap16::~PixelMap16()
{
	Check(this);

	if (Data.MapPointer != NULL)
	{
		Unregister_Pointer(Data.MapPointer);
		delete Data.MapPointer;
		Data.Size.x = 0;					// safety code
		Data.Size.y = 0;
		Data.MapPointer = NULL;
	}
	Check_Fpu();
}

Logical
	PixelMap16::TestInstance() const
{
	return True;
}

void
	PixelMap16::ShowInstance(char *indent)
{
	Check(this);

	char
		temp[80];

	std::cout << indent << "PixelMap16:\n";

	Str_Copy(temp, indent, 80);
	Str_Cat(temp, "...", 80);

	std::cout << temp << "Size        =" << Data.Size << "\n";
	std::cout << temp << "MapPointer  =" << (void *) Data.MapPointer << "\n";
	Check_Fpu();
}

//########################################################################
//########################### GraphicsDisplay ############################
//########################################################################
GraphicsDisplay::GraphicsDisplay(int x, int y)
{
	Check_Pointer(this);
	Verify(x >0);
	Verify(y >0);

	bounds.bottomLeft.x = 0;
	bounds.bottomLeft.y = 0;
	bounds.topRight.x = x-1;
	bounds.topRight.y = y-1;
	Check_Fpu();
}

GraphicsDisplay::~GraphicsDisplay()
{
	Check(this);
	Check_Fpu();
}

void
	GraphicsDisplay::ShowInstance(char *indent)
{
	Check(this);
	std::cout << indent << "GraphicsDisplay:\n";

	char
		temp[80];

	Str_Copy(temp, indent, 80);
	Str_Cat(temp, "...", 80);

	std::cout << temp << "bounds =" << bounds << "\n";
	Check_Fpu();
}

Logical
	GraphicsDisplay::TestInstance() const
{
	return True;
}

Logical
	GraphicsDisplay::Update(Logical)
{
	// no-op for base class
	return False;		// always returns 'all done'
}

void
	GraphicsDisplay::WaitForUpdate()
{
	// no-op for base class: immediately returns
}

Rectangle2D
	GraphicsDisplay::TextBounds(
		Enumeration,
		Logical,
		GraphicsDisplay::Justification,
		char *
	)
{
	Fail("GraphicsDisplay::TextBounds(...) is UNIMPLEMENTED\n");

	Rectangle2D
		r;
	Check_Fpu();
	return r;
}

void
	GraphicsDisplay::DrawPoint(
		int /*color*/,
		int /*bitmask*/,
		Enumeration /*operation*/,
		int /*x*/,
		int /*y*/
	)
{
	Fail("GraphicsDisplay::DrawPoint not overridden");
}

void
	GraphicsDisplay::DrawLine(
		int /*color*/,
		int /*bitmask*/,
		Enumeration /*operation*/,
		int /*x1*/,
		int /*y1*/,
		int /*x2*/,
		int /*y2*/,
		Logical /*include_last_pixel*/
	)
{
	Fail("GraphicsDisplay::DrawLine not overridden");
}

void
	GraphicsDisplay::DrawFilledRectangle(
		int /*color*/,
		int /*bitmask*/,
		Enumeration /*operation*/,
		int /*x1*/,
		int /*y1*/,
		int /*x2*/,
		int /*y2*/
	)
{
	Fail("GraphicsDisplay::DrawFilledRectangle not overridden");
}

void
	GraphicsDisplay::DrawText(
		int /*color*/,
		int /*bitmask*/,
		Enumeration /*operation*/,
		Logical /*opaque*/,
		int /*rotation*/,
		Enumeration /*fontNumber*/,
		Logical /*vertical*/,
		GraphicsDisplay::Justification /*justification*/,
		Rectangle2D	* /*clippingRectanglePtr*/,
		char * /*stringPointer*/
	)
{
	Fail("GraphicsDisplay::DrawText not overridden");
}

void
	GraphicsDisplay::DrawBitMap(
		int /*color*/,
		int /*bitmask*/,
		Enumeration /*operation*/,
		int /*rotation*/,
		int /*x*/,
		int /*y*/,
		BitMap * /*bitmap*/,
		int /*sx1*/,
		int /*sy1*/,
		int /*sx2*/,
		int /*sy2*/	// these are SOURCE coordinates
	)
{
	Fail("GraphicsDisplay::DrawBitMap not overridden");
}

void
	GraphicsDisplay::DrawBitMapOpaque(
		int /*color*/,
		int /*background*/,
		int /*bitmask*/,
		Enumeration /*operation*/,
		int /*rotation*/,
		int /*x*/,
		int /*y*/,
		BitMap * /*bitmap*/,
		int /*sx1*/,
		int /*sy1*/,
		int /*sx2*/,
		int /*sy2*/	// these are SOURCE coordinates
	)
{
	Fail("GraphicsDisplay::DrawBitMapOpaque not overridden");
}

void
	GraphicsDisplay::DrawPixelMap8(
		int * /*translation_table*/,
		int /*bitmask*/,
		Enumeration /*operation*/,
		Logical /*opaque*/,
		int /*rotation*/,
		int /*x*/,
		int /*y*/,
		PixelMap8	* /*pixelmap*/,
		int /*sx1*/,
		int /*sy1*/,
		int /*sx2*/,
		int /*sy2*/	// these are SOURCE coordinates
	)
{
	Fail("GraphicsDisplay::DrawPixelMap8 not overridden");
}

void
	GraphicsDisplay::DrawPixelMap8SingleColor(
		int /*color*/,
		int /*bitmask*/,
		Enumeration /*operation*/,
		int /*rotation*/,
		int /*x*/,
		int /*y*/,
		PixelMap8	* /*pixelmap*/,
		int /*sx1*/,
		int /*sy1*/,
		int /*sx2*/,
		int /*sy2*/	// these are SOURCE coordinates
	)
{
	Fail("GraphicsDisplay::DrawPixelMap8SingleColor not overridden");
}

//########################################################################
//############################ GraphicsPort ##############################
//########################################################################
GraphicsPort::GraphicsPort(
	GraphicsDisplay *graphics_display,
	const char *new_name
)
{
	Check_Pointer(this);

	if (graphics_display != NULL)
	{
		Check(graphics_display);
		bounds = graphics_display->bounds;
	}
	else
	{
		bounds.bottomLeft.x = 0;
		bounds.bottomLeft.y = 0;
		bounds.topRight.x = 0;
		bounds.topRight.y = 0;
	}
	graphicsDisplay = graphics_display;

	Str_Copy(name, new_name, sizeof(name));
	Check_Fpu();
}

GraphicsPort::~GraphicsPort()
{
	Check(this);
	Check_Fpu();
}

Logical
	GraphicsPort::TestInstance() const
{
	return True;
}

void
	GraphicsPort::ShowInstance(char *indent)
{
	Check(this);

	std::cout << indent << "GraphicsPort:\n";

	char
		temp[80];

	Str_Copy(temp, indent, 80);
	Str_Cat(temp, "...", 80);

	std::cout << temp << "bounds         =" << bounds << "\n";
	std::cout << temp << "graphicsDisplay=" << graphicsDisplay << "\n";
	std::cout << std::flush;
	Check_Fpu();
}

Rectangle2D
	GraphicsPort::TextBounds(
		Enumeration fontNumber,
		Logical vertical,
		GraphicsDisplay::Justification justification,
		char *stringPointer
	)
{
	Check(this);
	Check(graphicsDisplay);				// HACK!!!! Check for NULL!

	Check_Fpu();
	return
		graphicsDisplay->
			TextBounds(
				fontNumber,
				vertical,
				justification,
				stringPointer
			);
}

void
	GraphicsPort::SetColor(
		PaletteTriplet * /*source_color*/,
		int /*color_index*/
	)
{
	Fail("GraphicsPort::SetColor not overridden");
}

void
	GraphicsPort::DrawPoint(
		int /*color*/,
		Enumeration /*operation*/,
		int /*x*/,
		int /*y*/
	)
{
	Fail("GraphicsPort::DrawPoint not overridden");
}

void
	GraphicsPort::DrawLine(
		int /*color*/,
		Enumeration /*operation*/,
		int /*x1*/,
		int /*y1*/,
		int /*x2*/,
		int /*y2*/,
		Logical /*include_last_pixel*/
	)
{
	Fail("GraphicsPort::DrawLine not overridden");
}

void
	GraphicsPort::DrawFilledRectangle(
		int /*color*/,
		Enumeration /*operation*/,
		int /*x1*/,
		int /*y1*/,
		int /*x2*/,
		int /*y2*/
	)
{
	Fail("GraphicsPort::DrawFilledRectangle not overridden");
}

void
	GraphicsPort::DrawText(
		int /*color*/,
		Enumeration /*operation*/,
		Logical /*opaque*/,
		int /*rotation*/,
		Enumeration /*fontNumber*/,
		Logical /*vertical*/,
		GraphicsDisplay::Justification /*justification*/,
		Rectangle2D	* /*clippingRectanglePtr*/,
		char * /*stringPointer*/
	)
{
	Fail("GraphicsPort::DrawText not overridden");
}

void
	GraphicsPort::DrawBitMap(
		int /*color*/,
		Enumeration /*operation*/,
		int /*rotation*/,
		int /*x*/,
		int /*y*/,
		BitMap * /*bitmap*/,
		int /*sx1*/,
		int /*sy1*/,
		int /*sx2*/,
		int /*sy2*/	// these are SOURCE coordinates
	)
{
	Fail("GraphicsPort::DrawBitMap not overridden");
}

void
	GraphicsPort::DrawBitMapOpaque(
		int /*color*/,
		int /*background*/,
		Enumeration /*operation*/,
		int /*rotation*/,
		int /*x*/,
		int /*y*/,
		BitMap * /*bitmap*/,
		int /*sx1*/,
		int /*sy1*/,
		int /*sx2*/,
		int /*sy2*/	// these are SOURCE coordinates
	)
{
	Fail("GraphicsPort::DrawBitMapOpaque not overridden");
}

void
	GraphicsPort::DrawPixelMap8(
		Enumeration /*operation*/,
		Logical /*opaque*/,
		int /*rotation*/,
		int /*x*/,
		int /*y*/,
		PixelMap8	* /*pixelmap*/,
		int /*sx1*/,
		int /*sy1*/,
		int /*sx2*/,
		int /*sy2*/	// these are SOURCE coordinates
	)
{
	Fail("GraphicsPort::DrawPixelMap8 not overridden");
}

void
	GraphicsPort::DrawPixelMap8SingleColor(
		int /*color*/,
		Enumeration /*operation*/,
		int /*rotation*/,
		int /*x*/,
		int /*y*/,
		PixelMap8	* /*pixelmap*/,
		int /*sx1*/,
		int /*sy1*/,
		int /*sx2*/,
		int /*sy2*/	// these are SOURCE coordinates
	)
{
	Fail("GraphicsPort::DrawPixelMap8SingleColor not overridden");
}

//########################################################################
//######################### GraphicsViewRecord ###########################
//########################################################################
GraphicsViewRecord::GraphicsViewRecord()
{
	Check_Pointer(this);

	commandListLength = 4096;
	commandListIndex = 0;
	commandList = new char[commandListLength];
	Register_Pointer(commandList);
	link = NULL;
	Check_Fpu();
}

GraphicsViewRecord::~GraphicsViewRecord()
{
	Check(this);

	if (link != NULL)
	{
		Check(link);
		Unregister_Object(link);
		delete link;
		link = NULL;			// safety code
	}

	if (commandList != NULL)
	{
		Unregister_Pointer(commandList);
		delete commandList;
		commandList = NULL;			// safety code
	}
	Check_Fpu();
}

Logical
	GraphicsViewRecord::TestInstance() const
{
	return True;
}

void
	GraphicsViewRecord::Clear()
{
	Check(this);

	if (link != NULL)
	{
		Check(link);
		link->Clear();
	}

	commandListIndex = 0;
	Check_Fpu();
}

void
	GraphicsViewRecord::Draw(GraphicsView *graphics_view, int forced_color)
{
	Check(this);

	GraphicsPort
		*graphics_port(graphics_view->graphicsPort);
	if (graphics_port == NULL)
	{
		Clear();
		return;
	}

	PixelMap8
		*pixelmap_pointer;
	BitMap
		*bitmap_pointer;

	int
		running=1,
		color, bg,
		op,
		x1, y1, x2, y2,
		sx1, sy1, sx2, sy2,
		rot, q;

	//--------------------------------------------------------------
	// Mark end of list, rewind to beginning
	//--------------------------------------------------------------
	PutChar(GraphicsViewRecord::endCommand);
	Clear();
	//--------------------------------------------------------------
	// Parse the command stream
	//--------------------------------------------------------------
	while(running)
	{
		switch(GetChar())
		{
		case endCommand:
			running = 0;
			break;

#define RECORDER_POINT(recorder, color, op, x1, y1) \
	if (recorder != NULL) \
	{ \
		recorder->PutChar(GraphicsViewRecord::pointCommand); \
		recorder->PutShort(color); \
		recorder->PutChar(op); \
		recorder->PutShort(x1); \
		recorder->PutShort(y1); \
	}
		case pointCommand:
			color = GetShort();
			op =    GetChar();
			x1 =    GetShort();
			y1 =    GetShort();

			if (forced_color != -1)
			{
				color = forced_color;
			}
			graphics_port->DrawPoint(color, op, x1, y1);
			break;

#define RECORDER_LINE(recorder, color, op, x1, y1, x2, y2, endpoint) \
	if (recorder != NULL) \
	{ \
		recorder->PutChar(GraphicsViewRecord::lineCommand); \
		recorder->PutShort(color); \
		recorder->PutChar(op); \
		recorder->PutShort(x1); \
		recorder->PutShort(y1); \
		recorder->PutShort(x2); \
		recorder->PutShort(y2); \
		recorder->PutLogical(endpoint); \
	}
		case lineCommand:
			color = GetShort();
			op = GetChar();
			x1 = GetShort();
			y1 = GetShort();
			x2 = GetShort();
			y2 = GetShort();
			q =  GetLogical();

			if (forced_color != -1)
			{
				color = forced_color;
			}
			graphics_port->DrawLine(color, op, x1, y1, x2, y2, q);
			break;

#define RECORDER_FRECT(recorder, color, op, x1, y1, x2, y2) \
	if (recorder != NULL) \
	{ \
		recorder->PutChar(GraphicsViewRecord::frectCommand); \
		recorder->PutShort(color); \
		recorder->PutChar(op); \
		recorder->PutShort(x1); \
		recorder->PutShort(y1); \
		recorder->PutShort(x2); \
		recorder->PutShort(y2); \
	}
		case frectCommand:
			color = GetShort();
			op = GetChar();
			x1 = GetShort();
			y1 = GetShort();
			x2 = GetShort();
			y2 = GetShort();

			if (forced_color != -1)
			{
				color = forced_color;
			}
			graphics_port->DrawFilledRectangle(color, op, x1, y1, x2, y2);
			break;

#define RECORDER_BITMAP(r, color, op, rot, x1, y1, p, sx1, sy1, sx2, sy2) \
	if (r != NULL) \
	{ \
		r->PutChar(GraphicsViewRecord::bitmapCommand); \
		r->PutShort(color); \
		r->PutChar(op); \
		r->PutShort(rot); \
		r->PutShort(x1); \
		r->PutShort(y1); \
		r->PutPointer(p); \
		r->PutShort(sx1); \
		r->PutShort(sy1); \
		r->PutShort(sx2); \
		r->PutShort(sy2); \
	}
		case bitmapCommand:
			color = GetShort();
			op  = GetChar();
			rot = GetShort();
			x1  = GetShort();
			y1  = GetShort();
			bitmap_pointer = (BitMap *) GetPointer();
			sx1 = GetShort();
			sy1 = GetShort();
			sx2 = GetShort();
			sy2 = GetShort();

			Check(bitmap_pointer);

			if (forced_color != -1)
			{
				color = forced_color;
			}
			graphics_port->DrawBitMap(
				color,
				op,
				rot,
				x1, y1,
				bitmap_pointer,
				sx1, sy1, sx2, sy2
			);
			break;

#define RECORDER_BITMAP_OPAQUE( \
		r, fg, bg, op, rot, x1, y1, p, sx1, sy1, sx2, sy2) \
	if (r != NULL) \
	{ \
		r->PutChar(GraphicsViewRecord::bitmapOpaqueCommand); \
		r->PutShort(fg); \
		r->PutShort(bg); \
		r->PutChar(op); \
		r->PutShort(rot); \
		r->PutShort(x1); \
		r->PutShort(y1); \
		r->PutPointer(p); \
		r->PutShort(sx1); \
		r->PutShort(sy1); \
		r->PutShort(sx2); \
		r->PutShort(sy2); \
	}
		case bitmapOpaqueCommand:
			color = GetShort();
			bg  = GetShort();
			op  = GetChar();
			rot = GetShort();
			x1  = GetShort();
			y1  = GetShort();
			bitmap_pointer = (BitMap *) GetPointer();
			sx1 = GetShort();
			sy1 = GetShort();
			sx2 = GetShort();
			sy2 = GetShort();

			Check(bitmap_pointer);

			if (forced_color != -1)
			{
				color = forced_color;
			}
			graphics_port->DrawBitMapOpaque(
				color,
				bg,
				op,
				rot,
				x1, y1,
				bitmap_pointer,
				sx1, sy1, sx2, sy2
			);
			break;

#define RECORDER_PIXELMAP( \
		r, op, q, rot, x1, y1, p, sx1, sy1, sx2, sy2) \
	if (r != NULL) \
	{ \
		r->PutChar(GraphicsViewRecord::pixelmap8Command); \
		r->PutChar(op); \
		r->PutShort(q); \
		r->PutShort(rot); \
		r->PutShort(x1); \
		r->PutShort(y1); \
		r->PutPointer(p); \
		r->PutShort(sx1); \
		r->PutShort(sy1); \
		r->PutShort(sx2); \
		r->PutShort(sy2); \
	}
		case pixelmap8Command:
			op  = GetChar();
			q   = GetShort();
			rot = GetShort();
			x1  = GetShort();
			y1  = GetShort();
			pixelmap_pointer = (PixelMap8 *) GetPointer();
			sx1 = GetShort();
			sy1 = GetShort();
			sx2 = GetShort();
			sy2 = GetShort();

			Check(pixelmap_pointer);

			if (forced_color != -1)
			{
				graphics_port->DrawPixelMap8SingleColor(
					forced_color,
					op,
					rot,
					x1, y1,
					pixelmap_pointer,
					sx1, sy1, sx2, sy2
			);
			}
			else
			{
				graphics_port->DrawPixelMap8(
					op,
					q,
					rot,
					x1, y1,
					pixelmap_pointer,
					sx1, sy1, sx2, sy2
				);
			}
			break;

#define RECORDER_PIXELMAP_SINGLE_COLOR( \
		r, color, op, rot, x1, y1, p, sx1, sy1, sx2, sy2) \
	if (r != NULL) \
	{ \
		r->PutChar(GraphicsViewRecord::pixelmap8SolidCommand); \
		r->PutShort(color); \
		r->PutChar(op); \
		r->PutShort(rot); \
		r->PutShort(x1); \
		r->PutShort(y1); \
		r->PutPointer(p); \
		r->PutShort(sx1); \
		r->PutShort(sy1); \
		r->PutShort(sx2); \
		r->PutShort(sy2); \
	}
		case pixelmap8SolidCommand:
			color = GetShort();
			op  = GetChar();
			rot = GetShort();
			x1  = GetShort();
			y1  = GetShort();
			pixelmap_pointer = (PixelMap8 *) GetPointer();
			sx1 = GetShort();
			sy1 = GetShort();
			sx2 = GetShort();
			sy2 = GetShort();

			Check(pixelmap_pointer);

			if (forced_color != -1)
			{
				color = forced_color;
			}
			graphics_port->DrawPixelMap8SingleColor(
				color,
				op,
				rot,
				x1, y1,
				pixelmap_pointer,
				sx1, sy1, sx2, sy2
			);
			break;
		}
	}
	Check_Fpu();
}

void
	GraphicsViewRecord::PutChar(int value)
{
	Check(this);
	//--------------------------------------------------------------
	// If the buffer is not full, jam it in
	//--------------------------------------------------------------
	if (commandListIndex < commandListLength)
	{
		commandList[commandListIndex++] = (char) value;
	}
	//--------------------------------------------------------------
	// If the buffer IS full, put it in the 'next' linked buffer
	//--------------------------------------------------------------
	else
	{
		//--------------------------------------------------------------
		// If there is no linked buffer, create one
		//--------------------------------------------------------------
		if (link == NULL)
		{
			link = new GraphicsViewRecord();
			Check(link);
			Register_Object(link);
		}
		//--------------------------------------------------------------
		// Give it to the linked buffer
		//--------------------------------------------------------------
		link->PutChar(value);
	}
	Check_Fpu();
}

int
	GraphicsViewRecord::GetChar()
{
	Check(this);
	//--------------------------------------------------------------
	// If the buffer is not empty, pull the char out
	//--------------------------------------------------------------
	if (commandListIndex < commandListLength)
	{
		Check_Fpu();
		return (int) commandList[commandListIndex++];
	}
	//--------------------------------------------------------------
	// If the buffer IS empty, get char from the 'next' linked buffer
	//--------------------------------------------------------------
	else
	{
		//--------------------------------------------------------------
		// If there is no linked buffer, it's a catastrophe
		//--------------------------------------------------------------
		if (link == NULL)
		{
			Fail("GraphicsViewRecord::GetChar() has no link");
			Check_Fpu();
			return GraphicsViewRecord::endCommand;
		}
		//--------------------------------------------------------------
		// Read from the linked buffer
		//--------------------------------------------------------------
		Check(link);
		Check_Fpu();
		return link->GetChar();
	}
}

void
	GraphicsViewRecord::PutLogical(Logical value)
{
	Check(this);
	if (value)
	{
		PutChar(1);
	}
	else
	{
		PutChar(0);
	}
	Check_Fpu();
}

Logical
	GraphicsViewRecord::GetLogical()
{
	Check(this);
	Check_Fpu();
	if (GetChar())
	{
		return True;
	}
	else
	{
		return False;
	}
}

void
	GraphicsViewRecord::PutShort(int value)
{
	Check(this);
	PutChar(value >> 8);
	PutChar(value);
	Check_Fpu();
}

int
	GraphicsViewRecord::GetShort()
{
	Check(this);
	int
		q;

	q  = GetChar() << 8;
	q |= GetChar() & 0xFF;

	if (q & 0x8000)		// extend sign
	{
		q |= 0xFFFF0000;
	}

	Check_Fpu();
	return q;
}

void
	GraphicsViewRecord::PutPointer(void *pointer)
{
	Check(this);
	Verify(pointer != NULL);
	union
	{
		void *pointer;
		int value;			// assumes 32-bit integer!
	}
	faker;

	faker.pointer = pointer;

	PutChar(faker.value >> 24);
	PutChar(faker.value >> 16);
	PutChar(faker.value >> 8);
	PutChar(faker.value);
	Check_Fpu();
}


void *
	GraphicsViewRecord::GetPointer()
{
	Check(this);
	union
	{
		void *pointer;
		int value;			// assumes 32-bit integer!
	}
	faker;

	faker.value  =  GetChar() << 24;
	faker.value |= (GetChar() & 0xFF) << 16;
	faker.value |= (GetChar() & 0xFF) << 8;
	faker.value |=  GetChar() & 0xFF;

	Verify(faker.pointer != NULL);
	Check_Fpu();
	return faker.pointer;
}

//########################################################################
//############################ GraphicsView ##############################
//########################################################################
GraphicsView::GraphicsView(
	GraphicsPort *graphics_port,
	int x1, int y1, int x2, int y2
)
{
	Check_Pointer(this);
	// NOTE: graphics_port may be NULL!

	//--------------------------------------------------------------
	// Initialize values
	//--------------------------------------------------------------
	graphicsPort = graphics_port;

	currentPosition.x = 0;	
	currentPosition.y = 0;
	currentFontID = 0;
	currentColor = 1;
	currentOperation = GraphicsDisplay::Replace;

	//--------------------------------------------------------------
	// Set areaWithinPort
	//--------------------------------------------------------------
	if (graphics_port == NULL)
	{
		//--------------------------------------------------------------
		// GraphicsPort doesn't exist, set areaWithinPort to safe value
		//--------------------------------------------------------------
		areaWithinPort.bottomLeft.x = 0;
		areaWithinPort.bottomLeft.y = 0;
		areaWithinPort.topRight.x = 0;
		areaWithinPort.topRight.y = 0;

		origin = areaWithinPort.bottomLeft;
	}
	else
	{
		//--------------------------------------------------------------
		// Set areaWithinPort to given size
		//--------------------------------------------------------------
		areaWithinPort.bottomLeft.x = x1;
		areaWithinPort.bottomLeft.y = y1;
		areaWithinPort.topRight.x = x2;
		areaWithinPort.topRight.y = y2;
		//--------------------------------------------------------------
		// Adjust area according to graphicsPort offset
		//--------------------------------------------------------------
		areaWithinPort.bottomLeft += graphicsPort->bounds.bottomLeft;
		areaWithinPort.topRight += graphicsPort->bounds.bottomLeft;
		//--------------------------------------------------------------
		// Set origin to bottom left corner of area
		//--------------------------------------------------------------
		origin = areaWithinPort.bottomLeft;
		//--------------------------------------------------------------
		// Clip to graphicsPort bounds
		//--------------------------------------------------------------
		areaWithinPort.Intersection(areaWithinPort, graphicsPort->bounds);
	}
	//--------------------------------------------------------------
	// Set clipping rectangle to size of view
	//--------------------------------------------------------------
	clippingRectangle = areaWithinPort;
	//--------------------------------------------------------------
	// Clear recorder pointer
	//--------------------------------------------------------------
	recorder = (GraphicsViewRecord *) NULL;
	Check_Fpu();
}

GraphicsView::GraphicsView(GraphicsPort *graphics_port)
{
	Check_Pointer(this);

	// NOTE: graphics_port may be NULL!
	//--------------------------------------------------------------
	// Initialize values
	//--------------------------------------------------------------
	graphicsPort = graphics_port;

	currentPosition.x = 0;	
	currentPosition.y = 0;
	currentFontID = 0;
	currentColor = 1;
	currentOperation = GraphicsDisplay::Replace;
	//--------------------------------------------------------------
	// Set areaWithinPort
	//--------------------------------------------------------------
	if (graphics_port == NULL)
	{
		//--------------------------------------------------------------
		// GraphicsPort doesn't exist, set areaWithinPort to safe value
		//--------------------------------------------------------------
		areaWithinPort.bottomLeft.x = 0;
		areaWithinPort.bottomLeft.y = 0;
		areaWithinPort.topRight.x = 0;
		areaWithinPort.topRight.y = 0;
	}
	else
	{
		//--------------------------------------------------------------
		// Set areaWithinPort to size of graphicsPort
		//--------------------------------------------------------------
		areaWithinPort = graphics_port->bounds;
	}           
	//--------------------------------------------------------------
	// Set origin to bottom left corner of area
	//--------------------------------------------------------------
	origin = areaWithinPort.bottomLeft;
	//--------------------------------------------------------------
	// Set clipping rectangle to size of view
	//--------------------------------------------------------------
	clippingRectangle = areaWithinPort;
	//--------------------------------------------------------------
	// Clear recorder pointer
	//--------------------------------------------------------------
	recorder = (GraphicsViewRecord *) NULL;
	Check_Fpu();
}

GraphicsView::~GraphicsView()
{
	Check(this);
	Check_Fpu();
}

void
	GraphicsView::SetPositionWithinPort(int x1, int y1, int x2, int y2)
{
#	if defined(DEBUG)
		Tell(
			"GraphicsView::SetPositionWithinPort(" << x1 <<
			"," << y1 <<
			", " << x2 <<
			"," << y2 <<
			"\n"
		);
#	endif
	Check(this);
	Verify (x2 > x1);
	Verify (y2 > y1);

	if (graphicsPort != NULL)
	{
		//--------------------------------------------------------------
		// Set the new area
		//--------------------------------------------------------------
		areaWithinPort.bottomLeft.x = x1;	
		areaWithinPort.bottomLeft.y = y1;
		areaWithinPort.topRight.x = x2-1;	
		areaWithinPort.topRight.y = y2-1;
		//--------------------------------------------------------------
		// Adjust area according to graphicsPort offset
		//--------------------------------------------------------------
		areaWithinPort.bottomLeft += graphicsPort->bounds.bottomLeft;
		areaWithinPort.topRight += graphicsPort->bounds.bottomLeft;
		//--------------------------------------------------------------
		// Set new origin
		//--------------------------------------------------------------
		origin = areaWithinPort.bottomLeft;
		//--------------------------------------------------------------
		// Clip to graphicsPort bounds
		//--------------------------------------------------------------
		areaWithinPort.Intersection(areaWithinPort, graphicsPort->bounds);
		//--------------------------------------------------------------
		// Set clipping rectangle to size of view
		//--------------------------------------------------------------
		clippingRectangle = areaWithinPort;
	}
#	if defined(DEBUG)
		Tell(
			"areaWithinPort=" << areaWithinPort <<
			"\n"
		);
#	endif
	Check_Fpu();
}

Logical
	GraphicsView::TestInstance() const
{
	return True;
}

void
	GraphicsView::ShowInstance(char *indent)
{
	Check(this);

	std::cout << indent << "GraphicsView:\n";

	char
		temp[80];

	Str_Copy(temp, indent, 80);
	Str_Cat(temp, "...", 80);

	std::cout << temp << "areaWithinPort    =" << areaWithinPort << "\n";
	std::cout << temp << "origin            =" << origin << "\n";
	std::cout << temp << "currentPosition   =" << currentPosition << "\n";
	std::cout << temp << "currentFontID     =" << currentFontID << "\n";
	std::cout << temp << "currentColor      =" << currentColor << "\n";
	std::cout << temp << "clippingRectangle =" << clippingRectangle << "\n";
	std::cout << temp << "currentOperation  =";
	switch(currentOperation)
	{
	case GraphicsDisplay::And:			std::cout << "AND\n"; break;
	case GraphicsDisplay::Or:			std::cout << "OR\n"; break;
	case GraphicsDisplay::Replace:	std::cout << "REPLACE\n"; break;
	default:	std::cout << "Unsupported type:" << currentOperation << "\n"; break;
	}
	std::cout << temp << "graphicsPort   =" << graphicsPort << "\n";
	Check_Fpu();
}

void
	GraphicsView::SetOperation(GraphicsDisplay::Operation new_operation)
{
	Check(this);
	currentOperation = new_operation;
	Check_Fpu();
}

void
	GraphicsView::SetOrigin(int x, int y)
{
#	if defined(DEBUG)
		Tell(
			"GraphicsView::SetOrigin(" << x <<
			"," << y <<
			"\n"
		);
#	endif
	Check(this);
	//--------------------------------------------------------------
	// Adjust the current position
	//--------------------------------------------------------------
	currentPosition -= origin;
	//--------------------------------------------------------------
	// Set the new origin
	//--------------------------------------------------------------
	origin.x = x + areaWithinPort.bottomLeft.x;
	origin.y = y + areaWithinPort.bottomLeft.y;
	//--------------------------------------------------------------
	// Adjust the current position to the new location
	//--------------------------------------------------------------
	currentPosition += origin;
#	if defined(DEBUG)
		Tell(
			"origin=" << origin <<
			", currentPosition=" << currentPosition <<
			"\n"
		);
#	endif
	Check_Fpu();
}

void
	GraphicsView::SetFont(Enumeration new_font_ID)
{
	Check(this);

	currentFontID = new_font_ID;
	Check_Fpu();
}

void
	GraphicsView::SetColor(int new_color)
{
	Check(this);

	currentColor = new_color;
	Check_Fpu();
}

void
	GraphicsView::SetClippingRectangle(Rectangle2D *clippingRectanglePtr)
{
	Check(this);
	Check(clippingRectanglePtr);

	Rectangle2D	r;
	//--------------------------------------------------------------
	// Get a copy of the requested clipping rectangle,
	// add origin offset
	//--------------------------------------------------------------
	r = *clippingRectanglePtr;
	r.bottomLeft += origin;
	r.topRight += origin;
	//--------------------------------------------------------------
	// Limit it to the graphicsPort bounds, save the result
	//--------------------------------------------------------------
	if (graphicsPort != NULL)
	{
		clippingRectangle.Intersection(graphicsPort->bounds, r);
	}
	else
	{
		clippingRectangle.MakeEmpty();
	}
	Check_Fpu();
}

void
	GraphicsView::ClearClippingRectangle()
{
	Check(this);

	clippingRectangle = areaWithinPort;
	Check_Fpu();
}

void
	GraphicsView::MoveToAbsolute(int x, int y)
{
	Check(this);

	currentPosition.x = origin.x + x;
	currentPosition.y = origin.y + y;
	Check_Fpu();
}

void
	GraphicsView::MoveToRelative(int x, int y)
{
	Check(this);

	currentPosition.x += x;
	currentPosition.y += y;
	Check_Fpu();
}

void
	GraphicsView::DrawPoint()
{
	Check(this);

	int code(BuildPointCode(currentPosition));

	if (graphicsPort != NULL)
	{
		if (code == GraphicsView::IsWithin)
		{
			graphicsPort->
				DrawPoint(
					currentColor,
					currentOperation,
					currentPosition.x, currentPosition.y
				);

			RECORDER_POINT(
				recorder,
				currentColor,
				currentOperation,
				currentPosition.x, currentPosition.y
			);
		}
	}
	Check_Fpu();
}

void
	GraphicsView::DrawLineToAbsolute(int x, int y)
{
	Check(this);

	Vector2DOf<int>
		end;

	end.x = origin.x + x;
	end.y = origin.y + y;
	DrawLine(currentPosition, end, True);
	currentPosition = end;
	Check_Fpu();
}

void
	GraphicsView::DrawLineToRelative(int x, int y)
{
	Check(this);

	Vector2DOf<int>
		end;

	end.x = currentPosition.x + x;
	end.y = currentPosition.y + y;
	DrawLine(currentPosition, end, True);
	currentPosition = end;
	Check_Fpu();
}

void
	GraphicsView::DrawThickLineToAbsolute(int x, int y)
{
	Check(this);

	Vector2DOf<int>
		end,
		start,
		stop;

	end.x = origin.x + x;
	end.y = origin.y + y;

	start.x = currentPosition.x;
	start.y = currentPosition.y;
	stop.x = end.x;
	stop.y = end.y;
	DrawThickLine(start, stop);

	currentPosition = end;
	Check_Fpu();
}

void
	GraphicsView::DrawThickLineToRelative(int x, int y)
{
	Check(this);

	Vector2DOf<int>
		end,
		start,
		stop;

	end.x = currentPosition.x + x;
	end.y = currentPosition.y + y;

	start.x = currentPosition.x;
	start.y = currentPosition.y;
	stop.x = end.x;
	stop.y = end.y;
	DrawThickLine(start, stop);

	currentPosition = end;
	Check_Fpu();
}

void
	GraphicsView::DrawRectangleToAbsolute(int x, int y)
{
	Check(this);

	Vector2DOf<int>
		home;

	home = currentPosition;
	home -= origin;
	DrawLineToAbsolute(home.x, y);
	DrawLineToAbsolute(x, y);
	DrawLineToAbsolute(x, home.y);
	DrawLineToAbsolute(home.x, home.y);

	currentPosition.x = x;
	currentPosition.y = y;
	Check_Fpu();
}

void
	GraphicsView::DrawRectangleToRelative(int x, int y)
{
	Check(this);

	Vector2DOf<int>
		home;

	home = currentPosition;
	home -= origin;
	DrawLineToRelative(0, y);
	DrawLineToRelative(x, 0);
	DrawLineToRelative(0, -y);
	DrawLineToRelative(-x, 0);

	currentPosition.x = home.x + x;
	currentPosition.y = home.y + y;
	Check_Fpu();
}

void
	GraphicsView::DrawFilledRectangleToAbsolute(int x, int y)
{
	Check(this);

	Rectangle2D	r;

	r.bottomLeft = currentPosition;
	r.topRight.x = origin.x + x;
	r.topRight.y = origin.y + y;

	DrawFilledRectangle(r);

	currentPosition = r.topRight;
	Check_Fpu();
}

void
	GraphicsView::DrawFilledRectangleToRelative(int x, int y)
{
	Check(this);

	Rectangle2D	r;

	r.bottomLeft = currentPosition;
	r.topRight.x = currentPosition.x + x;
	r.topRight.y = currentPosition.y + y;

	DrawFilledRectangle(r);

	currentPosition = r.topRight;
	Check_Fpu();
}

void
	GraphicsView::DrawText(
		Logical ,
		Logical ,
		GraphicsDisplay::Justification ,
		Rectangle2D	*,
		char *
	)
{
	Check(this);
	Check_Fpu();
}

void
	GraphicsView::DrawBitMap(
		int rotation,
		BitMap	*bitmap,
		int sx1, int sy1, int sx2, int sy2	// these are SOURCE coordinates
	)
{
	Check(this);

	Rectangle2D sourceBounds(sx1, sy1, sx2, sy2);
	Vector2DOf<int> position;

	if (bitmap != NULL)
	{
		Check(bitmap);

		if (ClipImage(&sourceBounds, &position, bitmap->Data.Size))
		{
			graphicsPort->
				DrawBitMap(
					currentColor,
					currentOperation,
					rotation,
					position.x, position.y,
					bitmap,
					sourceBounds.bottomLeft.x, sourceBounds.bottomLeft.y,
					sourceBounds.topRight.x, sourceBounds.topRight.y
				);
			RECORDER_BITMAP(
					recorder,
					currentColor,
					currentOperation,
					rotation,
					position.x, position.y,
					bitmap,
					sourceBounds.bottomLeft.x, sourceBounds.bottomLeft.y,
					sourceBounds.topRight.x, sourceBounds.topRight.y
			);
		}
	}
	Check_Fpu();
}

void
	GraphicsView::DrawBitMapOpaque(
		int background,
		int rotation,
		BitMap	*bitmap,
		int sx1, int sy1, int sx2, int sy2	// these are SOURCE coordinates
	)
{
	Check(this);

	Rectangle2D sourceBounds(sx1, sy1, sx2, sy2);
	Vector2DOf<int> position;

	if (bitmap != NULL)
	{
		Check(bitmap);

		if (ClipImage(&sourceBounds, &position, bitmap->Data.Size))
		{
			graphicsPort->
				DrawBitMapOpaque(
					currentColor,
					background,
					currentOperation,
					rotation,
					position.x, position.y,
					bitmap,
					sourceBounds.bottomLeft.x, sourceBounds.bottomLeft.y,
					sourceBounds.topRight.x, sourceBounds.topRight.y
				);
			RECORDER_BITMAP_OPAQUE(
					recorder,
					currentColor,
					background,
					currentOperation,
					rotation,
					position.x, position.y,
					bitmap,
					sourceBounds.bottomLeft.x, sourceBounds.bottomLeft.y,
					sourceBounds.topRight.x, sourceBounds.topRight.y
			);
		}
	}
	Check_Fpu();
}

void
	GraphicsView::DrawPixelMap8(
		Logical opaque,
		int rotation,
		PixelMap8	*pixelmap,
		int sx1, int sy1, int sx2, int sy2	// these are SOURCE coordinates
	)
{
	Check(this);

	Rectangle2D sourceBounds(sx1, sy1, sx2, sy2);
	Vector2DOf<int> position;

	if (pixelmap != NULL)
	{
		Check(pixelmap);

		if (ClipImage(&sourceBounds, &position, pixelmap->Data.Size))
		{
			graphicsPort->
				DrawPixelMap8(
					currentOperation,
					opaque,
					rotation,
					position.x, position.y,
					pixelmap,
					sourceBounds.bottomLeft.x, sourceBounds.bottomLeft.y,
					sourceBounds.topRight.x, sourceBounds.topRight.y
				);
			RECORDER_PIXELMAP(
					recorder,
					currentOperation,
					opaque,
					rotation,
					position.x, position.y,
					pixelmap,
					sourceBounds.bottomLeft.x, sourceBounds.bottomLeft.y,
					sourceBounds.topRight.x, sourceBounds.topRight.y
			);
		}
	}
	Check_Fpu();
}

void
	GraphicsView::DrawPixelMap8SingleColor(
		int rotation,
		PixelMap8	*pixelmap,
		int sx1, int sy1, int sx2, int sy2	// these are SOURCE coordinates
	)
{
	Check(this);

	Rectangle2D sourceBounds(sx1, sy1, sx2, sy2);
	Vector2DOf<int> position;

	if (pixelmap != NULL)
	{
		Check(pixelmap);

		if (ClipImage(&sourceBounds, &position, pixelmap->Data.Size))
		{
			graphicsPort->
				DrawPixelMap8SingleColor(
					currentColor,
					currentOperation,
					rotation,
					position.x, position.y,
					pixelmap,
					sourceBounds.bottomLeft.x, sourceBounds.bottomLeft.y,
					sourceBounds.topRight.x, sourceBounds.topRight.y
				);
			RECORDER_PIXELMAP_SINGLE_COLOR(
					recorder,
					currentColor,
					currentOperation,
					rotation,
					position.x, position.y,
					pixelmap,
					sourceBounds.bottomLeft.x, sourceBounds.bottomLeft.y,
					sourceBounds.topRight.x, sourceBounds.topRight.y
			);
		}
	}
	Check_Fpu();
}

void
	GraphicsView::SetColor(
		PaletteTriplet *source_color,
		int color_index
	)
{
	Check(this);
	Check_Pointer(source_color);

	if (graphicsPort != NULL)
	{
		graphicsPort->SetColor(source_color, color_index);
	}
	Check_Fpu();
}

void
	GraphicsView::AttachRecorder(GraphicsViewRecord *graphics_view_record)
{
	Check(this);
	Verify(recorder == NULL);
	Check(graphics_view_record);
	recorder = graphics_view_record;
	Check_Fpu();
}

void
	GraphicsView::DetachRecorder()
{
	Check(this);
	recorder = NULL;
	Check_Fpu();
}

int
	GraphicsView::BuildPointCode(Vector2DOf<int> vector)
{
	// This is static. Don't attempt to check for signature!

	int result(0);

	if (vector.x < clippingRectangle.bottomLeft.x)
	{
		result |= GraphicsView::IsLeft;
	}
	else if (vector.x > clippingRectangle.topRight.x)
	{
		result |= GraphicsView::IsRight;
	}

	if (vector.y < clippingRectangle.bottomLeft.y)
	{
		result |= GraphicsView::IsBelow;
	}
	else if (vector.y > clippingRectangle.topRight.y)
	{
		result |= GraphicsView::IsAbove;
	}
	Check_Fpu();
	return result;
}

void
	GraphicsView::DrawThickLine(
		Vector2DOf<int> start,
		Vector2DOf<int> stop
	)
{
	Check(this);

	DrawLine(start, stop, True);
	--start.x;
	--stop.x;
	DrawLine(start, stop, True);
	++start.x;
	++stop.x;
	++start.y;
	++stop.y;
	DrawLine(start, stop, True);
	++start.x;
	++stop.x;
	--start.y;
	--stop.y;
	DrawLine(start, stop, True);
	--start.x;
	--stop.x;
	--start.y;
	--stop.y;
	DrawLine(start, stop, True);

	Check_Fpu();
}

void
	GraphicsView::DrawLine(
		Vector2DOf<int> start,
		Vector2DOf<int> end,
		Logical include_last_pixel
	)
{
#			if defined(DEBUG)
				Tell(
					"DrawLine(=" << start <<
					", " << end <<
					", " << include_last_pixel <<
					")\n"
				);
				Tell(
					"clippingRectangle=" << clippingRectangle <<
					"\n"
				);
#			endif
	Check(this);

	int
		start_code,
		end_code;

	Logical
		swapped(False),
		start_clipped(False),
		end_clipped(False);

	if (graphicsPort != NULL)
	{
		//----------------------------------------------------
		// Generate endpoint codes
		//----------------------------------------------------
		start_code = BuildPointCode(start);
		end_code   = BuildPointCode(end);
#		if defined(DEBUG)
			Tell(
				"start code=" << start_code <<
				", end_code=" << end_code <<
				"\n"
			);
#		endif

		//----------------------------------------------------
		// Loop until either both codes are zero
		// or it's completely out of the display
		//----------------------------------------------------
		while(start_code | end_code)
		{
#			if defined(DEBUG)
				Tell(
					"Clipping, start=" << start <<
					", end=" << end <<
					"\n"
				);
#			endif
			//----------------------------------------------------
			// If the line is totally off the display, abandon it
			//----------------------------------------------------
			if ((start_code & end_code) != 0)
			{
#				if defined(DEBUG)
					Tell("Abandoned\n");
#				endif
				return;
			}
			//----------------------------------------------------
			// Make sure an out-of-bounds point is being clipped
			//----------------------------------------------------
			if (start_code == GraphicsView::IsWithin)
			{
#				if defined(DEBUG)
					Tell("Swapping endpoints\n");
#				endif

				Vector2DOf<int>
					temp_vector;
				temp_vector = start;
				start = end;
				end = temp_vector;

				int temp_code;
				temp_code = start_code;
				start_code = end_code;
				end_code = temp_code;

				Logical temp_flag;
				temp_flag = start_clipped;
				start_clipped = end_clipped;
				end_clipped = temp_flag;

				swapped ^= True;
			}
			//----------------------------------------------------
			// Clip the starting segment
			//----------------------------------------------------
			if (start_code & GraphicsView::IsLeft)
			{
#				if defined(DEBUG)
					Tell("Clip left\n");
#				endif
				Verify(end.x != start.x);
				start.y += (end.y - start.y) *
					(clippingRectangle.bottomLeft.x-start.x) / (end.x-start.x);
				Check_Fpu();
				start.x = clippingRectangle.bottomLeft.x;

				start_clipped = True;
			}
			else if (start_code & GraphicsView::IsRight)
			{
#				if defined(DEBUG)
					Tell("Clip right\n");
#				endif
				Verify(end.x != start.x);
				start.y += (end.y - start.y) *
					(clippingRectangle.topRight.x-start.x) / (end.x-start.x);
				Check_Fpu();
				start.x = clippingRectangle.topRight.x;

				start_clipped = True;
			}
			else if (start_code & GraphicsView::IsAbove)
			{
#				if defined(DEBUG)
					Tell("Clip above\n");
#				endif
				Verify(end.y != start.y);
				start.x += (end.x - start.x) *
					(clippingRectangle.topRight.y-start.y) / (end.y-start.y);
				Check_Fpu();
				start.y = clippingRectangle.topRight.y;

				start_clipped = True;
			}
			else if (start_code & GraphicsView::IsBelow)
			{
#				if defined(DEBUG)
					Tell("Clip below\n");
#				endif
				Verify(end.y != start.y);
				start.x += (end.x - start.x) *
					(clippingRectangle.bottomLeft.y-start.y) / (end.y-start.y);
				Check_Fpu();
				start.y = clippingRectangle.bottomLeft.y;

				start_clipped = True;
			}
			//----------------------------------------------------
			// Build a new point code for the starting point,
			// loop until line is either in or out.
			//----------------------------------------------------
			start_code = BuildPointCode(start);
		}
#		if defined(DEBUG)
			Tell(
				"Clipping done, start=" << start <<
				", end=" << end <<
				"\n"
			);
#		endif
		//----------------------------------------------------
		// If swapped, flip them back
		//----------------------------------------------------
		if (swapped)
		{
#			if defined(DEBUG)
				Tell("Swapping back\n");
#			endif
			Vector2DOf<int>
				temp_vector;
			temp_vector = start;
			start = end;
			end = temp_vector;

			Logical temp_flag;
			temp_flag = start_clipped;
			start_clipped = end_clipped;
			end_clipped = temp_flag;
		}
		//----------------------------------------------------
		// If the endpoint was clipped, include the final pixel
		//----------------------------------------------------
		if (end_clipped)
		{
#			if defined(DEBUG)
				Tell("End was clipped, including last pixel\n");
#			endif
			include_last_pixel = True;
		}
		//----------------------------------------------------
		// Finally, draw the line!
		//----------------------------------------------------
#		if defined(DEBUG)
			Tell(
				"All done, start=" << start <<
				", end=" << end <<
				"\n"
			);
#		endif

		Verify(BuildPointCode(start) == 0);
		Verify(BuildPointCode(end) == 0);

		graphicsPort->DrawLine(
			currentColor,
			currentOperation,
			start.x, start.y,
			end.x, end.y,
			include_last_pixel
		);

		RECORDER_LINE(
			recorder,
			currentColor,
			currentOperation,
			start.x, start.y,
			end.x, end.y,
			include_last_pixel
		);
	}
	Check_Fpu();
}

void
	GraphicsView::DrawFilledRectangle(Rectangle2D r)
{
	Check(this);
	if (graphicsPort != NULL)
	{
		//
		// Swap X positions if needed
		//
		if (r.bottomLeft.x > r.topRight.x)
		{
			int temp;

			temp = r.bottomLeft.x;
			r.bottomLeft.x = r.topRight.x;
			r.topRight.x = temp;
		}
		//
		// Swap Y positions if needed
		//
		if (r.bottomLeft.y > r.topRight.y)
		{
			int temp;

			temp = r.bottomLeft.y;
			r.bottomLeft.y = r.topRight.y;
			r.topRight.y = temp;
		}
		//
		// Clip to clipping rectangle
		//
		r.Intersection(clippingRectangle, r);
		//
		// Draw if non-empty
		//
		if (! r.IsEmpty())
		{
			graphicsPort->
   			DrawFilledRectangle(
   				currentColor,
   				currentOperation,
   				r.bottomLeft.x, r.bottomLeft.y, r.topRight.x, r.topRight.y
				);
			RECORDER_FRECT(
				recorder,
				currentColor,
				currentOperation,
   			r.bottomLeft.x, r.bottomLeft.y, r.topRight.x, r.topRight.y
			);
		}
	}
	Check_Fpu();
}

Logical
	GraphicsView::ClipImage(
		Rectangle2D *sourceRect,
		Vector2DOf<int> *position,
		Vector2DOf<int> imageLimits
		)
{
	Check(this);

	if (graphicsPort != NULL)
	{
		Rectangle2D
			destRect;

		Vector2DOf<int>
			source_origin,
			source_size;

		//
		// clip sourceRect to image limits
		//
		if (sourceRect->bottomLeft.x < 0)
		{
			sourceRect->bottomLeft.x = 0;
		}
		if (sourceRect->topRight.x >= imageLimits.x)
		{
			sourceRect->topRight.x = imageLimits.x-1;
		}

		if (sourceRect->bottomLeft.y < 0)
		{
			sourceRect->bottomLeft.y = 0;
		}
		if (sourceRect->topRight.y >= imageLimits.y)
		{
			sourceRect->topRight.y = imageLimits.y-1;
		}

		source_origin = sourceRect->bottomLeft;
		source_size =  sourceRect->topRight;
		source_size -= sourceRect->bottomLeft;

		//
		// build corresponding rectangle in port space
		//
		destRect.bottomLeft = currentPosition;
		destRect.topRight = currentPosition;
		destRect.topRight += source_size;
		//
		// now, clip the rectangle
		//
		Rectangle2D r;
		r = destRect;
		destRect.Intersection(r, clippingRectangle);
		//
   	// draw if non-empty
   	//
		if (! destRect.IsEmpty())
   	{
			//
			// Set position
			//
			*position = destRect.bottomLeft;
			//
			// return rectangle to image space
			//
			source_size =  destRect.topRight;
			source_size -= destRect.bottomLeft;

			sourceRect->bottomLeft = source_origin;
			sourceRect->topRight   = source_origin;
			sourceRect->topRight   += source_size;
			Check_Fpu();
			return True;
		}
	}
	Check_Fpu();
	return False;
}
