/***********************************************************************/
/*                                                                     */
/*                      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.                                    */
/*                                                                     */
/***********************************************************************/

var gWorkflowTriggerALL = null;

function Workflow(/*[string]*/ inID)
{
    this.id = inID;
    this.name = inID;
    this.content = new WorkflowContent();
    this.steps = [];
    this.stepswitch = 'automatic';
	this.session = '';	// uuid for each workflow execution
    
	//////////////////////////////////////////////////////////////////////////////
	//
	// Initialize with JSON
	//
    this.initializeJSON = function(/*[Object]*/ inJSON)
    {
	    throwInvalid(inJSON);

		this.id = inJSON['id'];
		throwInvalid(this.id);

		var name = inJSON['name'];
		if (isValidProperty(name))
		{
			this.name = name;
		}

		this.stepswitch = inJSON['stepswitch'];		
		if (isValidProperty(this.stepswitch))
		{
			if (this.stepswitch != Workflow.STEPSWITCH_AUTOMATIC &&
				this.stepswitch != Workflow.STEPSWITCH_MANUALLY)
			{
				this.stepswitch = Workflow.STEPSWITCH_AUTOMATIC;
			}
		}
		else
		{
			this.stepswitch = Workflow.STEPSWITCH_AUTOMATIC;
		}

		// add content to the workflow
		if (inJSON.hasOwnProperty('content'))
		{
			this.content.initializeJSON(inJSON['content']);
		}
		else
		{
			throw "Missing Workflow Content";
		}

		// add steps
		var stepsArray = inJSON['steps'];
		throwInvalid(stepsArray, 'Array');
		
		// make this.steps visible to forEach-callback function
		var steps = this.steps;
		var thisObj = this;
		
		forEach(stepsArray, function(stepObj)
		{
			var step = new WorkflowStep(thisObj);
			step.initializeJSON(stepObj);
			steps.push(step);
		});
    }
     
	//////////////////////////////////////////////////////////////////////////////
	//
	// Get content for locator
	//
    this.getContent = function(/*[string]*/ inLocator)
    {
	    throwInvalid(this.id);
    	return this.content.getContent(inLocator);
    }
     
	//////////////////////////////////////////////////////////////////////////////
	//
	// Get all known content locator IDs
	//
    this.getContentLocator = function()
    {
	    throwInvalid(this.id);
    	return this.content.getContentLocator();
    }
     
	//////////////////////////////////////////////////////////////////////////////
	//
	// Get WorkflowStep for given index
	//
    this.getStep = function(/*[Number]*/ inStepNumber)
    {
	    throwInvalid(this.id);
    	var ret = null;
    	
    	if (inStepNumber >= 0 && inStepNumber < this.steps.length)
    	{
    		ret = this.steps[inStepNumber];
    	}
    	
    	return ret;
    }
    
	//////////////////////////////////////////////////////////////////////////////
	//
	// Get number of WorkflowStep's
	//
    this.getStepLength = function()
    {
    	return this.steps.length;
    }
}

Workflow.STEPSWITCH_AUTOMATIC	= 'automatic';
Workflow.STEPSWITCH_MANUALLY	= 'manually';

//////////////////////////////////////////////////////////////////////////////
 
