// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
// Copyright 2012 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE:  Adobe permits you to use, modify, and distribute this file in accordance with the terms
// of the Adobe license agreement accompanying it.
// =================================================================================================

#include "public/include/XMP_Environment.h"	// ! XMP_Environment.h must be the first included header.

#include "public/include/XMP_Const.h"

#include "source/XMP_LibUtils.hpp"
#include "source/XMP_ProgressTracker.hpp"

// =================================================================================================
// XMP_ProgressTracker::XMP_ProgressTracker
// ========================================

XMP_ProgressTracker::XMP_ProgressTracker ( const CallbackInfo & _cbInfo )
{

	this->Clear();
	if ( _cbInfo.clientProc == 0 ) return;
	XMP_Assert ( _cbInfo.wrapperProc != 0 );

	this->cbInfo = _cbInfo;
	if ( this->cbInfo.interval < 0.0 ) this->cbInfo.interval = 1.0;

}	// XMP_ProgressTracker::XMP_ProgressTracker

// =================================================================================================
// XMP_ProgressTracker::BeginWork
// ==============================

void XMP_ProgressTracker::BeginWork ( float _totalWork )
{

	if ( _totalWork < 0.0 ) _totalWork = 0.0;
	this->totalWork = _totalWork;
	this->workDone = 0.0;
	this->workInProgress = true;

	this->startTime = this->prevTime = PerfUtils::NoteThisMoment();
	if ( this->cbInfo.sendStartStop ) this->NotifyClient ( true );

}	// XMP_ProgressTracker::BeginWork

// =================================================================================================
// XMP_ProgressTracker::AddTotalWork
// =================================

void XMP_ProgressTracker::AddTotalWork ( float workIncrement )
{

	if ( workIncrement < 0.0 ) workIncrement = 0.0;
	this->totalWork += workIncrement;

}	// XMP_ProgressTracker::AddTotalWork

// =================================================================================================
// XMP_ProgressTracker::AddWorkDone
// ================================

void XMP_ProgressTracker::AddWorkDone ( float workIncrement )
{

	if ( workIncrement < 0.0 ) workIncrement = 0.0;
	this->workDone += workIncrement;
	this->NotifyClient();

}	// XMP_ProgressTracker::AddWorkDone

// =================================================================================================
// XMP_ProgressTracker::WorkComplete
// =================================

void XMP_ProgressTracker::WorkComplete ()
{

	if ( this->totalWork == 0.0 ) this->totalWork = 1.0;	// Force non-zero fraction done.
	this->workDone = this->totalWork;
	XMP_Assert ( this->workDone > 0.0 );	// Needed in NotifyClient for start/stop case.

	this->NotifyClient ( this->cbInfo.sendStartStop );
	this->workInProgress = false;

}	// XMP_ProgressTracker::WorkComplete

// =================================================================================================
// XMP_ProgressTracker::Clear
// ==========================

void XMP_ProgressTracker::Clear ()
{

	this->cbInfo.Clear();
	this->workInProgress = false;
	this->totalWork = 0.0;
	this->workDone = 0.0;
	this->startTime = this->prevTime = PerfUtils::kZeroMoment;
	// ! There is no standard zero value for PerfUtils::MomentValue.

}	// XMP_ProgressTracker::Clear

// =================================================================================================
// XMP_ProgressTracker::NotifyClient
// =================================
//
// The math for the time remaining is easy but not immediately obvious. We know the elapsed time and
// fraction of work done:
//
//		elapsedTime = totalTime * fractionDone		or		totalTime = (elapsedTime / fractionDone)
//		remainingTime = totalTime * (1 - fractionDone)
// so:
//		remainingTime = (elapsedTime / fractionDone) * (1 - fractionDone)

void XMP_ProgressTracker::NotifyClient ( bool isStartStop )
{
	XMP_Bool ok = !kXMP_Bool_False;
	float fractionDone = 0.0;

	if ( this->cbInfo.clientProc == 0 ) return;
	XMP_Assert ( this->cbInfo.wrapperProc != 0 );
	XMP_Assert ( (this->totalWork >= 0.0) && (this->workDone >= 0.0) && (this->cbInfo.interval >= 0.0) );
	// ! Note that totalWork might be unknown or understimated, and workDone greater than totalWork.

	if ( isStartStop ) {

		float totalTime = 0.0;
		if ( this->workDone > 0.0 ) {
			fractionDone = 1.0;	// This is the stop call.
			totalTime = PerfUtils::GetElapsedSeconds ( this->startTime, PerfUtils::NoteThisMoment() );
		}
		ok = (*this->cbInfo.wrapperProc ) ( this->cbInfo.clientProc, this->cbInfo.context,
											totalTime, fractionDone, 0.0 );

	} else {

		PerfUtils::MomentValue currentTime = PerfUtils::NoteThisMoment();
		float elapsedTime = PerfUtils::GetElapsedSeconds ( this->prevTime, currentTime );
		if ( elapsedTime < this->cbInfo.interval ) return;

		float remainingTime = 0.0;
		if ( (this->totalWork > 0.0) && (this->workDone > 0.0) ) {
			fractionDone = this->workDone / this->totalWork;
			if ( fractionDone > 1.0 ) fractionDone = 1.0;
			elapsedTime = PerfUtils::GetElapsedSeconds ( this->startTime, currentTime );
			remainingTime = (elapsedTime / fractionDone) * (1.0 - fractionDone);
		}

		this->prevTime = currentTime;
		ok = (*this->cbInfo.wrapperProc ) ( this->cbInfo.clientProc, this->cbInfo.context,
											elapsedTime, fractionDone, remainingTime );

	}

	if ( ok == kXMP_Bool_False ) XMP_Throw ( "Abort signaled by progress reporting callback", kXMPErr_ProgressAbort );

}	// XMP_ProgressTracker::NotifyClient

// =================================================================================================
