/***********************************************************************/
/*                                                                     */
/*                      ADOBE CONFIDENTIAL                             */
/*                   _ _ _ _ _ _ _ _ _ _ _ _ _                         */
/*                                                                     */
/*  Copyright 2016 Adobe Systems Incorporated                          */
/*  All Rights Reserved.                                               */
/*                                                                     */
/* NOTICE:  All information contained herein is, and remains           */
/* the property of Adobe Systems Incorporated and its suppliers,       */
/* if any.  The intellectual and technical concepts contained          */
/* herein are proprietary to Adobe Systems Incorporated and its        */
/* suppliers and are protected by all applicable intellectual property */
/* laws, including trade secret and copyright laws.                    */
/* Dissemination of this information or reproduction of this material  */
/* is strictly forbidden unless prior written permission is obtained   */
/* from Adobe Systems Incorporated.                                    */
/*                                                                     */
/***********************************************************************/

function CompoundJob(/*[Function]*/ inResultFilter, /*[Function]*/ inErrorFilter)
{
    var jobs = [];
	var attached = [];
    var count = 0;
	var status = IJob.STATUS_PENDING;
	var waiting = false;
	var partialResults = false;
	var successHandler = null;
	var errorHandler = null;
    var resultFilter = inResultFilter;
    var errorFilter = inErrorFilter;
    
    var resultCache = null;
    var errorCache = null;
    var statusCache = null;

    //////////////////////////////////////////////////////////////////////////////
    //
    // Add Job
    //
    this.addJob = function(/*[IJob]*/ inJob)
    {
        throwInvalid(inJob);
        jobs.push(inJob);

		if (waiting)
		{
			inJob.wait(resultHandler, errorHandler);
		}
    }

	//////////////////////////////////////////////////////////////////////////////
	//
	// Remove Job
	//
	this.removeJob = function(/*[IJob]*/ inJob)
	{
		throwInvalid(inJob);

		for (var j=0; j<jobs.length; j++)
		{
			if (jobs[j] == inJob)
			{
				jobs.splice(j, 1);
				break;
			}
		}
	}

	//////////////////////////////////////////////////////////////////////////////
    //
    // Get Job result
    //
    this.getResult = function()
    {
		if (!isValidProperty(resultCache))
		{
			var result = [];
			
			for (var j=0; j<jobs.length; j++)
			{
				if (jobs[j].isStatus(IJob.STATUS_FINISHED | IJob.STATUS_SUCCESS))
				{
					var jobResult = jobs[j].getResult();

					if (jobs[j] instanceof CompoundJob)
					{
						// if the job is a CompoundJob then concat the results
						result = result.concat(jobResult);
					}
					else
					{
						result.push(jobResult);
					}
				}
			}

			resultCache = (isValidProperty(resultFilter) ? resultFilter(result) : result);
		}
		
		return resultCache;
    }

    //////////////////////////////////////////////////////////////////////////////
    //
    // Get Job error
    //
    this.getError = function()
    {
		if (!isValidProperty(errorCache))
		{
			var result = [];

			for (var j=0; j<jobs.length; j++)
			{
				if (jobs[j].isStatus(IJob.STATUS_FINISHED | IJob.STATUS_ERROR))
				{
					var jobError = jobs[j].getError();

					if (jobs[j] instanceof CompoundJob)
					{
						// if the job is a CompoundJob then concat the errors
						result = result.concat(jobError);
					}
					else
					{
						result.push(jobError);
					}
				}
			}

			errorCache = (isValidProperty(errorFilter) ? errorFilter(result) : result);
		}
		
		return errorCache;
    }

    //////////////////////////////////////////////////////////////////////////////
    //
    // Wait for Job completed
    //
	function resultHandler(/*[any]*/ inResult)
	{
		count++;
		
		resultCache = null;
		statusCache = null;

		if ((partialResults || (!partialResults && count >= jobs.length)) &&
			isValidProperty(successHandler))
		{
			successHandler(inResult);
		}
	}

	function failureHandler(/*[any]*/ inError)
	{
		count++;

		errorCache = null;
		statusCache = null;

		if ((partialResults || (!partialResults && count >= jobs.length)) &&
			isValidProperty(errorHandler))
		{
			errorHandler(inError);
		}
	}

	this.wait = function(/*[Function]*/ inSuccessFct, /*[Function]*/ inErrorFct, /*[Boolean]*/ inPartialResults)
    {
    	if (!waiting && !this.isStatus(IJob.STATUS_CANCELED))
		{
			waiting = true;
			partialResults = isValidProperty(inPartialResults) ? inPartialResults : false;

			successHandler = inSuccessFct;
			errorHandler = inErrorFct;

			var thisObj = this;

			forEach(jobs, function (job)
			{
				job.wait(function()
				{
					resultHandler(thisObj.getResult());
				},
				function()
				{
					failureHandler(thisObj.getError());
				});
			});
		}
    }

	//////////////////////////////////////////////////////////////////////////////
    //
    // Cancel Job
    //
    this.cancel = function()
    {
		successHandler = null;
        errorHandler = null;

		forEach(attached, function(attachment)
		{
			attachment.cancel();
		});

		attached = [];

		forEach(jobs, function(job)
        {
            job.cancel();
        });

		jobs = [];
		status = IJob.STATUS_CANCELED | IJob.STATUS_FINISHED;
    }

    //////////////////////////////////////////////////////////////////////////////
    //
    // Get Job status
    //
    this.getStatus = function()
    {
    	if (!isValidProperty(statusCache))
    	{
			var result = (jobs.length > 0 ? 0 : status);

			var finishCount = 0;

			forEach(jobs, function(job)
			{
				var jobStatus = job.getStatus();

				if (jobStatus & IJob.STATUS_FINISHED)
				{
					finishCount++;
					jobStatus &= ~(IJob.STATUS_FINISHED);
				}

				if (result != 0 && result != jobStatus)
				{
					result |= IJob.STATUS_MULTIPLE;
				}

				result |= jobStatus;
			});

			if (finishCount == jobs.length)
			{
				result |= IJob.STATUS_FINISHED;
			}

			statusCache = result;
		}
		
		return statusCache;
    }

    //////////////////////////////////////////////////////////////////////////////
    //
    // Check Job status
    //
    this.isStatus = function(/*[Number]*/ inStatusFlags)
    {
        return ((this.getStatus() & inStatusFlags) == inStatusFlags);
    }
}
