#ifndef _T_JConstHashCursor
#define _T_JConstHashCursor

/******************************************************************************
 JConstHashCursor.tmpl

	JConstHashCursor is the basic cursor for JHashTable, providing the ability
	to step through the hash table appropriately and read entries.  As the name
	implies, it cannot modify the table; however, it can be used on a const
	table.  Its subclass JHashCursor can modify its table but may not be used
	on a const table.

	Usage

	A JCore cursor behaves like a JCore iterator, except it isn't safe; if one
	cursor modifies a table while another is examining it the second cursor may
	well be left in an invalid state.  In that regard it is more like a simple
	array index.  Cursor usage generally fits the following stereotype:

	(1) Create a cursor attached to the desired table (or reset an existing one
	already attached), (2) call one of its Next... functions in a while loop,
	(3) examine each record found in the loop body for some property, and
	possibly (4) modify a record and then exit the loop.

	For example, to examine all records with a given hash value to find one
	to delete:

		JHashTable<V> table;
			...
		JHashCursor<V> cursor(&table, hash);
		while ( cursor.NextHash() )
			{
			V value = cursor.GetValue();
			if (value == theRightValue)
				{
				cursor.Delete();
				break;
				}
			}

	The break is important; after modifying the table the cursor might not
	be in a valid state and must be reset before using again.  This is true
	whenever the table is modified for all cursors then attached to it.

	Note that the hash value was passed in--presumably the client called the
	hash function or already had it on hand.  Used as shown, the table calls
	neither the hash function nor the comparison function pointers at all and
	is an example of purely manual use.

Topics to add:

	Need example of use with hash and comparison functions

	Allows passing in hash values even when key is also there for efficiency;
	don't screw it up.

	NextMapInsertHash, NextMapInsertKey backup is dangerous.

	*** Warning about moving records from one table to another with a different
	hash function.

	BASE CLASS = <NONE>

	Copyright © 1997 by Dustin Laurence.  All rights reserved.
	
	Base code generated by Codemill v0.1.0

 *****************************************************************************/

//Class Header
#include <JConstHashCursor.h>

#include <jAssert.h>

/******************************************************************************
 Constructor

	The version without a hash value or key parameter iterates through the
	entire table, obviously in no perceptible order.  Those which take a hash
	value or key pointer iterate over all records containing the given hash
	value or key, and if a key pointer was specified the key to which it points
	must exist while the cursor is being used (though it need not exist when
	the cursor is destructed, because the destructor will not reference the
	pointer).

 *****************************************************************************/

template <class V>
JConstHashCursor<V>::JConstHashCursor
	(
	const JHashTable<V>* table
	)
	:
	itsTable(table),
	itsValue(NULL),
	itsHashValue(0),
	itsInitialIndex(0),
	itsIndex(0),
	itsIncrement(1),
	itsInitialCount(0)
{
	assert(itsTable != NULL);
}

template <class V>
JConstHashCursor<V>::JConstHashCursor
	(
	const JHashTable<V>* table,
	const JHashValue hash
	)
	:
	itsTable(table),
	itsValue(NULL),
	itsHashValue(hash),
//	itsInitialIndex(0),
//	itsIndex(0),
	itsIncrement( DualHash(itsHashValue) ),
	itsInitialCount(0)
{
	assert(itsTable != NULL);

	itsInitialIndex = itsIndex = itsTable->HashToIndex(itsHashValue);
}

#if 0
template <class V>
JConstHashCursor<V>::JConstHashCursor
	(
	const JHashTable<V>* const table,
	const K&               key
	)
	:
	itsTable(table),
	itsValue(NULL),
	itsHashValue( Hash(*itsValue) ),
//	itsInitialIndex(0),
//	itsIndex(0),
	itsIncrement( DualHash(itsHashValue) ),
	itsInitialCount(0)
{
	assert(itsTable != NULL);
	assert(itsValue != NULL);

	itsInitialIndex = itsIndex = itsTable->HashToIndex(itsHashValue);
}
#endif

/******************************************************************************
 Destructor

 *****************************************************************************/