function WorkflowStep(/*[Workflow]*/ inWorkflow)
{
    this.indicator = [];
    this.exe = null;
    this.triggers = [];
    this.content = new WorkflowContent();
    
    var workflow = inWorkflow;
    
	//////////////////////////////////////////////////////////////////////////////
	//
	// Initialize with JSON
	//
    this.initializeJSON = function(/*[Object]*/ inJSON)
    {
	    throwInvalid(inJSON);

		var indicator = inJSON['indicator'];
		
		if (isValidProperty(indicator))
		{
			if (isValidProperty(indicator.length))
			{
				var indicators = this.indicator;
				var thisObj = this;
				
				forEach(indicator, function(aIndicator)
				{
					try
					{
						var indicatorObj = new Indicator(thisObj);
						indicatorObj.initializeJSON(aIndicator);
						indicators.push(indicatorObj);
					}
					catch(exc)
					{
						exclog(exc);
					}
				});
			}
			else
			{
				try
				{
					var indicatorObj = new Indicator(this);
					indicatorObj.initializeJSON(indicator);
					this.indicator.push(indicatorObj);
				}
				catch(exc)
				{
					exclog(exc);
				}
			}
		}
		
		// add content to the step
		var contentArray = inJSON['content'];
		throwInvalid(contentArray);
		this.content.initializeJSON(contentArray);
		
		// add trigger events to the sep
		var triggerArray = inJSON['trigger'];
		throwInvalid(triggerArray, 'Array');

		// make this.triggers visible for forEach-callback function
		var triggers = this.triggers;
		
		forEach(triggerArray, function(triggerObj)
		{
			var trigger = new WorkflowTrigger();
			trigger.initializeJSON(triggerObj);
			triggers.push(trigger);
		});
	}
	
	//////////////////////////////////////////////////////////////////////////////
	//
	// Get content for locator ID
	//
    this.getContent = function(/*[string]*/ inLocator)
    {
    	return this.content.getContent(inLocator);
    }

	//////////////////////////////////////////////////////////////////////////////
	//
	// Get indicator
	//
    this.getIndicator = function(/*[Number]*/ inIndex)
    {
    	return this.indicator[inIndex];
    }

	//////////////////////////////////////////////////////////////////////////////
	//
	// Get all indicator
	//
    this.getAllIndicator = function()
    {
    	return this.indicator;
    }

	//////////////////////////////////////////////////////////////////////////////
	//
	// Get all known locator IDs
	//
    this.getContentLocator = function()
    {
    	return this.content.getContentLocator();
    }
    
	//////////////////////////////////////////////////////////////////////////////
	//
	// Return owning worklfow
	//
    this.getWorkflow = function()
    {
    	return workflow;
    }
    
	//////////////////////////////////////////////////////////////////////////////
	//
	// Get all WorkflowTrigger
	//
    this.getTriggers = function()
    {
    	var ret = [];
    	
    	forEach(this.triggers, function(trigger)
    	{
    		var component = trigger.getComponent();
    		
    		if (isValidProperty(component))
    		{
    			ret = ret.concat(component.getTrigger(trigger));
    		}
    		else
    		{
    			ret.push(trigger);
    		}
    	});

    	return ret;
    }
}

//////////////////////////////////////////////////////////////////////////////

function Indicator(/*[WorkflowStep]*/ inStep)
{
	this.menu = null;
	this.ctrl = null;
	this.text = null;
	this.direction = null;
	
	var step = inStep;
	var displaying = false;

	//////////////////////////////////////////////////////////////////////////////
	//
	// Initialize with JSON
	//
    this.initializeJSON = function(/*[Object]*/ inJSON)
    {
	    throwInvalid(inJSON);

		var menu = inJSON['menu'];
		var ctrl = inJSON['ctrl'];
		var text = inJSON['text'];
		var direction = inJSON['direction'];
		
		if (isValidProperty(menu) && menu.length)
		{
			this.menu = menu;
		}
		else
		{
			if (isValidProperty(ctrl) && ctrl.length)
			{
				this.ctrl = ctrl;
				this.text = isValidProperty(text) ? text : '';
				this.direction = isValidProperty(direction) ? direction : 'left';
			}
			else
			{
				throw "Empty Indicator definition";
			}
		}
	}

	//////////////////////////////////////////////////////////////////////////////
	//
	// Is this a menu item indicator?
	//
	this.isMenu = function()
	{
		return isValidProperty(this.menu);
	}
	
	//////////////////////////////////////////////////////////////////////////////
	//
	// Is this a ui control indicator?
	//
	this.isControl = function()
	{
		return isValidProperty(this.ctrl);
	}
	
	//////////////////////////////////////////////////////////////////////////////
	//
	// Display indicator
	//
	this.display = function(/*[IContentStorage]*/ inContentStorage)
	{
		displaying = true;

		if (this.isMenu())
		{
			var script = 'displayMenuIndicator("' + this.menu + '");';
			var cs = new CSInterface();
			cs.evalScript(script);
		}
		else
		{
			var text = this.text;
			var ctrl = this.ctrl;
			var direction = this.direction;

			function doDisplay(/*[String]*/ inText)
			{
				if (displaying)
				{
					var text = inText.replace(/\n/g, "\\n");

					if (isValidProperty(ctrl))
					{
						var script = 'displayUIIndicator("' + ctrl + '", "' + text + '", "' + direction + '");';
						var cs = new CSInterface();
						cs.evalScript(script);
					}
				}
			}

			var worklfow = null;
			if (isValidProperty(inContentStorage))
			{
				if (isValidProperty(step))
				{
					workflow = step.getWorkflow();
				}
			}

			if (isValidProperty(workflow))
			{
				inContentStorage.getString(workflow.id, text, function(inContent)
				{
					if (isValidProperty(inContent) && inContent.length > 0)
					{
						text = inContent;
					}

					doDisplay(text);
				});
			}
			else
			{
				doDisplay(text);
			}
		}
	}

	//////////////////////////////////////////////////////////////////////////////
	//
	// Cancel asynchronous display call
	//
	this.cancelDisplay = function()
	{
		displaying = false;
	}
}

