///////////////////////////////////////////////////////////////////////////////
// CreateChildArray
///////////////////////////////////////////////////////////////////////////////
void cHierDatabaseIter::CreateChildArray() //throw (eArchive, eHierDatabase)
{
    ASSERT( ! Done() );
    ASSERT( ! CanDescend() );
    if( Done() )
    {
        throw eHierDatabase( _T("Attempt to call iter::CreateChildArray() when done is true"));
    }
    if( CanDescend() )
    {
        throw eHierDatabase( _T("Attempt to call iter::CreateChildArray() when child already exists"));
    }
        
    cHierArrayInfo  newInfo;
    cHierAddr       infoAddr;

    // write the new info
    newInfo.mParent = mInfoAddr;
    infoAddr        = util_WriteObject( mpDb, &newInfo );
    mIter->mChild   = infoAddr;
    //
    // rewrite the current object, since its child info just changed
    //
    util_RewriteObject( mpDb, &(*mIter), GetCurrentAddr() );
}
///////////////////////////////////////////////////////////////////////////////
// AtRoot
///////////////////////////////////////////////////////////////////////////////
bool cHierDatabaseIter::AtRoot() const //throw (eHierDatabase)
{
    bool bResult = ( mInfoAddr == mpDb->mRootArrayAddr );

    // TODO -- maybe I want to put this in a #ifdef?
    //
    if( bResult )
    {
        // we are at the root; assert that we have no parent...
        //
        ASSERT( mInfo.mParent.IsNull() );
        if( ! mInfo.mParent.IsNull() )
        {
            throw eHierDatabase( _T("Root node of db has a non-null parent") );
        }
    }
    else
    {
        // if we are not at the root, assert that we have a parent...
        ASSERT( ! mInfo.mParent.IsNull() );
        if( mInfo.mParent.IsNull() )
        {
            throw eHierDatabase( _T("Non-root node of db has a null parent!") );
        }
    }

    return bResult;
} 
///////////////////////////////////////////////////////////////////////////////
// SetFCOData
///////////////////////////////////////////////////////////////////////////////
void cDbDataSourceIter::SetFCOData( const iFCO* pFCO ) //throw (eError)
{
	ASSERT( ! Done() );
	if( Done() )
	{
		throw eHierDatabase( _T("Attempt to set FCO data when the iterator is done.") );
	}
	// TODO -- assert and throw if the fco's type is not the same as our creation function
	//		There is no way to do this through the serializable interface, but when there is,
	//		we should do the above assertion.
	//
	// if data already exists here, we first remove it; 
	//
	if( mDbIter.HasData() )
	{
		mDbIter.RemoveData();
	}
	//
	// write the fco's property set to a memory archive...
	//
	// TODO -- does this need to be static?
	static cMemoryArchive arch;
	arch.Seek			( 0, cBidirArchive::BEGINNING );
	cSerializerImpl ser	(arch, cSerializerImpl::S_WRITE);
	ser.Init();
	ser.WriteObject		( pFCO->GetPropSet() );
	ser.Finit();
	//
	// write this to the archive...
	//
	mDbIter.SetData( arch.GetMemory(), arch.CurrentPos() );
}
///////////////////////////////////////////////////////////////////////////////
// DeleteChildArray
///////////////////////////////////////////////////////////////////////////////
void cHierDatabaseIter::DeleteChildArray() //throw (eArchive, eHierDatabase)
{
    ASSERT( ! Done() );
    ASSERT( CanDescend() );
    cDebug d("cHierDatabaseIter::DeleteChildArray");

    if( Done() )
    {
        d.TraceDetail("Attempt to DeleteChildArray() when Done() == true; returning\n");
        return;
    }
    if( ! CanDescend() )
    {
        d.TraceDetail("Attempt to DeleteChildArray() when none exists; returning\n");
        return;
    }
    //
    // make sure that the child index is empty...
    //
    if( ! ChildArrayEmpty() )
    {
        ASSERT( false );
        throw eHierDatabase( _T("Attempt to delete a child array that still has entries") );
    }
    //
    // ok, no we can remove it...
    //
    mpDb->RemoveItem( cBlockRecordFile::tAddr( mIter->mChild.mBlockNum, mIter->mChild.mIndex ) );
    mIter->mChild = cHierAddr();
    util_RewriteObject( mpDb, &(*mIter), GetCurrentAddr() );
}
///////////////////////////////////////////////////////////////////////////////
// GetName
///////////////////////////////////////////////////////////////////////////////
const TCHAR* cHierDatabaseIter::GetName() const
{
    ASSERT( ! Done() );
    if( Done() )
    {
        throw eHierDatabase( _T("Attempt to call iter::GetName() when done is true"));
    }

    return mIter->mName.c_str();
}
static inline void util_ThrowIfNull( const cHierAddr& addr, const TSTRING& context )
{
    ASSERT( ! addr.IsNull() );
    if( addr.IsNull() )
    {
        TSTRING msg(_T("Attempt to access null address"));
        if( ! context.empty() )
        {
            msg += _T(" in ") + context;
        }
        throw eHierDatabase( msg );
    }   
}
///////////////////////////////////////////////////////////////////////////////
// GetData
///////////////////////////////////////////////////////////////////////////////
int8* cHierDatabaseIter::GetData(int32& length) const //throw (eArchive, eHierDatabase)
{
    ASSERT( HasData() );
    if( ! HasData() )
    {
        throw eHierDatabase( _T("Attempt to get data from a node when HasData() is false"));
    }

    //
    // note that we can only get data for reading; perhaps in the future I will add
    // support for retrieving data for writing as well.
    //
    return mpDb->GetDataForReading( cBlockRecordFile::tAddr(mIter->mData.mBlockNum, mIter->mData.mIndex), length );

}
///////////////////////////////////////////////////////////////////////////////
// DeleteEntry
///////////////////////////////////////////////////////////////////////////////
void cHierDatabaseIter::DeleteEntry() //throw (eArchive, eHierDatabase)
{
    ASSERT( ! Done() );
    ASSERT( ! CanDescend() ); // this node can't have any children.
    cDebug d("cHierDatabaseIter::DeleteEntry");

    if( Done() )
    {
        d.TraceDetail("Attempt to DeleteEntry() when Done() == true; returning\n");
        return;
    }
    if( CanDescend() )
    {
        throw eHierDatabase( _T("Attempt to delete an entry that still has children.\n"));
    }
    
    //
    // first, we should set the previous node's next pointer...
    //
    cHierAddr curAddr = GetCurrentAddr();
    if( mIter == mEntries.begin() )
    {
        // we are changing the info's mArray pointer
        //
        mInfo.mArray = mIter->mNext;
        util_RewriteObject( mpDb, &mInfo, mInfoAddr );
    }
    else
    {
        // altering the previous node...
        //
        mIter--;
        mIter->mNext = (mIter+1)->mNext;
        util_RewriteObject( mpDb, &(*mIter), GetCurrentAddr() );
        mIter++;
    }
    //
    // now, delete the node from the file and from our array...
    //
    mpDb->RemoveItem( cBlockRecordFile::tAddr( curAddr.mBlockNum, curAddr.mIndex ) );
    mIter = mEntries.erase(mIter);

}