template <class V>
JConstHashCursor<V>::~JConstHashCursor()
{
}

/******************************************************************************
 copy constructor and assignment operator (default versions)

	The meaning of copying and assignment of cursors is straightforward; after
	the the same series of operations with either cursor will have exactly the
	same behavior and effects as with the other.  However, it is easy to get
	into trouble using cursor copies, since neither cursor knows about the
	other and changes to and/or by one do not affect the other.  For example,
	after the calls

		cursor2 = cursor1;
		cursor1.NextFull();

	cursor2 still points to the original record, not the new one that cursor1
	stepped to.  After a subsequent

		cursor2.NextFull();

	they would again point to the same record.  More complex behavior occurs
	when the table is modified.  If the above example continues

		cursor1.NextFull();
		cursor1.Remove();
		cursor2.NextFull();

	the cursors would not point to the same record because cursor2 will step
	over the formerly full record that was deleted by cursor1.

	In spite of this hazardous complexity it is occasionally crucial to be
	able to copy cursors, for example to remember a position and then scan
	on (an example is in the code to do a Set() for a map or associative
	array).  As always, hide your dangerous operations behind opaque
	interfaces!

 *****************************************************************************/

/******************************************************************************
 Next...

	These functions are the cursor's reason for being.  If an appropriate
	unvisited element remains, advances to the next one and returns kTrue;
	otherwise, returns kFalse.  They only differ in what kinds of elements
	they seek and which they skip over.  Calls to different Next... functions
	may be freely intermixed with each other without a Reset.

 *****************************************************************************/

/******************************************************************************
 Next

	The form with no arguments visits all records, regardless of state or hash
	value.

 *****************************************************************************/

template <class V>
JBoolean
JConstHashCursor<V>::Next()
{
	switch (itsInitialCount)
	{
	case 0:
		++itsInitialCount;
		return kTrue;
		break;

	case 1:
		Step();
		if (GetIndex() == itsInitialIndex)
			{
			++itsInitialCount;
			return kFalse;
			}
		return kTrue;
		break;

	case 2:
		return kFalse;
		break;

	default:
		assert(0); // Should never happen!
		return kFalse;
		break;
	}
}


// Is this function worth having?
template <class V>
JBoolean
JConstHashCursor<V>::Next
	(
	JBoolean (*NextRecordType)(const JHashRecord<V>&),
	const JBoolean stopOnEmpty
	)
{
	switch (itsInitialCount)
	{
	case 0:
		++itsInitialCount;
		while ( !NextRecordType( GetRecord() ) )
			{
			Step();
			if (GetIndex() == itsInitialIndex)
				{
				++itsInitialCount;
				return kFalse;
				}
			}
		if (stopOnEmpty && IsEmpty() )
			{
			++itsInitialCount;
			}
		return kTrue;
		break;

	case 1:
		do
			{
			Step();
			if (GetIndex() == itsInitialIndex)
				{
				++itsInitialCount;
				return kFalse;
				}
			}
			while ( !NextRecordType( GetRecord() ) );
		if (stopOnEmpty && IsEmpty() )
			{
			++itsInitialCount;
			}
		return kTrue;
		break;

	case 2:
		return kFalse;
		break;

	default:
		assert(0); // Should never happen!
		return kFalse;
		break;
	}
}

/******************************************************************************
 NextFull

	Only visits full records.  Will step past kEmpty records, since it is
	intended for stepping through all records in the hash table.

 *****************************************************************************/

template <class V>
JBoolean
JConstHashCursor<V>::NextFull()
{
	switch (itsInitialCount)
	{
	case 0:
		++itsInitialCount;
		while ( !IsFull() )
			{
			Step();
			if (GetIndex() == itsInitialIndex)
				{
				++itsInitialCount;
				return kFalse;
				}
			}
		return kTrue;
		break;

	case 1:
		do
			{
			Step();
			if (GetIndex() == itsInitialIndex)
				{
				++itsInitialCount;
				return kFalse;
				}
			}
			while ( !IsFull() );
		return kTrue;
		break;

	case 2:
		return kFalse;
		break;

	default:
		assert(0); // Should never happen!
		return kFalse;
		break;
	}
}