//////////////////////////////////////////////////////////////////////////////

function WorkflowTrigger()
{
	this.eventType = null;
	this.timer = false;
	this.condition = null;
	this.ignore = false
	this.component = null;
	this.match = WorkflowTrigger.MATCHTYPE_POSITIVE;
	this.isComponent = false;
	this.content = null;
	
	//////////////////////////////////////////////////////////////////////////////
	//
	// Return true if there's content defined for this trigger
	//
	this.hasContent = function()
	{
		return isValidProperty(this.content);
	}
	
	//////////////////////////////////////////////////////////////////////////////
	//
	// Is a match a success (or a failure)
	//
	this.isPositiveMatch = function()
	{
		return this.match == WorkflowTrigger.MATCHTYPE_POSITIVE;
	}
	
	//////////////////////////////////////////////////////////////////////////////
	//
	// Initialize with JSON
	//
	this.initializeJSON = function(/*[Object]*/ inJSON)
	{
	    throwInvalid(inJSON);
	    
	    var evType = inJSON['event'];
	    var timer = inJSON['timer'];
	    var condition = inJSON['condition'];
	    var ignore = inJSON['ignore'];
	    var component = inJSON['name'];
	    var match = inJSON['match'];
	    
	    if (isValidProperty(evType, 'String'))
	    {
	    	this.eventType = evType;
	    }
	    if (isValidProperty(timer, 'Boolean'))
	    {
	    	this.timer = timer;
	    }
	    if (isValidProperty(condition, 'String'))
	    {
	    	this.condition = condition;
	    }
	    if (isValidProperty(ignore, 'Boolean'))
	    {
	    	this.ignore = ignore;
	    }
	    if (isValidProperty(component, 'String'))
	    {
		    this.component = component;
		}
	    if (isValidProperty(match, 'String') &&
	    	(match == WorkflowTrigger.MATCHTYPE_POSITIVE || match == WorkflowTrigger.MATCHTYPE_NEGATIVE))
	    {
		    this.match = match;
		    
		    if (match == WorkflowTrigger.MATCHTYPE_NEGATIVE)
		    {
		    	this.ignore = false;
		    }
		}
		if (inJSON.hasOwnProperty('content'))
		{
			this.content = new WorkflowContent();
			this.content.initializeJSON(inJSON['content']);
		}
	}
	
	//////////////////////////////////////////////////////////////////////////////
	//
	// Return related component if defined
	//
	this.getComponent = function()
	{
		var ret = null;
		
		if (this.component == 'all')
		{
			ret = getTriggerALL();
		}
		else
		{
			ret = WorkflowTriggerComponentManager.get().getComponent(this.component);
		}
		
		return ret;
	}
	
	//////////////////////////////////////////////////////////////////////////////
	//
	// Validate event for trigger
	//
	this.validate = function(/*[EventObject]*/ inEvent, /*[Function]*/ inResultCallback, /*[Boolean]*/ inIgnoreCondition)
	{
		var ignoreCondition = false;
		if (isValidProperty(inIgnoreCondition))
		{
			ignoreCondition = inIgnoreCondition;
		}
		
		if (this.component == 'all')
		{
			// match to everything
			inResultCallback(this, inEvent, true);
		}
		else
		{
			var ret = false;
			var event = inEvent;
		
			if (this.eventType == event.type ||
				(this.timer && event.type == WorkflowTrigger.EVENTTYPE_TIMER))
			{
				if (!ignoreCondition && isValidProperty(this.condition) && this.condition.length)
				{
					var script = 'validateCondition("' + this.condition + '", "' + inEvent.data + '", ' + __debuglog__ + ');';
					
					dbglog('WorkflowTrigger ("' + this.eventType + '") CONDITION-SCRIPT "' + script + '"');
					
					var thisObj = this;
				
					var cs = new CSInterface();
					cs.evalScript(script, ResultCB);
				
					function ResultCB(result)
					{
						dbglog('WorkflowTrigger ("' + thisObj.eventType + '") CONDITION-RESULT "' + result + '"');
						
						if (result.indexOf(EvalScript_ErrMessage) == 0)
						{
							ret = false;
						}
						else 
						{
							try
							{
								var resultObj = JSON.parse(result);
								if (resultObj.error.length > 0)
								{
									ret = false;
								}
								else
								{
									ret = resultObj.result;
								}
							}
							catch(e)
							{
								exclog(e);
								ret = false;
							}
						}					
					
						inResultCallback(thisObj, inEvent, ret);
					}
				}
				else if (!this.timer)
				{
					inResultCallback(this, inEvent, true);
				}
			}
			else
			{
				inResultCallback(this, inEvent, false);
			}
		}
	}

	//////////////////////////////////////////////////////////////////////////////
	//
	// Clone this instance
	//
	this.clone = function()
	{
		var ret = new WorkflowTrigger();
		ret.eventType	= this.eventType;
		ret.timer 		= this.timer; 	
		ret.condition 	= this.condition;
		ret.ignore 		= this.ignore; 
		ret.component 	= this.component;
		ret.isComponent = this.isComponent;
		return ret;
	}
}

