#include "mungal4.h"
#pragma hdrstop

#include "..\munga\fileutil.h"
#include "l4gauima.h"
#include "l4app.h"
#include "..\munga\notation.h"
#include "..\munga\namelist.h"

// #define LOCAL_TEST

#if defined(LOCAL_TEST)
#	define Test_Tell(n) std::cout << n << std::flush
#else
#	define Test_Tell(n)
#endif


//############################################################################
// GroupAttribute
// - this is a private class used nowhere else.
// It is initialized from a .GAT notation file, after which it
// holds a list of attribute group names with associated color and
// attribute fields.  It may then be searched to find the values for
// that attribute group.
//############################################################################
class GroupAttribute SIGNATURED
{
public:
	enum
	{
		nameLength=64
	};

	int color;
	int attribute;
	char name[nameLength];

	GroupAttribute
		*next;

	GroupAttribute(const char *new_name, int new_color, int new_attrib);
	~GroupAttribute()
		{}
	
	Logical
		TestInstance() const
			{ return True; }

	static Logical
		Initialize(const char *gat_file_name);
	static void
		Terminate();
	static Logical
		Search(const char *name, int *color, int *attribute);
protected:
	static
		GroupAttribute
			*head;
};

GroupAttribute
	*GroupAttribute::head;

GroupAttribute::GroupAttribute(
	const char *new_name,
	int new_color,
	int new_attrib
)
{
	Test_Tell(
		"GroupAttribute::GroupAttribute(" << new_name <<
		", " << new_color <<
		", " << new_attrib <<
		")\n"
	);
	Check_Pointer(this);
	Check_Pointer(new_name);

	name[0] = '\0';		// safety check
	Str_Copy(name, new_name, nameLength-1);
	name[nameLength-1] = '\0';

	color = new_color;
	attribute = new_attrib;

	next = head;
	head = this;
	Check_Fpu();
}

Logical
	GroupAttribute::Initialize(const char *gat_filename)
{
	Test_Tell("GroupAttribute::Initialize(" << gat_filename << ")\n");
	Check_Pointer(gat_filename);

	Logical
		result = False;

	//-------------------------------------------------------------
	// Open the associated .GAT file
	//-------------------------------------------------------------
	NotationFile
		*gat_file = new NotationFile(gat_filename);
	Check(gat_file);
	Register_Object(gat_file);

	//-------------------------------------------------------------
	// Create group, attribute, color name lists
	//-------------------------------------------------------------
	NameList
		*color_namelist = gat_file->MakeEntryList("colors");
	if (color_namelist == NULL)
	{
		DEBUG_STREAM << gat_filename << " has no 'colors' page!" << std::endl;
	}

	NameList
		*attribute_namelist = gat_file->MakeEntryList("attributes");
	if (attribute_namelist == NULL)
	{
		DEBUG_STREAM << gat_filename << " has no 'attributes' page!" << std::endl;
	}         

	NameList
		*group_namelist = gat_file->MakeEntryList("groups");
	if (group_namelist == NULL)
	{
		DEBUG_STREAM << gat_filename << " has no 'groups' page!" << std::endl;
	}         

	if (
			(attribute_namelist != NULL) &&
			(color_namelist != NULL) &&
			(group_namelist != NULL)
		)
	{
		//-------------------------------------------------------------
		// Register the name lists
		//-------------------------------------------------------------
		Register_Object(color_namelist);
		Register_Object(attribute_namelist);
		Register_Object(group_namelist);
		//-------------------------------------------------------------
		// Clear the chain header
		//-------------------------------------------------------------
		head = NULL;
		//-------------------------------------------------------------
		// For all group names, build a GroupAttribute object
		//-------------------------------------------------------------
		NameList::Entry
			*group_entry,
			*color_entry,
			*attribute_entry;
		int
			i,
			new_attribute,
			new_color;
		Logical
			found;

		for (
				group_entry = group_namelist->GetFirstEntry();
				group_entry != NULL;
				group_entry = group_entry->GetNextEntry()
			)
		{
			Check(group_entry);

			char
				buffer[256];
			char
				*dest,
				*text = group_entry->GetChar();

			new_attribute = 0;
			new_color = 0;

			if (text == NULL)
			{
				DEBUG_STREAM << text << " is an empty .GAT group!" << std::endl;
			}
			else
			{
				//-------------------------------------------------------------
				// Skip white space
				//-------------------------------------------------------------
				while (*text != '\0')
				{
					while (isspace(*text))
					{
						++text;
					}
					//-------------------------------------------------------------
					// Parse out one name
					//-------------------------------------------------------------
					for (
							i=0,dest=buffer;
         				i<(sizeof(buffer)-1);
							++i,++dest,++text
         			)
         		{
						if (!isalnum(*text))
         			{
							break;
         			}

						*dest = *text;
         		}
         		*dest = '\0';

					Test_Tell("attribute item= <" << buffer << ">\n");
					//-------------------------------------------------------------
					// Search color list for name
					//-------------------------------------------------------------
					found = False;

					for (
							color_entry = color_namelist->GetFirstEntry();
							color_entry != NULL;
							color_entry = color_entry->GetNextEntry()
						)
					{
						Check(color_entry);
						if (strcmp(color_entry->GetName(), buffer) == 0)
						{
							Check_Pointer(color_entry->GetData());
							new_color = color_entry->GetAtoi();
							Test_Tell("color=" << new_color << "\n");
							found = True;
							break;
						}
					}
					if (! found)
					{
						//----------------------------------------------------------
						// Search attribute list for name
						//----------------------------------------------------------
						found = False;

						for (
								attribute_entry =attribute_namelist->GetFirstEntry();
								attribute_entry != NULL;
								attribute_entry = attribute_entry->GetNextEntry()
							)
						{
							Check(attribute_entry);
							if (strcmp(attribute_entry->GetName(), buffer) == 0)
   						{
								Check_Pointer(attribute_entry->GetData());
								new_attribute |= attribute_entry->GetAtoi();
   							Test_Tell(
   								"attribute=" << std::hex << new_attribute << std::dec << ">\n"
   							);
   							found = True;
   							break;
   						}
   					}
   					if (! found)
   					{
							DEBUG_STREAM <<
								"Attribute '" << buffer << "' not found!" << std::endl;
   					}
					}
            }
			}
			Check(group_entry);
			Check_Pointer(group_entry->GetName());

#			if DEBUG_LEVEL == 0
				new GroupAttribute(
					group_entry->GetName(),
					new_color,
					new_attribute
				);
#			else
				GroupAttribute
					*group_attribute =
						new GroupAttribute(
							group_entry->GetName(),
							new_color,
							new_attribute
						);
				Check(group_attribute);
				Register_Object(group_attribute);
				Test_Tell("object created\n");
#			endif
		}
		//-------------------------------------------------------------
		// Release the name lists
		//-------------------------------------------------------------
		Test_Tell("loop finished, deleting namelists\n");
		Check(color_namelist);
		Unregister_Object(color_namelist);
		delete color_namelist;

		Check(attribute_namelist);
		Unregister_Object(attribute_namelist);
		delete attribute_namelist;

		Check(group_namelist);
		Unregister_Object(group_namelist);
		delete group_namelist;

		result = True;
	}
	//-------------------------------------------------------------
	// Release the notation file
	//-------------------------------------------------------------
	Test_Tell("deleting .GAT notation file\n");
	Check(gat_file);
	Unregister_Object(gat_file);
	delete gat_file;

	Test_Tell("returning " << result <<"\n");
	Check_Fpu();
	return result;
}