/******************************************************************************
 NextOpen

	Only visits open (empty or deleted) records.

	After stopping on an empty record it will not step further on subsequent
	calls.

 *****************************************************************************/

template <class V>
JBoolean
JConstHashCursor<V>::NextOpen()
{
	switch (itsInitialCount)
	{
	case 0:
		++itsInitialCount;
		while ( IsFull() )
			{
			Step();
			if (GetIndex() == itsInitialIndex)
				{
				++itsInitialCount;
				return kFalse;
				}
			}
		if ( IsEmpty() )
			{
			++itsInitialCount;
			}
		return kTrue;
		break;

	case 1:
		do
			{
			Step();
			if (GetIndex() == itsInitialIndex)
				{
				++itsInitialCount;
				return kFalse;
				}
			}
			while ( IsFull() );
		if ( IsEmpty() )
			{
			++itsInitialCount;
			}
		return kTrue;
		break;

	case 2:
		return kFalse;
		break;

	default:
		assert(0); // Should never happen!
		return kFalse;
		break;
	}
}

/******************************************************************************
 NextHash

	Only visits records with the same hash value as was used to initialize the
	cursor (and so it better have been initialized with a key or hash value).

	If the argument is kFalse (the default) NextHash will return kFalse as soon
	as it steps to an empty record, so the client code never sees the empty
	record.  If kTrue, it will return kTrue for the last time when it steps to
	an empty record.  In either case all subsequent calls will return kFalse.

 *****************************************************************************/

template <class V>
JBoolean
JConstHashCursor<V>::NextHash
	(
	const JBoolean allowEmpty // = kFalse
	)
{
	switch (itsInitialCount)
	{
	case 0:
		++itsInitialCount;
		while ( IsDeleted() || (IsFull() && GetHashValue() != itsHashValue) )
			{
			Step();
			if (GetIndex() == itsInitialIndex)
				{
				++itsInitialCount;
				return kFalse;
				}
			}
		if ( IsEmpty() )
			{
			++itsInitialCount;
			return allowEmpty;
			}
		else
			{
			return kTrue;
			}
		break;

	case 1:
		do
			{
			Step();
			if (GetIndex() == itsInitialIndex)
				{
				++itsInitialCount;
				return kFalse;
				}
			}
			while ( IsDeleted() || (IsFull() && GetHashValue() != itsHashValue) );
		if ( IsEmpty() )
			{
			++itsInitialCount;
			return allowEmpty;
			}
		else
			{
			return kTrue;
			}
		break;

	case 2:
		return kFalse;
		break;

	default:
		assert(0); // Should never happen!
		return kFalse;
		break;
	}
}

/******************************************************************************
 NextHashOrOpen

	Visits records which are empty or deleted or that are full and have the same
	hash value as was used to initialize the cursor (and so it better have been
	initialized with a key or hash value).  This strange behavior is needed to
	implement maps (associative arrays).

 *****************************************************************************/

template <class V>
JBoolean
JConstHashCursor<V>::NextHashOrOpen()
{
	switch (itsInitialCount)
	{
	case 0:
		++itsInitialCount;
		while (IsFull() && GetHashValue() != itsHashValue)
			{
			Step();
			if (GetIndex() == itsInitialIndex)
				{
				++itsInitialCount;
				return kFalse;
				}
			}
		if ( IsEmpty() )
			{
			++itsInitialCount;
			}
		return kTrue;
		break;

	case 1:
		do
			{
			Step();
			if (GetIndex() == itsInitialIndex)
				{
				++itsInitialCount;
				return kFalse;
				}
			}
			while (IsFull() && GetHashValue() != itsHashValue);
		if ( IsEmpty() )
			{
			++itsInitialCount;
			}
		return kTrue;
		break;

	case 2:
		return kFalse;
		break;

	default:
		assert(0); // Should never happen!
		return kFalse;
		break;
	}
}

