/******************************************************************************
 JLatentPG.cc

	This class encapsulates a progress display that only pops up after
	N seconds (default 3) and only redraws itself every M increments
	(default 1).

	This allows one to write a loop that doesn't waste time creating
	a progress display unless it takes a while and doesn't waste time
	redrawing every time through the loop.

	The scale factor is especially useful when one has a loop whose contents
	execute very quickly on each pass, so that redrawing the screen after every
	pass adds a huge overhead.

	BASE CLASS = JProgressDisplay

	Copyright © 1998 by John Lindal. All rights reserved.

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

#include <JLatentPG.h>
#include <JString.h>
#include <jGlobals.h>
#include <jAssert.h>

const time_t kDefMaxSilentTime = 3;		// seconds

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

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

JLatentPG::JLatentPG
	(
	const JSize scaleFactor
	)
{
	itsPG         = JNewPG();
	itsOwnsPGFlag = kTrue;

	itsMessage = new JString;
	assert( itsMessage != NULL );

	itsMaxSilentTime = kDefMaxSilentTime;
	itsScaleFactor   = scaleFactor;
}

JLatentPG::JLatentPG
	(
	JProgressDisplay*	pg,
	const JBoolean		ownIt,
	const JSize			scaleFactor
	)
{
	itsPG         = pg;
	itsOwnsPGFlag = ownIt;

	itsMessage = new JString;
	assert( itsMessage != NULL );

	itsMaxSilentTime = kDefMaxSilentTime;
	itsScaleFactor   = scaleFactor;
}

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

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

JLatentPG::~JLatentPG()
{
	if (itsOwnsPGFlag)
		{
		delete itsPG;
		}

	delete itsMessage;
}

/******************************************************************************
 UseDefaultPG

	Use a progress display obtained from JNewPG().

	This can only be called when a process is not running.

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

void
JLatentPG::UseDefaultPG()
{
	SetPG(JNewPG(), kTrue);
}

/******************************************************************************
 SetPG

	If ownIt == kTrue, we delete the progress display when we no longer
	need it.

	This can only be called when a process is not running.

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

void
JLatentPG::SetPG
	(
	JProgressDisplay*	pg,
	const JBoolean		ownIt
	)
{
	assert( !ProcessRunning() );
	assert( pg != NULL && !pg->ProcessRunning() );

	if (itsOwnsPGFlag)
		{
		delete itsPG;
		}

	itsPG         = pg;
	itsOwnsPGFlag = ownIt;
}

/******************************************************************************
 ProcessBeginning (virtual protected)

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

void
JLatentPG::ProcessBeginning
	(
	const ProcessType	processType,
	const JSize			stepCount,
	const JCharacter*	message,
	const JBoolean		allowCancel,
	const JBoolean		allowBackground
	)
{
	JProgressDisplay::ProcessBeginning(processType, stepCount, message,
									   allowCancel, allowBackground);

	*itsMessage  = message;
	itsStartTime = time(NULL);
	itsCounter   = 0;

	if (itsMaxSilentTime == 0)
		{
		StartInternalProcess();
		}
}

/******************************************************************************
 StartInternalProcess (private)

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

void
JLatentPG::StartInternalProcess()
{
	const ProcessType type = GetCurrentProcessType();
	if (type == kFixedLengthProcess)
		{
		itsPG->FixedLengthProcessBeginning(
			GetMaxStepCount(), *itsMessage,
			AllowCancel(), AllowBackground());
		}
	else
		{
		assert( type == kVariableLengthProcess );
		itsPG->VariableLengthProcessBeginning(
			*itsMessage, AllowCancel(), AllowBackground());
		}
}

/******************************************************************************
 IncrementProgress (virtual)

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

JBoolean
JLatentPG::IncrementProgress
	(
	const JCharacter* message
	)
{
	assert( ProcessRunning() );

	IncrementStepCount();

	const JBoolean pgRunning = itsPG->ProcessRunning();
	JBoolean result          = kTrue;

	itsCounter++;
	if (!pgRunning && (TimeToStart() || message != NULL))
		{
		StartInternalProcess();

		// delta must be calculated -after- ProcessBeginning()

		const JSize delta = GetCurrentStepCount() - itsPG->GetCurrentStepCount();
		if (delta > 1)
			{
			itsPG->IncrementProgress(delta - 1);
			}
		result     = itsPG->IncrementProgress(message);
		itsCounter = 0;
		}
	else if (pgRunning && message != NULL)
		{
		const JSize delta = GetCurrentStepCount() - itsPG->GetCurrentStepCount();
		if (delta > 1)
			{
			itsPG->IncrementProgress(delta - 1);
			}
		result     = itsPG->IncrementProgress(message);
		itsCounter = 0;
		}
	else if (TimeToUpdate() && pgRunning)
		{
		result = itsPG->IncrementProgress(GetCurrentStepCount() -
										  itsPG->GetCurrentStepCount());
		itsCounter = 0;
		}

	return result;
}

/******************************************************************************
 IncrementProgress (virtual)

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

JBoolean
JLatentPG::IncrementProgress
	(
	const JSize delta
	)
{
	assert( ProcessRunning() );

	IncrementStepCount(delta);

	const JBoolean pgRunning = itsPG->ProcessRunning();
	JBoolean result          = kTrue;

	itsCounter++;
	if (!pgRunning && TimeToStart())
		{
		StartInternalProcess();
		result = itsPG->IncrementProgress(GetCurrentStepCount() -
										  itsPG->GetCurrentStepCount());
		itsCounter = 0;
		}
	else if (TimeToUpdate() && pgRunning)
		{
		result = itsPG->IncrementProgress(GetCurrentStepCount() -
										  itsPG->GetCurrentStepCount());
		itsCounter = 0;
		}

	return result;
}

/******************************************************************************
 ProcessContinuing (virtual)

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

JBoolean
JLatentPG::ProcessContinuing()
{
	if (itsPG->ProcessRunning())
		{
		return itsPG->ProcessContinuing();
		}
	else
		{
		return kTrue;
		}
}

/******************************************************************************
 CheckForCancel (virtual protected)

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

JBoolean
JLatentPG::CheckForCancel()
{
	return kFalse;
}

/******************************************************************************
 ProcessFinished (virtual)

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

void
JLatentPG::ProcessFinished()
{
	JProgressDisplay::ProcessFinished();

	if (itsPG->ProcessRunning())
		{
		itsPG->ProcessFinished();
		}
}

/******************************************************************************
 DisplayBusyCursor (virtual)

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

void
JLatentPG::DisplayBusyCursor()
{
	itsPG->DisplayBusyCursor();
}