void
	GroupAttribute::Terminate()
{
	Test_Tell("GroupAttribute::Terminate\n");
	GroupAttribute
		*group_attribute,
		*next;

	for(
			group_attribute= head;
			group_attribute != NULL;
			group_attribute = next
		)
	{
		Check(group_attribute);
		next = group_attribute->next;

		Unregister_Object(group_attribute);
		delete group_attribute;
	}

	head = NULL;
	Test_Tell("Termination completed\n");
	Check_Fpu();
}


Logical
	GroupAttribute::Search(const char *name, int *color_dest, int *attrib_dest)
{
	Test_Tell("GroupAttribute::Search("<< name <<")\n");
	GroupAttribute
		*group_attribute;

	for(
			group_attribute= head;
			group_attribute != NULL;
			group_attribute = group_attribute->next
		)
	{
		Check(group_attribute);
		if (
				strncmp(
					name,
					group_attribute->name,
					GroupAttribute::nameLength
				) == 0
			)
		{
			Test_Tell("Found!\n");
			*color_dest = group_attribute->color;
			*attrib_dest = group_attribute->attribute;
			Check_Fpu();
			return True;
		}
	}
	Check_Fpu();
	return False;
}

//############################################################################
// L4GaugeImage
//############################################################################

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Construction and Destruction
//
L4GaugeImage *
	L4GaugeImage::Make(const char */*file_name*/)
{
	return NULL;	// Gauge images come exclusively from resources
}

L4GaugeImage *
	L4GaugeImage::Make(ResourceDescription::ResourceID new_resource_id)
{
	Check(application);
	Check(application->GetResourceFile());

	L4GaugeImage
		*image = NULL;
	//-------------------------------------------
	// Attempt to find the specified resource
	//-------------------------------------------
	ResourceDescription
		*stream_resource_description =
			application->GetResourceFile()->SearchList(
				new_resource_id,
				ResourceDescription::GaugeImageStreamResourceType
			);

	if (stream_resource_description == NULL)
	{
		Tell(
			"L4GaugeImage::Make: resource ID '" << new_resource_id <<
			"' does not exist\n"
		);
	}
	else
	{
		//-------------------------------------------
		// Found it, open a DynamicMemoryStream
		//-------------------------------------------
		Check(stream_resource_description);
		stream_resource_description->Lock();
		MemoryStream
			memory_stream(
				stream_resource_description->resourceAddress,
				stream_resource_description->resourceSize
			);
		if (stream_resource_description->resourceAddress == NULL)
		{
			Tell("L4GaugeImage::Make: couldn't open stream\n");
		}
		else
		{
			//-------------------------------------------
			// Create the object
			//-------------------------------------------
			image = new L4GaugeImage(&memory_stream);
		}
		stream_resource_description->Unlock();
	}
	Check_Fpu();
	return image;
}