/******************************************************************************
 NextKey

	Only visits records with the same key as was used to initialize the cursor
	(and so it better have been initialized with a key).  Naturally, the key
	whose address was passed in must still exist.

	If the argument is kFalse (the default) NextKey will return kFalse as soon
	as it steps to an empty record, so the client code never sees the empty
	record.  If kTrue, it will return kTrue for the last time when it steps to
	an empty record.  In either case all subsequent calls will return kFalse.

 *****************************************************************************/

template <class V>
JBoolean
JConstHashCursor<V>::NextKey
	(
	const JBoolean allowEmpty // = kFalse
	)
{
	assert(itsValue != NULL);

	switch (itsInitialCount)
	{
	case 0:
		++itsInitialCount;
		while
			(
				IsDeleted() 
				||
				(
					IsFull()
					&&
					(GetHashValue() != itsHashValue || !Equal(GetValue(), *itsValue) )
				)
			)
			{
			Step();
			if (GetIndex() == itsInitialIndex)
				{
				++itsInitialCount;
				return kFalse;
				}
			}
		if ( IsEmpty() )
			{
			++itsInitialCount;
			return allowEmpty;
			}
		else
			{
			return kTrue;
			}
		break;

	case 1:
		do
			{
			Step();
			if (GetIndex() == itsInitialIndex)
				{
				++itsInitialCount;
				return kFalse;
				}
			}
			while
				(
					IsDeleted() 
					||
					(
						IsFull()
						&&
						(GetHashValue() != itsHashValue || !Equal(GetValue(), *itsValue) )
					)
				);
		if ( IsEmpty() )
			{
			++itsInitialCount;
			return allowEmpty;
			}
		else
			{
			return kTrue;
			}
		break;

	case 2:
		return kFalse;
		break;

	default:
		assert(0); // Should never happen!
		return kFalse;
		break;
	}
}

/******************************************************************************
 NextKeyOrOpen

	Visits records which are empty or deleted or that are full and have the
	same key as was used to initialize the cursor (and so it better have been
	initialized with a key or hash value).  This strange behavior is needed
	to implement maps (associative arrays).

 *****************************************************************************/

template <class V>
JBoolean
JConstHashCursor<V>::NextKeyOrOpen()
{
	assert(itsValue != NULL);

	switch (itsInitialCount)
	{
	case 0:
		++itsInitialCount;
		while (IsFull() && GetHashValue() != itsHashValue && Equal(GetValue(), *itsValue) )
			{
			Step();
			if (GetIndex() == itsInitialIndex)
				{
				++itsInitialCount;
				return kFalse;
				}
			}
		if ( IsEmpty() )
			{
			++itsInitialCount;
			}
		return kTrue;
		break;

	case 1:
		do
			{
			Step();
			if (GetIndex() == itsInitialIndex)
				{
				++itsInitialCount;
				return kFalse;
				}
			}
			while (IsFull() && GetHashValue() != itsHashValue && Equal(GetValue(), *itsValue) );
		if ( IsEmpty() )
			{
			++itsInitialCount;
			}
		return kTrue;
		break;

	case 2:
		return kFalse;
		break;

	default:
		assert(0); // Should never happen!
		return kFalse;
		break;
	}
}

/******************************************************************************
 NextMapInsertHash

	FindMapInsertHash finds the next location for a map- or array-style
	insertion, and is mostly useful for implementing associative arrays, or
	maps.  The resulting behavior is intuitive in action precisely because
	it is designed to mimic a simple array indexed by the key type, but
	implementing this behavior in a data structure which has no such
	intrinsic behavior is somewhat complex complex.

	Starting from the current position, it returns kTrue positioned at the
	first slot that has the hash value the cursor was initialized with (so
	you better have initialized it with a hash value or key).  If it finds
	no such hash value before the scan terminates (at an empty slot or by
	wrapping over the entire table), it backs up to the first deleted slot
	it encountered.  If there was no deleted slot, it returns kTrue at the
	empty slot which stopped the scan; in this case the cursor is finished
	and will return kFalse on all subsequent calls.  If there is no empty
	slot (which means the table is full, so it shouldn't happen normally)
	it returns kFalse (and of course is also finished).

	A key- rather than hash-based search could be implemented by repeated
	calls to NextMapInsertHash, but NextMapInsertKey() does the job
	automatically (if you're using the built-in key comparison facilities,
	of course).

 *****************************************************************************/

