/*
	$Id: CTextObjMgr.cpp,v 1.1 2003/04/20 23:31:08 dasenbro Exp $

	File:		CTextObjMgr.cpp

	Contains:	C++ implementation of generic 

	Version:	Apple Mail Server - Mac OS X :  $Revision: 1.1 $

	Written by:	Michael Dasenbrock

	Copyright:	© 1996-2001 by Apple Computer, Inc., all rights reserved.

 	NOT_FOR_OPEN_SOURCE <to be reevaluated at a later time>

	Change History:

		$Log: CTextObjMgr.cpp,v $
		Revision 1.1  2003/04/20 23:31:08  dasenbro
		Initial check-in.
		
		Revision 1.13  2002/07/19 01:58:21  dasenbro
		Prevent objects from adding data and not releasing it.  Also cleaned up
		a DB object leak.
		
		Revision 1.12  2002/07/17 01:38:31  dasenbro
		Removed a text object leak.
		
		Revision 1.11  2002/04/18 18:09:15  dasenbro
		Changed bool to Bool for word alignment.
		
		Revision 1.10  2002/03/26 23:00:14  dasenbro
		Changed new to ::Create and added Done() to remove string.
		
		Revision 1.9  2002/03/21 16:41:42  dasenbro
		Updated file version information.
		
		Revision 1.8  2001/06/21 20:51:09  dasenbro
		Updated file header info.
		
		Revision 1.7  2001/06/21 19:54:42  dasenbro
		Added Change History.
		

	Projector History:
		
	To Do:
*/


// includes
#include "CTextObjMgr.h"
#include "CTextObject.h"
#include "CRootObject.h"
#include "CUtils.h"

CTextObjMgr*	CTextObjMgr::sTextObjMgr	= nil;

//--------------------------------------------------------------------------------------------------
//	* CTextObjMgr ()
//
//--------------------------------------------------------------------------------------------------

CTextObjMgr::CTextObjMgr ( void )
{
	fTextObj	= nil;
} // CTextObjMgr


//--------------------------------------------------------------------------------------------------
//	* ~CTextObjMgr ()
//
//--------------------------------------------------------------------------------------------------

CTextObjMgr::~CTextObjMgr ( void )
{
	if ( fTextObj != nil )
	{
		fTextObj->Done( fTextObj );
	}
} // ~CTextObjMgr


//--------------------------------------------------------------------------------------------------
//	* DeInitialize ()
//
//--------------------------------------------------------------------------------------------------

void CTextObjMgr::DeInitialize ( void )
{
	// Set the done flag
	if ( CTextObjMgr::sTextObjMgr != nil )
	{
		CTextObjMgr::sTextObjMgr->DoCleanUp();

		delete( CTextObjMgr::sTextObjMgr );
		CTextObjMgr::sTextObjMgr = nil;
	}

} // DeInitialize


//--------------------------------------------------------------------------------------------------
//	* AddString ()
//
//--------------------------------------------------------------------------------------------------

sInt32 CTextObjMgr::AddString ( const char *inString, TextStrObj *inStrObj )
{
	sInt32		siResult	= kNoErr;

	if ( CTextObjMgr::sTextObjMgr == nil )
	{
		CTextObjMgr::sTextObjMgr = new CTextObjMgr();
		if ( CTextObjMgr::sTextObjMgr != nil )
		{
			CTextObjMgr::sTextObjMgr->Initialize();
		}
		else
		{
			siResult = kMemFullErr;
		}
	}

	if ( (CTextObjMgr::sTextObjMgr != nil) && (siResult == kNoErr) )
	{
		siResult = CTextObjMgr::sTextObjMgr->AddThisString( inString, inStrObj );
	}

	return( siResult );

} // AddString


//--------------------------------------------------------------------------------------------------
//	* RemoveString ()
//
//--------------------------------------------------------------------------------------------------

sInt32 CTextObjMgr::RemoveString ( TextStrObj *inStrObj )
{
	sInt32		siResult	= kNoErr;

	if ( CTextObjMgr::sTextObjMgr == nil )
	{
		CTextObjMgr::sTextObjMgr = new CTextObjMgr();
		if ( CTextObjMgr::sTextObjMgr != nil )
		{
			siResult = CTextObjMgr::sTextObjMgr->Initialize();
		}
		else
		{
			siResult = kMemFullErr;
		}
	}

	if ( (CTextObjMgr::sTextObjMgr != nil) && (siResult == kNoErr) )
	{
		siResult = CTextObjMgr::sTextObjMgr->RemoveThisString( inStrObj );
	}

	return( siResult );

} // RemoveString