L4GaugeImage::L4GaugeImage(MemoryStream *mem_stream)
{
	Test_Tell("L4GaugeImage::L4GaugeImage\n");
	Check_Pointer(this);
	Check(mem_stream);

	int
		i;

	//-------------------------------------------------------------
	// Read the number of vertices
	//-------------------------------------------------------------
	MemoryStream_Read(mem_stream, &vertexCount);
	Verify(vertexCount > 0);
	//-------------------------------------------------------------
	// Allocate the vertex array
	//-------------------------------------------------------------
	vertexArray = new Point3D[vertexCount];
	Check_Pointer(vertexArray);
	Register_Pointer(vertexArray);
	//-------------------------------------------------------------
	// Read in the vertex values
	//-------------------------------------------------------------
	for(i=0; i<vertexCount; ++i)
	{
		MemoryStream_Read(mem_stream, &vertexArray[i]);
	}
	//-------------------------------------------------------------
	//	Read the number of LOD's from the stream
	//-------------------------------------------------------------
	MemoryStream_Read(mem_stream, &LODCount);
	Verify(LODCount > 0);
	//-------------------------------------------------------------
	// Allocate the LOD scales
	//-------------------------------------------------------------
	LODScales = new Scalar[LODCount];
	Check_Pointer(LODScales);
	Register_Pointer(LODScales);
	//-------------------------------------------------------------
	// Read the LOD scales
	//-------------------------------------------------------------
	for(i=0; i<LODCount; ++i)
	{
		MemoryStream_Read(mem_stream, &LODScales[i]);
	}
	//-------------------------------------------------------------
	// Allocate the LOD lists
	//-------------------------------------------------------------
	LODList = new L4GaugeImageList*[LODCount];
	Check_Pointer(LODList);
	Register_Pointer(LODList);
	//-------------------------------------------------------------
	// Read the LOD lists
	//-------------------------------------------------------------
	for(i=0; i<LODCount; ++i)
	{
		LODList[i] = new L4GaugeImageList(mem_stream);
		Check(LODList[i]);
		Register_Object(LODList[i]);
	}
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
L4GaugeImage::~L4GaugeImage()
{
	Test_Tell("L4GaugeImage::~L4GaugeImage\n");
	Check(this);

	int
		i;

	//-------------------------------------------------------------
	// Delete the vertices
	//-------------------------------------------------------------
	Check_Pointer(vertexArray);
	Unregister_Pointer(vertexArray);
	delete[] vertexArray;
	//-------------------------------------------------------------
	// Delete the LOD scales
	//-------------------------------------------------------------
	Check_Pointer(LODScales);
	Unregister_Pointer(LODScales);
	delete[] LODScales;
	//-------------------------------------------------------------
	// Delete the LOD list items
	//-------------------------------------------------------------
	Check_Pointer(LODList);
	for(i=0; i<LODCount; ++i)
	{
		Check(LODList[i]);
		Unregister_Object(LODList[i]);
		delete LODList[i];
	}
	//-------------------------------------------------------------
	// Delete the LOD list
	//-------------------------------------------------------------
	Unregister_Pointer(LODList);
	delete[] LODList;
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ResourceDescription::ResourceID
	L4GaugeImage::CreateL4GaugeImageStream(
		ResourceFile *resource_file,
		const char *model_name,
		NotationFile *model_file,
		const ResourceDirectories *directories
	)
{
	Check(resource_file);
	Check_Pointer(model_name);
	Check(model_file);
	Check_Pointer(directories);

	ResourceDescription::ResourceID
		res_id = ResourceDescription::NullResourceID;

	//----------------------------------------------------------------------
	// Check to see if the model has a gauge image specified...
	//----------------------------------------------------------------------
	const char *gauge_filename;

	if (model_file->GetEntry("gauge", "image", &gauge_filename) != 0)
	{
		//-------------------------------------------------------------
		// It does, attempt to open the given notation file
		//-------------------------------------------------------------
		Tell("Opening gauge image " << gauge_filename << "\n");

		char *gim_filename =
			MakePathedFilename(directories->gaugeDirectory, gauge_filename);
		Register_Pointer(gim_filename);

		NotationFile
			*gim_file = new NotationFile(gim_filename);
		Check(gim_file);
		Register_Object(gim_file);

		//--------------------------------------------------------
		// Search for the name of the .GAT file
		//--------------------------------------------------------
		const char
			*gat_entry;

		if (gim_file->GetEntry("object", "gatfile", &gat_entry) == 0)
		{
			DEBUG_STREAM << "Missing .gat specification!" << std::endl;
		}
		else
		{
			//-------------------------------------------------------------
			// Create the GroupAttribute data
			//-------------------------------------------------------------
			char *gat_filename =
				MakePathedFilename(directories->gaugeDirectory, gat_entry);
			Register_Pointer(gat_filename);

			if (GroupAttribute::Initialize(gat_filename))
			{
				//-------------------------------------------------------------
				// Create a dynamic memory stream, write out the data
				// (note: the call to L4GaugeImage::DynamicMemoryStreamWrite()
				// internally calls the dynamic stream writes for all of the
				// 'owned' sub-parts as well)
				//-------------------------------------------------------------
				DynamicMemoryStream
					*gim_stream = new DynamicMemoryStream();

				Check(gim_stream);
				Register_Object(gim_stream);

				if (
						L4GaugeImage::MemoryStreamWrite(
							gim_stream,
							gim_file
						) 	== True
					)
				{
					//-------------------------------------------------------------
					// Create the resource description, assign an ID
					//-------------------------------------------------------------
					ResourceDescription
						*res_desc_stream =
							resource_file->ResourceFile::AddResourceMemoryStream(
								model_name,
								ResourceDescription::GaugeImageStreamResourceType,
								1,
								ResourceDescription::Preload,
								gim_stream
							);

					Check(res_desc_stream);
					res_id = res_desc_stream->resourceID;
				}

				//-------------------------------------------------------------
				// Release the GroupAttribute data
				//-------------------------------------------------------------
				GroupAttribute::Terminate();

				Check(gim_stream);
				Unregister_Object(gim_stream);
				delete gim_stream;
			}
			Check_Pointer(gat_filename);
			Unregister_Pointer(gat_filename);
			delete gat_filename;
		}
		//-------------------------------------------------------------
		// Release variables
		//-------------------------------------------------------------
		Check(gim_file);
		Unregister_Object(gim_file);
		delete gim_file;

		Check_Pointer(gim_filename);
		Unregister_Pointer(gim_filename);
		delete gim_filename;

		Tell("Closed gauge image\n" << std::flush);
	}
	//-------------------------------------------------------------
	// Return the new ResourceID
	//-------------------------------------------------------------
	Test_Tell("res_id = " << res_id << "\n");
	Check_Fpu();
	return res_id;
}


Logical
	L4GaugeImage::MemoryStreamWrite(
		MemoryStream *mem_stream,
		NotationFile *gim_file
	)
{
	Test_Tell("	L4GaugeImage::MemoryStreamWrite(" << mem_stream <<
		", " << gim_file <<
		")\n"
	);
	Check(mem_stream);
	Check(gim_file);
									  
	int
		i;
	//-------------------------------------------------------------
	// 'result' is set to False if an error occurs.
	//-------------------------------------------------------------
	Logical
		result = True;
	//-------------------------------------------------------------
	// Write the vertices
	//-------------------------------------------------------------
	NameList
		*vertex_list = gim_file->MakeEntryList("vertices");

	if (vertex_list == NULL)
	{
		DEBUG_STREAM << "No 'vertices' page in the .GIM file!" << std::endl;
		result = False;
	}
	else
	{
		Check(vertex_list);
		Register_Object(vertex_list);
		//-------------------------------------------------------------
		//	Get the number of vertices
		//-------------------------------------------------------------
		int
			vertex_count = vertex_list->EntryCount();

		Test_Tell("vertex_count = " << vertex_count << "\n");
		if (vertex_count <= 0)
		{
			DEBUG_STREAM <<
				"The 'vertices' page in the .GIM file has no entries!" <<std::endl;
			result = False;
		}
		else
		{
			//-------------------------------------------------------------
			//	Write the number of vertices to the stream
			//-------------------------------------------------------------
			MemoryStream_Write(mem_stream, &vertex_count);
			//-------------------------------------------------------------
			// Write the vertices
			//-------------------------------------------------------------
			Point3D
				point_3d;
			NameList::Entry
				*vertex_entry = vertex_list->GetFirstEntry();
		
			for (int i=0; i<vertex_count; ++i)
			{
				Verify(vertex_entry != NULL);

				Convert_From_Ascii(vertex_entry->GetChar(), &point_3d);
				Test_Tell("   " << point_3d << "\n");
				MemoryStream_Write(mem_stream, &point_3d);

				vertex_entry = vertex_entry->GetNextEntry();
			}
		}
		//-------------------------------------------------------------
		// Delete the vertex list
		//-------------------------------------------------------------
		Check(vertex_list);
		Unregister_Object(vertex_list);
		delete vertex_list;
	}

	//-------------------------------------------------------------
	// Write the levels of detail
	//-------------------------------------------------------------
	if (result == True)
	{
		NameList
			*lod_namelist = gim_file->MakeEntryList("lods");

		if (lod_namelist == NULL)
		{
			DEBUG_STREAM << "No 'lods' page in the .GIM file!" << std::endl;
			result = False;
		}
		else
		{
			Check(lod_namelist);
			Register_Object(lod_namelist);
			//-------------------------------------------------------------
			//	Get the number of LOD's
			//-------------------------------------------------------------
			int
				lod_count = lod_namelist->EntryCount();
			Test_Tell("lod_count = " << lod_count << "\n");
			if (lod_count <= 0)
			{
				DEBUG_STREAM <<
					"The 'lod' page in the .GIM file has no entries!" << std::endl;
				result = False;
			}
			else
			{
				//-------------------------------------------------------------
				//	Write the number of LOD's to the stream
				//-------------------------------------------------------------
				MemoryStream_Write(mem_stream, &lod_count);
				//-------------------------------------------------------------
				// Write the LOD scales
				//-------------------------------------------------------------
				NameList::Entry
					*scale_entry;
				Scalar
					scale;

				const char
					*text;
		
				scale_entry = lod_namelist->GetFirstEntry();
				for (i=0; i<lod_count; ++i)
				{
					Verify(scale_entry != NULL);
					text = scale_entry->GetChar();
					if (text == NULL)
					{
						DEBUG_STREAM << "Missing scale value!" << std::endl;
						result = False;
						break;
					}
					scale = (Scalar) atof(text);

					Test_Tell("   " << scale << "\n");
					MemoryStream_Write(mem_stream, &scale);

					scale_entry = scale_entry->GetNextEntry();
				}
				//-------------------------------------------------------------
				// Write the LOD lists
				//-------------------------------------------------------------
				scale_entry = lod_namelist->GetFirstEntry();
				for (i=0; i<lod_count; ++i)
				{
					Verify(scale_entry != NULL);

					result = L4GaugeImageList::MemoryStreamWrite(
						mem_stream,
						gim_file,
						scale_entry->GetName()
					);

					if (result == False)
					{
						break;
					}

					scale_entry = scale_entry->GetNextEntry();
				}
			}
		}
		//-------------------------------------------------------------
		// Delete the LOD list
		//-------------------------------------------------------------
		Check(lod_namelist);
		Unregister_Object(lod_namelist);
		delete lod_namelist;
	}
	Check_Fpu();
	return result;
}


void
	L4GaugeImage::Draw(
   	Scalar LOD_value,					// smaller = more detail (1.0 = 'normal')
		Scalar scale_value,				// metersPerPixel
		GraphicsView *graphics_view,
		int default_color,
		AffineMatrix &localToView,
		int boxed_color
	)
{
	Test_Tell(
		"L4GaugeImage::Draw(" << scale_value <<
		", " << graphics_view <<
		", " << default_color <<
		", " << localToView <<
		", " << boxed_color <<
		")\n"
	);
	Check(this);
	Check(graphics_view);
	// localToView allowed to be NULL
	Check_Pointer(LODScales);
	Check_Pointer(LODList);

#	define VERTEX_ALLOTMENT 100	// HACK - I'm guessing this is big enough.

	Verify(vertexCount <= VERTEX_ALLOTMENT);

	static
		Point3D
			local_vertex_array[VERTEX_ALLOTMENT];

	static
		Logical
			local_flag_array[VERTEX_ALLOTMENT];

	int
		lod_index;

	if (localToView == NULL)
	{
		return;
	}
	//-------------------------------------------------------------
	// Find LOD index
	//-------------------------------------------------------------
	for(lod_index=0; lod_index<LODCount; ++lod_index)
	{
		if (LOD_value <= LODScales[lod_index])
		{
			break;
		}
	}
	//-------------------------------------------------------------
	// Continue if the index is within range
	//-------------------------------------------------------------
	if (lod_index < LODCount)
	{
		//-------------------------------------------------------------
		// Clear transformed vertex flag array
		//-------------------------------------------------------------
		Logical
			*flag_pointer = &local_flag_array[0];
		int
			i;

		for(i=0; i<vertexCount; ++i)
		{
			*flag_pointer++ = False;
		}
		//-------------------------------------------------------------
		// Draw LODList item
		//-------------------------------------------------------------
		Check(LODList[lod_index]);

		LODList[lod_index]->Draw(
			graphics_view,
			default_color,
			localToView,
			vertexArray,
			local_vertex_array,
			local_flag_array,
			scale_value,
			boxed_color
		);
	}
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Test Instance
//
Logical
	L4GaugeImage::TestInstance() const
{
	return True;
}
//############################################################################
// L4GaugeImageList
//############################################################################

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Construction and Destruction
//
L4GaugeImageList::L4GaugeImageList(MemoryStream *mem_stream)
{
	Test_Tell("L4GaugeImageList::L4GaugeImageList\n");
	Check_Pointer(this);
	Check(mem_stream);

	int
		i;

	//-------------------------------------------------------------
	//	Read the number of primitives from the stream
	//-------------------------------------------------------------
	MemoryStream_Read(mem_stream, &primitiveCount);
	Verify(primitiveCount > 0);
	//-------------------------------------------------------------
	// Allocate the primitive list
	//-------------------------------------------------------------
	primitiveList = new L4GaugeImagePrimitive*[primitiveCount];
	Check_Pointer(primitiveList);
	Register_Pointer(primitiveList);
	//-------------------------------------------------------------
	// Read the primitives
	//-------------------------------------------------------------
	for(i=0; i<primitiveCount; ++i)
	{
		primitiveList[i] = new L4GaugeImagePrimitive(mem_stream);
		Check(primitiveList[i]);
		Register_Object(primitiveList[i]);
	}
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
L4GaugeImageList::~L4GaugeImageList()
{
	Test_Tell("L4GaugeImageList::~L4GaugeImageList\n");
	Check(this);
	Check_Pointer(primitiveList);

	int
		i;

	for(i=0; i<primitiveCount; ++i)
	{
		Check(primitiveList[i]);
		Unregister_Object(primitiveList[i]);
		delete primitiveList[i];
	}
	Unregister_Pointer(primitiveList);
	delete[] primitiveList;
	Check_Fpu();
}

Logical
	L4GaugeImageList::MemoryStreamWrite(
		MemoryStream *mem_stream,
		NotationFile *gim_file,
		const char *page_name
	)
{
	Test_Tell("	L4GaugeImageList::MemoryStreamWrite(" << mem_stream <<
		", " << gim_file <<
		", " << page_name <<
		")\n"
	);
	Check(mem_stream);
	Check(gim_file);

	int
		i;
	Logical
		result = True;
	NameList
		*primitive_list = gim_file->MakeEntryList(page_name);

	if (primitive_list == NULL)
	{
		DEBUG_STREAM << "Can't find the '" << page_name << "' page in the .GIM file!"
			<< std::endl;
		result = False;
	}
	else
	{
		Check(primitive_list);
		Register_Object(primitive_list);
		//-------------------------------------------------------------
		//	Get the number of primitives
		//-------------------------------------------------------------
		int
			primitive_count = primitive_list->EntryCount();

		Test_Tell("   primitive_count = " << primitive_count << "\n");
		if (primitive_count <= 0)
		{
			DEBUG_STREAM << "The '"<<page_name<<"' page in the .GIM file has no entries!"
				<< std::endl;
			result = False;
		}
		else
		{
			//-------------------------------------------------------------
			//	Write the number of primitives to the stream
			//-------------------------------------------------------------
			MemoryStream_Write(mem_stream, &primitive_count);
			//-------------------------------------------------------------
			// Write the primitives
			//-------------------------------------------------------------
			NameList::Entry
				*primitive_entry = primitive_list->GetFirstEntry();
			for (i=0; i<primitive_count; ++i)
			{
				Verify(primitive_entry != NULL);

				result = L4GaugeImagePrimitive::MemoryStreamWrite(
					mem_stream,
					primitive_entry->GetName(),
					primitive_entry->GetChar()
				);

				if (result == False)
				{
					break;
				}

				primitive_entry = primitive_entry->GetNextEntry();
			}
		}
		//-------------------------------------------------------------
		// Delete the primitive list
		//-------------------------------------------------------------
		Check(primitive_list);
		Unregister_Object(primitive_list);
		delete primitive_list;
	}
	Check_Fpu();
	return result;
}



//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//	Test Support
//
Logical
	L4GaugeImageList::TestInstance() const
{
	return True;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	L4GaugeImageList::Draw(
		GraphicsView *graphics_view,
		int default_color,
		AffineMatrix &localToView,
		Point3D *untransformed_vertex_array,
		Point3D *transformed_vertex_array,
		Logical *transformed_flag_array,
		Scalar scale_factor,
		int boxed_color
	)
{
	Test_Tell(
		"L4GaugeImageList::Draw(" << graphics_view <<
		", " << default_color <<
		", " << localToView <<
		", " << untransformed_vertex_array <<
		", " << transformed_vertex_array <<
		", " << transformed_flag_array <<
		", " << scale_factor <<
		", " << boxed_color <<
		")\n"
	);
	Check(this);
	Check(graphics_view);
	Check(&localToView);
	Check_Pointer(untransformed_vertex_array);
	Check_Pointer(transformed_vertex_array);
	Check_Pointer(transformed_flag_array);

	Check_Pointer(primitiveList);

	int
		i;
	L4GaugeImagePrimitive
		**array_pointer = primitiveList;

	if (boxed_color != 0)
	{
		//--------------------------------------------------------
		// Initialize bounding box
		//--------------------------------------------------------
		Rectangle2D
			bounds(9999,9999,-9999,-9999);
		//--------------------------------------------------------
		// Draw primitives, collect bounding data
		//--------------------------------------------------------
		for(i=0; i<primitiveCount; ++i)
		{
			Test_Tell("List #" << i << " of " << primitiveCount << "\n");
			Check(*array_pointer);
			(*array_pointer++)->Draw(
				graphics_view,
				default_color,
				localToView,
				untransformed_vertex_array,
				transformed_vertex_array,
				transformed_flag_array,
				scale_factor,
				&bounds
			);
		}
		//--------------------------------------------------------
		// Draw the bounding box
		//--------------------------------------------------------
		graphics_view->SetColor(boxed_color);

		graphics_view->MoveToAbsolute(
			bounds.bottomLeft.x-4,
			bounds.bottomLeft.y-4
		);
		graphics_view->DrawRectangleToAbsolute(
			bounds.topRight.x+4,
			bounds.topRight.y+4
		);
	}
	else
	{
		//--------------------------------------------------------
		// Just draw it
		//--------------------------------------------------------
		for(i=0; i<primitiveCount; ++i)
		{
			Test_Tell("List #" << i << " of " << primitiveCount << "\n");
			Check(*array_pointer);
			(*array_pointer++)->Draw(
				graphics_view,
				default_color,
				localToView,
				untransformed_vertex_array,
				transformed_vertex_array,
				transformed_flag_array,
				scale_factor,
				NULL
			);
		}
	}
	Check_Fpu();
}

//############################################################################
// L4GaugeImagePrimitive
//############################################################################

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//	Construction and Destruction
//
L4GaugeImagePrimitive::L4GaugeImagePrimitive(MemoryStream *mem_stream)
{
	Test_Tell("L4GaugeImagePrimitive::L4GaugeImagePrimitive\n");
	Check_Pointer(this);
	Check(mem_stream);

	int
		i,
		n;
	//--------------------------------------------------------------
	// Read the primitive type ID, use the input stream accordingly
	//--------------------------------------------------------------
	MemoryStream_Read(mem_stream, &n);
	type = (primitiveType) n;

	switch(type)
	{
	case primitiveVector:
		Test_Tell("      primitiveVector\n");
		//--------------------------------------------------------
		// Read the attribute and color
		//--------------------------------------------------------
		MemoryStream_Read(mem_stream, &color);
		MemoryStream_Read(mem_stream, &attributes);
		Test_Tell("      color = " << color << "\n");
		Test_Tell("      attributes = " << std::hex << attributes << std::dec << "\n");
		//--------------------------------------------------------
		// Read the vertex index count
		//--------------------------------------------------------
		MemoryStream_Read(mem_stream, &data.vector.indexCount);
		Test_Tell("      indexCount = " << data.vector.indexCount << "\n");
		Verify(data.vector.indexCount > 0);     
		//--------------------------------------------------------
		// Allocate the index table
		//--------------------------------------------------------
		data.vector.indexList = new int[data.vector.indexCount];
		Check_Pointer(data.vector.indexList);
		Register_Pointer(data.vector.indexList);
		//--------------------------------------------------------
		// Read the indices
		//--------------------------------------------------------
		for(i=0; i<data.vector.indexCount; ++i)
		{
			MemoryStream_Read(mem_stream, &data.vector.indexList[i]);
			Test_Tell("         " << data.vector.indexList[i] << "\n");
		}
		break;

	default:
		Warn(
			"L4GaugeImagePrimitive::L4GaugeImagePrimitive: Evil type_id!");
		break;
	}
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
L4GaugeImagePrimitive::~L4GaugeImagePrimitive()
{
	Test_Tell("L4GaugeImagePrimitive destructor\n");
	Check(this);

	switch(type)
	{
	case primitiveVector:
		Check_Pointer(data.vector.indexList);
		Unregister_Pointer(data.vector.indexList);
		delete[] data.vector.indexList;
		break;

	default:
		Warn("L4GaugeImagePrimitive::~L4GaugeImagePrimitive: Evil type_id!");
		break;
	}
	Check_Fpu();
}


Logical
	L4GaugeImagePrimitive::MemoryStreamWrite(
		MemoryStream *mem_stream,
		const char *type_name,
		const char *data
	)
{
	Test_Tell("L4GaugeImagePrimitive::MemoryStreamWrite(" <<mem_stream<<
		", " << type_name <<
		", " << data <<
		")\n"
	);
	Check(mem_stream);
	Check_Pointer(type_name);
	Check_Pointer(data);

#	define MAX_INDICES	64

	int
		i,
		type_number,
		attribute_value = 0,
		color_value = 0,
		index[MAX_INDICES],
		index_count = 0;
	Logical
		result = True;

	//--------------------------------------------------------
	// LINELIST primitive
	//--------------------------------------------------------
	if (stricmp(type_name, "linelist") == 0)
	{
		Test_Tell("   LINELIST\n");
		//--------------------------------------------------------
		// Write the primitive type ID
		//--------------------------------------------------------
		type_number = L4GaugeImagePrimitive::primitiveVector;
		MemoryStream_Write(mem_stream, &type_number);
		//--------------------------------------------------------
		// Parse vertices (which start with 'v')
		// and parse attributes (which start with 'a')
		//--------------------------------------------------------
		const char
			*scan = data;

#		if (DEBUG_LEVEL > 0)
			int
				check_length = strlen(data)+1;
#		endif

		while(*scan != '\0')
		{
			Verify((scan-data) < check_length);
			//--------------------------------------------------------
			// Skip white space
			//--------------------------------------------------------
			while (isspace(*scan))
			{
				++scan;
			}
			//--------------------------------------------------------
			// Parse non-space string
			//--------------------------------------------------------
			if (*scan == 'v')
			{
				int
            	n;
				//--------------------------------------------------------
				// Convert to value, save in array
				//--------------------------------------------------------
				++scan;									// ignore 'v'
				for(n=0; isdigit(*scan); ++scan)
				{
					n = (n*10) + (*scan - '0');
				}
				Test_Tell("      " << n << "\n");

				if (index_count < MAX_INDICES)
				{
					index[index_count++] = n;
				}
			}
			else if (*scan == 'a')
			{
				char
					buffer[64],
					*dest;
				//--------------------------------------------------------
				// Collect the name
				//--------------------------------------------------------
				for(
						i=0, dest=buffer;
						i<(sizeof(buffer)-1);
						++i,++scan,++dest
					)
				{
					if (isspace(*scan))
					{
						break;
					}
					if (*scan == '\0')
					{
						break;
					}
					*dest = *scan;
				}
				*dest = '\0';
				//--------------------------------------------------------
				// Search for the name
				//--------------------------------------------------------
				if (
						GroupAttribute::Search(
							buffer,
							&color_value,
							&attribute_value
						) == False
					)
				{
					DEBUG_STREAM << "Couldn't find attribute group '" <<
						buffer << "'!" << std::endl;
				}
			}
		}
		//--------------------------------------------------------
		// Write the attributes
		//--------------------------------------------------------
		Test_Tell("   color=" << color_value << "\n");
		MemoryStream_Write(mem_stream, &color_value);
		Test_Tell("   attribute=" << attribute_value << "\n");
		MemoryStream_Write(mem_stream, &attribute_value);
		//--------------------------------------------------------
		// Write the vertex index count
		//--------------------------------------------------------
		if (index_count == 0)
		{
			DEBUG_STREAM << "No vertex indices in primitive!" << std::endl;
			result = False;
		}
		else
		{
			Test_Tell("   index_count=" << index_count << "\n");
			MemoryStream_Write(mem_stream, &index_count);
			//--------------------------------------------------------
			// Write the indices
			//--------------------------------------------------------
			for(i=0; i < index_count; ++i)
			{
				MemoryStream_Write(mem_stream, &index[i]);
			}
		}
	}
	else
	{
		DEBUG_STREAM << "Unsupported primitive ("<< type_name << ") in .GIM file!"
			<< std::endl;
		result = False;
	}
	Check_Fpu();
	return result;
}


//============================================================================
// TestInstance
//============================================================================
Logical
	L4GaugeImagePrimitive::TestInstance() const
{
	return True;
}
//============================================================================
// Draw
//============================================================================
void
	L4GaugeImagePrimitive::Draw(
		GraphicsView *graphics_view,
		int default_color,
		AffineMatrix &localToView,
		Point3D *untransformed_vertex_array,
		Point3D *transformed_vertex_array,
		Logical *transformed_flag_array,
		Scalar scale_factor,
		Rectangle2D *bounds
	)
{
	Test_Tell(
		"L4GaugeImagePrimitive::Draw(" << graphics_view <<
		", " << default_color <<
		", " << localToView <<
		", " << untransformed_vertex_array <<
		", " << transformed_vertex_array <<
		", " << transformed_flag_array <<
		", " << scale_factor <<
		", " << bounds <<
		")\n"
	);

	Check(this);
	Check(graphics_view);
	Check(&localToView);
	Check_Pointer(untransformed_vertex_array);
	Check_Pointer(transformed_vertex_array);
	Check_Pointer(transformed_flag_array);

	//-----------------------------------------------
	// Draw primitive
	//-----------------------------------------------
	switch(type)
	{
	case primitiveVector:
		if (data.vector.indexCount > 1)
		{
			//-----------------------------------------------
			// Set color
			//-----------------------------------------------
			if (color != 0)
			{
				default_color = color;
			}
			graphics_view->SetColor(default_color);

			int
				i,
				*index_pointer(data.vector.indexList),
				index;
			Point3D
				*dest;
			//-----------------------------------------------
			// Move to starting point
			//-----------------------------------------------
			index = *index_pointer++;

			dest = &transformed_vertex_array[index];

			TransformVertex(
				localToView,
				untransformed_vertex_array[index],
				dest,
				transformed_flag_array[index],
				scale_factor,
				bounds
			);


			Test_Tell("Start at " << index << "=" <<  *dest <<  "\n");
			graphics_view->MoveToAbsolute((int)dest->x,(int)dest->z);
			//-----------------------------------------------
			// Draw subsequent lines
			//-----------------------------------------------
			for(i=data.vector.indexCount-1; i>0; --i)
			{
				index = *index_pointer++;

				dest = &transformed_vertex_array[index];

				TransformVertex(
					localToView,
					untransformed_vertex_array[index],
					dest,
					transformed_flag_array[index],
					scale_factor,
					bounds
				);

				Test_Tell("Draw to " << index << "=" << *dest <<  "\n");
				graphics_view->DrawLineToAbsolute((int)(dest->x),(int)(dest->z));
			}
		}
		break;
	default:
		Warn("L4GaugeImagePrimitive::Draw: Evil type_id!");
		break;
	}
	Check_Fpu();
}

void
	L4GaugeImagePrimitive::TransformVertex(
		AffineMatrix &local_to_view,
		Point3D &untransformed_vertex,
		Point3D *transformed_vertex,
		Logical &transformed_flag,
		Scalar scale_factor,
		Rectangle2D *bounds
	)
{
	Check(this);
	Check_Pointer(transformed_vertex);

	//-----------------------------------------
	// Transform only if not already done
	//-----------------------------------------
	if (! transformed_flag)
	{
		if (attributes & attributeUnscaled)
		{
			//-----------------------------------------
			// Unscaled: pre-multiply by scaling factor
			//-----------------------------------------
			Point3D
				temp;

			temp = untransformed_vertex;
			temp *= scale_factor;
			Check_Fpu();

			transformed_vertex->Multiply(
				temp,
				local_to_view
			);
			Check_Fpu();
		}
		else
		{
			//-----------------------------------------
			// Normally scaled: transform the vertex
			//-----------------------------------------
			transformed_vertex->Multiply(
				untransformed_vertex,
				local_to_view
			);
			Check_Fpu();
		}

		//-----------------------------------------
		// Round, and fix 'y' sign
		//-----------------------------------------
		transformed_vertex->x += .5;
		transformed_vertex->z = -transformed_vertex->z + .5;

		//-----------------------------------------------
		// Set the 'transformed' flag
		//-----------------------------------------------
		transformed_flag = True;
	}

	//-----------------------------------------------
	// Accumulate bounds
	//-----------------------------------------------
	if (bounds != NULL)
	{
		if (transformed_vertex->x < bounds->bottomLeft.x)
		{
			bounds->bottomLeft.x = transformed_vertex->x;
		}
		if (transformed_vertex->x > bounds->topRight.x)
		{
			bounds->topRight.x = transformed_vertex->x;
		}

		if (transformed_vertex->z < bounds->bottomLeft.y)
		{
			bounds->bottomLeft.y = transformed_vertex->z;
		}
		if (transformed_vertex->z > bounds->topRight.y)
		{
			bounds->topRight.y = transformed_vertex->z;
		}
	}
	Check_Fpu();
}