template <class V>
JBoolean
JConstHashCursor<V>::NextMapInsertHash()
{
	while ( NextHashOrOpen() )
		{
		if ( IsDeleted() )
			{
			const JSize deleted = itsIndex;
			const JSize initialCount = itsInitialCount;
			while ( NextHash(kTrue) )
				{
				if ( IsEmpty() )
					{
					break; // Return deleted
					}
				else if (GetHashValue() == itsHashValue)
					{
					return kTrue;
					}
				}
			// Back up
			itsIndex = deleted;
			itsInitialCount = initialCount;
			return kTrue;
			}
		else if (GetHashValue() == itsHashValue || IsEmpty() )
			{
			return kTrue;
			}
		}

	return kFalse;
}

/******************************************************************************
 NextMapInsertKey

	Like NextMapInsertHash(), but stops on key matches rather than hash value
	matches.  Obviously, the table has to have a comparison function, and the
	cursor must have been initialized with a key.

 *****************************************************************************/

template <class V>
JBoolean
JConstHashCursor<V>::NextMapInsertKey()
{
	while ( NextHashOrOpen() )
		{
		if ( IsDeleted() )
			{
			const JSize deleted = itsIndex;
			const JSize initialCount = itsInitialCount;
			while ( NextHash(kTrue) )
				{
				if ( IsEmpty() )
					{
					break; // Return deleted
					}
				else if ( GetHashValue() == itsHashValue && Equal(GetValue(), *itsValue) )
					{
					return kTrue;
					}
				}
			// Back up
			itsIndex = deleted;
			itsInitialCount = initialCount;
			return kTrue;
			}
		else if ( IsEmpty() || ( GetHashValue() == itsHashValue && Equal(GetValue(), *itsValue) ) )
			{
			return kTrue;
			}
		}

	return kFalse;
}

/******************************************************************************
 Reset

	With a boolean argument, kTrue makes the cursor ready for an iteration
	starting at index zero, stepping by one.  If the argument is kFalse (the
	default) makes ready for a new iteration using the previous parameters
	(either the same hash value or single-stepping with no hash value).

	With a hash value, makes the cursor ready for an iteration starting from
	the index corresponding to the given hash value, stepping by the dual hash
	of that hash value.

	With a key, the cursor is also ready to use the key-specific functions.
	However, since it only stores a key reference, the object refered to must
	exist throughout the entire time the cursor is in use.

	The hash and key versions only have different names because C++ can't be
	trusted to disambiguate pointers and integers.

 *****************************************************************************/

template <class V>
void
JConstHashCursor<V>::Reset
	(
	const JBoolean clear // = kFalse
	)
{
	if (clear)
		{
		itsHashValue = 0;
		itsIncrement = 1;
		itsValue = NULL;
		}

	itsInitialIndex = itsIndex = itsTable->HashToIndex(itsHashValue);
	itsInitialCount = 0;
}

template <class V>
void
JConstHashCursor<V>::ResetHash
	(
	const JHashValue hash
	)
{
	itsValue = NULL;
	itsHashValue = hash;
	itsInitialIndex = itsIndex = itsTable->HashToIndex(itsHashValue);
	itsIncrement = DualHash(itsHashValue);
	itsInitialCount = 0;
}

template <class V>
void
JConstHashCursor<V>::ResetKey
	(
	const V& value
	)
{
	itsValue = &value;
	itsHashValue = Hash(*itsValue);
	itsInitialIndex = itsIndex = itsTable->HashToIndex(itsHashValue);
	itsIncrement = DualHash(itsHashValue);
	itsInitialCount = 0;
}

#endif

// Instantiate the template for the specified type.

#if defined JTemplateType && ! defined JOnlyWantTemplateDefinition
	#define JTemplateName JConstHashCursor
	#include <instantiate_template.h>
	#undef JTemplateName
#endif