//--------------------------------------------------------------------------------------------------
//	* GetString ()
//
//--------------------------------------------------------------------------------------------------

sInt32 CTextObjMgr::GetString ( TextStrObj *inStrObj, char **outStr )
{
	sInt32		siResult	= kNoErr;

	if ( CTextObjMgr::sTextObjMgr == nil )
	{
		CTextObjMgr::sTextObjMgr = new CTextObjMgr();
		if ( CTextObjMgr::sTextObjMgr != nil )
		{
			siResult = CTextObjMgr::sTextObjMgr->Initialize();
		}
		else
		{
			siResult = kMemFullErr;
		}
	}

	if ( (CTextObjMgr::sTextObjMgr != nil) && (siResult == kNoErr) )
	{
		siResult = CTextObjMgr::sTextObjMgr->GetThisString( inStrObj, outStr );
	}

	return( siResult );

} // GetString


//--------------------------------------------------------------------------------------------------
//	* Initialize ()
//
//--------------------------------------------------------------------------------------------------

sInt32 CTextObjMgr::Initialize ( void )
{
	sInt32		siResult	= kNoErr;
	ObjID		textObjID	= 0;

	// Initialize private non-static data members
	if ( fTextObj != nil )
	{
		fTextObj->Done( fTextObj );
		fTextObj = nil;
	}
	
	// Do we have a database object
	if ( gRootObj != nil )
	{
		// Get the current text object ID from the DB header
		textObjID = gRootObj->GetCurrTextObjID();
		if ( textObjID == 0 )
		{
			// If the ID is 0, create a new text object
			fTextObj = CTextObject::Create();
			if ( fTextObj == nil )
			{
				siResult = kMemFullErr;
			}
		}
		else
		{
			fTextObj = CTextObject::FindByID( textObjID );
			if ( fTextObj == nil )
			{
				// This is not good.  Quick, make a new one.
				fTextObj = CTextObject::Create();
				if ( fTextObj == nil )
				{
					siResult = kMemFullErr;
				}
			}
		}

		// Do we have a good text object
		if ( fTextObj != nil )
		{
			// Just set it everytime
			gRootObj->SetCurrTextObjID( fTextObj->GetMyObjectID() );
		}
	}
	else
	{
		siResult = kFnfErr;
	}

	return( siResult );

} // Initialize


//--------------------------------------------------------------------------------------------------
//	* AddThisString ()
//
//--------------------------------------------------------------------------------------------------