WorkflowTrigger.EVENTTYPE_TIMER = '<timer>';
WorkflowTrigger.SYMBOL_EVENT_OBJECT = "${EVOBJ}$";
WorkflowTrigger.MATCHTYPE_POSITIVE = 'positive';
WorkflowTrigger.MATCHTYPE_NEGATIVE = 'negative';

//////////////////////////////////////////////////////////////////////////////

function WorkflowTriggerComponent()
{
	this.name = '';
	this.triggers = [];

	//////////////////////////////////////////////////////////////////////////////
	//
	// Initialize with JSON
	//
	this.initializeJSON = function(/*[Object]*/ inJSON)
    {
		// add content to the step
		this.name = inJSON['name'];
		throwInvalid(name);
		
		// add trigger events to the sep
		var triggerArray = inJSON['trigger'];
		throwInvalid(triggerArray, 'Array');

		// make this.triggers visible for forEach-callback function
		var triggers = this.triggers;
		
		forEach(triggerArray, function(triggerObj)
		{
			var trigger = new WorkflowTrigger();
			trigger.initializeJSON(triggerObj);
			trigger.isComponent = true;
			triggers.push(trigger);
		});
    }
    
	//////////////////////////////////////////////////////////////////////////////
	//
	// Return all trigger
	//
	this.getTrigger = function(/*[WorkflowTrigger]*/ inOwner)
    {
    	var ret = [];
    	
    	if (isValidProperty(inOwner))
    	{
    		forEach(this.triggers, function(trigger)
    		{
    			var itrigger = trigger.clone();
    			itrigger.match = inOwner.match;
    			if (inOwner.isPositiveMatch() && inOwner.ignore)
    			{
	    			itrigger.ignore = true;
	    		}
	    		
    			ret.push(itrigger);
    		});
    	}
	    
	    return ret;
    }
}

//////////////////////////////////////////////////////////////////////////////

function WorkflowContent()
{
	var parts = {};
	
	//////////////////////////////////////////////////////////////////////////////
	//
	// Initialize with JSON
	//
	this.initializeJSON = function(/*[Object]*/ inJSON)
    {
	    throwInvalid(inJSON);
	    
		for (var ci in inJSON)
		{
			var contentPart = inJSON[ci];
			throwInvalid(contentPart);

			if (contentPart.hasOwnProperty('id') && contentPart.hasOwnProperty('mime'))
			{
				parts[ci] = new WorkflowContentPart(contentPart['id'], contentPart['mime']);
			}
		}
	}

	//////////////////////////////////////////////////////////////////////////////
	//
	// Get content for locator ID
	//
    this.getContent = function(/*[string]*/ inLocator)
    {
    	return parts[inLocator];
    }

	//////////////////////////////////////////////////////////////////////////////
	//
	// Get all known locator IDs
	//
    this.getContentLocator = function()
    {
    	var ret = [];
    	
    	for (var pi in parts)
    	{
    		ret.push(pi);
    	}
    	
    	return ret;
    }
}
 
//////////////////////////////////////////////////////////////////////////////

function WorkflowContentPart(/*[string]*/ inID, /*[string]*/ inMimeType)
{
    throwInvalid(inID);
    throwInvalid(inMimeType);
     
    this.id = inID;
    this.mimeType = inMimeType;
}
 
//////////////////////////////////////////////////////////////////////////////

Workflow.create = function(/*[String]*/ inJSON)
{
	var wf = null;
	
	try
	{
		var obj = JSON.parse(inJSON);
		wf = new Workflow();
		wf.initializeJSON(obj);
	}
	catch(exc)
	{
		exclog(exc);
		wf = null;
	}
		
	return wf;
}

//////////////////////////////////////////////////////////////////////////////

function getTriggerALL()
{
	if (!isValidProperty(gWorkflowTriggerALL))
	{
		gWorkflowTriggerALL = new WorkflowTriggerComponent();
		gWorkflowTriggerALL.name = "all";
		var triggerALL = new WorkflowTrigger();
		triggerALL.component = "all";
		gWorkflowTriggerALL.triggers.push(triggerALL);
	}
	
	return gWorkflowTriggerALL;
}