sInt32 CTextObjMgr::AddThisString ( const char *inString, TextStrObj *outStrObj )
{
	sInt32			siResult	= kNoErr;
	uInt32			uiStrLen	= 0;
	uInt16			usOffset	= 0;
	uInt16			usCntr		= 0;
	ObjID			prevObjID	= 0;
	ObjID			currObjID	= 0;
	CTextObject	   *pCurrObj	= nil;
	CTextObject	   *pPrevObj	= nil;
	CTextObject	   *pNextObj	= nil;
	Bool			done		= false;
	Bool			added		= false;

	siResult = fTextObj->AddString( inString, &usOffset );

	if ( siResult == kNoErr ) 
	{
		outStrObj->fObID	= fTextObj->GetMyObjectID();
		outStrObj->fOffset	= usOffset;
	}
	else if ( siResult == CTextObject::kObjectFull )
	{
		// This object if full, Get a new on and save the string there
		pCurrObj = CTextObject::Create();
		if ( pCurrObj != nil )
		{
			// Get the link to the next object and set it to 0.  This one is full and is being
			//	removed from the list.
			currObjID = fTextObj->GetNextObjectID();
			fTextObj->SetNextObjectID( 0 );

			fTextObj->Done( fTextObj );

			fTextObj = pCurrObj;
			pCurrObj = nil;

			// Add the link to the next object
			fTextObj->SetNextObjectID( currObjID );

			// Update the database header
			gRootObj->SetCurrTextObjID( fTextObj->GetMyObjectID() );

			// Add the string to the new object and link it as head of the list
			siResult = fTextObj->AddString( inString, &usOffset );
			if ( siResult == kNoErr )
			{
				// Set the return data
				outStrObj->fObID	= fTextObj->GetMyObjectID();
				outStrObj->fOffset	= usOffset;
			}
		}
		else
		{
			siResult = kMemFullErr;
		}
	}
	else if ( siResult == CTextObject::kStringTooBig )
	{
		pCurrObj = fTextObj;
		currObjID = fTextObj->GetMyObjectID();
		prevObjID = currObjID;

		// Lets try to fit it into an existing object
		while ( pCurrObj != nil )
		{
			usCntr++;

			siResult = pCurrObj->AddString( inString, &usOffset );
			if ( siResult == kNoErr )
			{
				// We achieved our objective so we can mark the object and bail
				outStrObj->fObID	= currObjID;
				outStrObj->fOffset	= usOffset;

				added = true;

				// Don't done the head object
				if ( pCurrObj != fTextObj )
				{
					pCurrObj->Done( pCurrObj );
				}
			}
			else if ( siResult == CTextObject::kObjectFull )
			{
				// Take this object off the list
				usCntr--;

				// Is the current object the first one on the list
				if ( currObjID == fTextObj->GetMyObjectID() )
				{
					// Is this the only object on the list
					if ( pCurrObj->GetNextObjectID() == 0 )
					{
						// Can't remove the first and only object
						//	we'll remove it next time around
						pCurrObj = nil;
					}
					else
					{
						// Get the next object on the list
						pNextObj = CTextObject::FindByID( pCurrObj->GetNextObjectID() );
						if ( pNextObj != nil )
						{
							// Release the head object and set the new one
							fTextObj->SetNextObjectID( 0 );
							fTextObj->Done( fTextObj );
							fTextObj = pNextObj;

							// Release the current object
							pCurrObj->SetNextObjectID( 0 );
							pCurrObj->Done( pCurrObj );

							// Set the next object to the current one
							pCurrObj = pNextObj;
							pNextObj = nil;
						}
						else
						{
							// This is not good
							//	set the next ID to 0 and bail
							pCurrObj->SetNextObjectID( 0 );
							pCurrObj = nil;
						}
					}
				}
				else
				{
					// Is the previous head object the current head object
					if ( prevObjID == fTextObj->GetMyObjectID() )
					{
						pPrevObj = fTextObj;

						// Set the previous object's next object id 
						pPrevObj->SetNextObjectID( pCurrObj->GetNextObjectID() );
						pPrevObj = nil;

						// Get the next object and release the current object
						currObjID = pCurrObj->GetNextObjectID();
						pCurrObj->SetNextObjectID( 0 );
						pCurrObj->Done( pCurrObj );

						pCurrObj = CTextObject::FindByID( currObjID );
					}
					else
					{
						// Get the previous object
						pPrevObj = CTextObject::FindByID( prevObjID );
						if ( pPrevObj != nil )
						{
							// Set the previous object's next object id 
							pPrevObj->SetNextObjectID( pCurrObj->GetNextObjectID() );
							pPrevObj->Done( pPrevObj );

							// Get the next object and release the current object
							currObjID = pCurrObj->GetNextObjectID();
							pCurrObj->SetNextObjectID( 0 );
							pCurrObj->Done( pCurrObj );

							pCurrObj = CTextObject::FindByID( currObjID );
						}
						else
						{
							// This is not good
							//	bail
							if ( pCurrObj != fTextObj )
							{
								pCurrObj->Done( pCurrObj );
							}
							pCurrObj = nil;
						}
					}
				}
			}
			else
			{
				// Only need 16 items on the list
				if ( usCntr > 16 )
				{
					pCurrObj->SetNextObjectID( 0 );
				}

				// Set the previous object id
				prevObjID = pCurrObj->GetMyObjectID();

				// Get the next object id
				currObjID = pCurrObj->GetNextObjectID();

				// Get the next object but don't done the head object
				if ( pCurrObj != fTextObj )
				{
					pCurrObj->Done( pCurrObj );
				}
		
				// Get the next object
				pCurrObj = CTextObject::FindByID( currObjID );
			}
		}

		if ( !added )
		{
			// Make sure it will fit before we allocate another object
			uiStrLen = CUtils::Strlen( inString );
			if ( (uiStrLen > 0) && (uiStrLen < kTextObjSize) )
			{
				// Create a new current text object
				pCurrObj = CTextObject::Create();
				if ( pCurrObj != nil )
				{
					// Link it to the previous current text object
					pCurrObj->SetNextObjectID( fTextObj->GetMyObjectID() );
	
					// Done the current one
					fTextObj->Done( fTextObj );
	
					// Set the data member to the new current text object
					fTextObj = pCurrObj;
					pCurrObj = nil;
	
					// Add the string to the new object and link it as head of the list
					siResult = fTextObj->AddString( inString, &usOffset );
					if ( siResult == kNoErr )
					{
						outStrObj->fObID	= fTextObj->GetMyObjectID();
						outStrObj->fOffset	= usOffset;
					}
				}
				else
				{
					siResult = kMemFullErr;
				}
			}
			else
			{
				siResult = CTextObject::kStringTooBig;
			}
		}
	}
	else
	{
	}

	return( siResult );

} // AddThisString


//--------------------------------------------------------------------------------------------------
//	* RemoveThisString ()
//
//--------------------------------------------------------------------------------------------------

sInt32 CTextObjMgr::RemoveThisString ( TextStrObj *inStrObj )
{
	sInt32			siResult	= kNoErr;
	uInt16			usOffset	= 0;
	CTextObject	   *pTextObj	= nil;

	pTextObj = CTextObject::FindByID( inStrObj->fObID );
	if ( pTextObj != nil )
	{
		siResult = pTextObj->RemoveString( inStrObj->fOffset );
		if ( pTextObj->GetItemCount() == 0 )
		{
			if ( this->AddToFreeList( pTextObj ) == true )
			{
				pTextObj->Done( pTextObj );
			}
		}
		else
		{
			pTextObj->Done( pTextObj );
		}
	}
	else
	{
		siResult = CBaseDatabase::kDBObjNotFound;
	}

	return( siResult );

} // RemoveThisString


//--------------------------------------------------------------------------------------------------
//	* GetThisString ()
//
//--------------------------------------------------------------------------------------------------

sInt32 CTextObjMgr::GetThisString ( TextStrObj *inStrObj, char **outStr )
{
	sInt32			siResult	= kNoErr;
	CTextObject	   *pTextObj	= nil;

	pTextObj = CTextObject::FindByID( inStrObj->fObID );
	if ( pTextObj != nil )
	{
		if ( pTextObj->GetItemCount() != 0 )
		{
			siResult = pTextObj->GetString( inStrObj->fOffset, outStr );
		}
		else
		{
			siResult = CTextObject::kEmptyStrObject;
		}
		pTextObj->Done( pTextObj );
	}
	else
	{
		siResult = CBaseDatabase::kDBObjNotFound;
	}

	return( siResult );

} // GetThisString


//--------------------------------------------------------------------------------------------------
//	* AddToFreeList ()
//
//--------------------------------------------------------------------------------------------------

bool CTextObjMgr::AddToFreeList ( CTextObject *inTextObj )
{
	bool			bResult		= false;
	ObjID			textObjID	= 0;
	CTextObject	   *pTextObj	= nil;

	if ( inTextObj != nil )
	{
		inTextObj->InitObject();

		if ( fTextObj != nil )
		{
			// Is it already on the list?
			textObjID = fTextObj->GetMyObjectID();
			while ( textObjID != 0 )
			{
				if ( textObjID == inTextObj->GetObjectID() )
				{
					// We found it, we are done
					textObjID = 0;
					bResult = true;
				}
				else
				{
					pTextObj = CTextObject::FindByID( textObjID );
					if ( pTextObj != nil )
					{
						textObjID = pTextObj->GetNextObjectID();
						pTextObj->Done( pTextObj );
					}
					else
					{
						textObjID = 0;
					}
				}
			}

			if ( !bResult )
			{
				inTextObj->SetNextObjectID( fTextObj->GetMyObjectID() );

				// Done the current one
				fTextObj->Done( fTextObj );

				// Set the data member to the new current text object
				fTextObj = inTextObj;
			}
		}
	}

	return( bResult );

} // AddToFreeList


//--------------------------------------------------------------------------------------------------
//	* DoCleanUp ()
//
//--------------------------------------------------------------------------------------------------

void CTextObjMgr::DoCleanUp ( void )
{
	ObjID			textObjID	= 0;
	CTextObject	   *pTextObj	= nil;

	if ( fTextObj != nil )
	{
		textObjID = fTextObj->GetMyObjectID();
		while ( textObjID != 0 )
		{
			pTextObj = CTextObject::FindByID( textObjID );
			if ( pTextObj != nil )
			{
				textObjID = pTextObj->GetNextObjectID();
				if ( pTextObj->GetItemCount() == 0 )
				{
					CTextObject::Delete( pTextObj );
				}
				pTextObj->Done( pTextObj );
			}
			else
			{
				textObjID = 0;
			}
		}
		fTextObj->Done( fTextObj );
	}
} // DoCleanUp
