//----------------------------BEGIN LICENSE----------------------------
/* 
 * Willow : the Open Source WorkFlow Project
 * Distributable under GNU LGPL license by gun.org
 *
 * Copyright (C) 2004-2010 huihoo.org 
 * Copyright (C) 2004-2010  ZosaTapo <dertyang@hotmail.com> 
 * 
 * ====================================================================
 * Project Homepage : http://www.huihoo.org/willow
 * Source Forge     : http://sourceforge.net/projects/huihoo
 * Mailing list     : willow@lists.sourceforge.net 
 */
//----------------------------END  LICENSE-----------------------------
package org.huihoo.workflow.coreImpl.store;

import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.huihoo.workflow.WorkflowException;
import org.huihoo.workflow.WorkflowTransaction;
import org.huihoo.workflow.selector.SelectorContext;
import org.huihoo.workflow.selector.WorkflowRouteEvalutor;
import org.huihoo.workflow.selector.WorkflowTransitionSelector;
import org.huihoo.workflow.server.WorkflowCase;
import org.huihoo.workflow.server.WorkflowCaseContext;
import org.huihoo.workflow.server.WorkflowSession;
import org.huihoo.workflow.server.WorkflowWork;
import org.huihoo.workflow.server.WorkflowXPDL;
import org.huihoo.workflow.store.WorkflowCaseDatabase;
import org.huihoo.workflow.xpdl.ExtendedAttribute;
import org.huihoo.workflow.xpdl.WorkflowActivity;
import org.huihoo.workflow.xpdl.WorkflowCondition;
import org.huihoo.workflow.xpdl.WorkflowPackage;
import org.huihoo.workflow.xpdl.WorkflowParameter;
import org.huihoo.workflow.xpdl.WorkflowProcess;
import org.huihoo.workflow.xpdl.WorkflowTransition;
import org.huihoo.workflow.xpdl.XPDLGlobals;
import org.huihoo.workflow.xpdl.activity.SplitType;

public class OutgoingTransitionChooser
{
	private static Log log = LogFactory.getLog("willow.OutgoingTransitionsChooser");

	public static List chooseOutgingTransitions(
		WorkflowSession workflowSession,
		WorkflowCaseDatabase caseDatabase,
		WorkflowTransaction userTransaction,
		WorkflowWork workflowWork)
		throws WorkflowException
	{
		WorkflowActivity workflowActivity = workflowWork.getActivity();

		//========================================================================
		//  特殊处理 (有且只有一个Out WorkflowTransition的WorkflowActivity)
		//========================================================================
		List disp_outTrans = workflowActivity.getValidOutgoingTransitions();
		if (disp_outTrans.size() == 1)
		{
			List filter_outTrans = new ArrayList();
			filter_outTrans.add(disp_outTrans.get(0));
			log.debug("Only one outTransition,select it unconditionally.[" + workflowActivity + "]");
			return filter_outTrans;
		}

		//========================================================================
		//  TRANSITION_SELECTOR_QUALIFIED_CLASS_NAME 
		//========================================================================

		ExtendedAttribute attribute =
			(ExtendedAttribute) workflowActivity.getExtendedAttributes().get(
				XPDLGlobals.TRANSITION_SELECTOR_QUALIFIED_CLASS_NAME);
		if (attribute != null && attribute.getValue() != null)
		{
			return chooseOutgingTransitions_bySelector(workflowSession, caseDatabase, userTransaction, workflowWork);
		}

		//========================================================================
		//  SPLIT TYPE
		//========================================================================

		SplitType splitType = workflowActivity.getSplitType();
		if (SplitType.SPLIT_AND.equals(splitType))
		{
			return chooseOutgingTransitions_andSplit(workflowSession, caseDatabase, userTransaction, workflowWork);
		}
		else if (SplitType.SPLIT_XOR.equals(splitType))
		{
			try
			{
				return chooseOutgingTransitions_xorSplit(workflowSession, caseDatabase, userTransaction, workflowWork);
			}
			catch (IOException e)
			{
				throw new WorkflowException(e);
			}
		}

		try
		{
			//-----------Default Behavior---------------
			return chooseOutgingTransitions_orSplit(workflowSession, caseDatabase, userTransaction, workflowWork);
		}
		catch (IOException e)
		{
			throw new WorkflowException(e);
		}
	}

	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	public static List chooseOutgingTransitions_bySelector(
		WorkflowSession workflowSession,
		WorkflowCaseDatabase caseDatabase,
		WorkflowTransaction userTransaction,
		WorkflowWork workflowWork)
		throws WorkflowException
	{
		log.debug(" TRANSITION_SELECTOR_QUALIFIED_CLASS_NAME is NOT NULL ,  select  outgoing transitions by selector");
		List outTrans = new ArrayList();

		WorkflowActivity workflowActivity = workflowWork.getActivity();
		ExtendedAttribute attribute =
			(ExtendedAttribute) workflowActivity.getExtendedAttributes().get(
				XPDLGlobals.TRANSITION_SELECTOR_QUALIFIED_CLASS_NAME);

		String implClassName = attribute.getValue();
		Class implClass = null;
		WorkflowTransitionSelector selector = null;
		try
		{
			implClass = Class.forName(implClassName);
			selector = (WorkflowTransitionSelector) implClass.newInstance();
		}
		catch (Exception e)
		{
			WorkflowException ex = new WorkflowException("can not select outgoing transitions");
			ex.setRootCause(e);
			throw ex;
		}

		WorkflowCase workflowCase = workflowWork.getCase();
		WorkflowCaseContext caseContext = workflowCase.getCaseContext();
		SelectorContext context = createSelectorConext(caseContext, null);

		outTrans = selector.select(workflowWork, context);

		return outTrans;
	}

	public static List chooseOutgingTransitions_andSplit(
		WorkflowSession workflowSession,
		WorkflowCaseDatabase caseDatabase,
		WorkflowTransaction userTransaction,
		WorkflowWork workflowWork)
		throws WorkflowException
	{
		log.debug(" SplitType.SPLIT_AND  select all outgoing transitions");
		List outTrans = new ArrayList();
		WorkflowActivity workflowActivity = workflowWork.getActivity();
		List transList = workflowActivity.getValidOutgoingTransitions();

		int sizeTrans = transList.size();

		for (int i = 0; i < sizeTrans; ++i)
		{
			outTrans.add(transList.get(i));
		}

		return outTrans;
	}

	public static List chooseOutgingTransitions_xorSplit(
		WorkflowSession workflowSession,
		WorkflowCaseDatabase caseDatabase,
		WorkflowTransaction userTransaction,
		WorkflowWork workflowWork)
		throws WorkflowException, IOException
	{
		log.debug(" SplitType.SPLIT_XOR  select (single) specific  outgoing transitions");

		List outTrans = new ArrayList();
		WorkflowActivity workflowActivity = workflowWork.getActivity();
		List transList = workflowActivity.getValidOutgoingTransitions();
		WorkflowProcess workflowProcess = workflowActivity.getWorkflowProcess();
		WorkflowPackage workflowPackage = workflowProcess.getWorkflowPackage();
		WorkflowRouteEvalutor selector = caseDatabase.getWorkflowTemplate().getOwnerWorkflowXPDL().getRouteEvalutor();

		SelectorContext context = null;
		WorkflowCase workflowCase = workflowWork.getCase();
		WorkflowCaseContext caseContext = workflowCase.getCaseContext();

		int sizeTrans = transList.size();
		if (sizeTrans > 0)
		{
			WorkflowTransition transition = null;

			for (int i = 0; i < sizeTrans; ++i)
			{
				transition = (WorkflowTransition) transList.get(i);
				context = createSelectorConext(caseContext, transition);

				WorkflowCondition conditon = transition.getWorkflowCondition();
				if (conditon == null)
				{
					log.debug("ACCEPT transition:" + transition + " | condition: " + conditon);
					outTrans.add(transition);
					break;
				}
				else
				{
					if (selector.evalute(workflowWork, transition, context))
					{
						log.debug("ACCEPT transition:" + transition + " | condition: " + conditon);
						try
						{
							updateSelectorContext(
								workflowSession,
								workflowProcess,
								caseDatabase,
								userTransaction,
								context,
								caseContext,
								false);
						}
						catch (Throwable e)
						{
							WorkflowException ex = new WorkflowException("can not filter outgoing transitions");
							ex.setRootCause(e);
							throw ex;
						}
						outTrans.add(transition);
						break;
					}
					else
					{
						log.debug("REJECT transition:" + transition + " | condition: " + conditon);
					}
				}

			} //~end for (int i = 0; i < sizeTrans; ++i)

			try
			{
				updateSelectorContext(
					workflowSession,
					workflowProcess,
					caseDatabase,
					userTransaction,
					context,
					caseContext,
					true);
			}
			catch (Throwable e)
			{
				WorkflowException ex = new WorkflowException("can not filter outgoing transitions");
				ex.setRootCause(e);
				throw ex;
			}
		}
		return outTrans;
	}
	//------------------Default behavior------------------
	public static List chooseOutgingTransitions_orSplit(
		WorkflowSession workflowSession,
		WorkflowCaseDatabase caseDatabase,
		WorkflowTransaction userTransaction,
		WorkflowWork workflowWork)
		throws WorkflowException, IOException
	{
		log.debug(" SplitType.SPLIT_OR  select outgoing transitions based on condition evaluation");

		WorkflowXPDL workflowXPDL = caseDatabase.getWorkflowTemplate().getOwnerWorkflowXPDL();

		List outTrans = new ArrayList();
		WorkflowActivity workflowActivity = workflowWork.getActivity();
		List transList = workflowActivity.getValidOutgoingTransitions();
		WorkflowProcess workflowProcess = workflowActivity.getWorkflowProcess();
		WorkflowPackage workflowPackage = workflowProcess.getWorkflowPackage();

		WorkflowRouteEvalutor evalutor = workflowXPDL.getRouteEvalutor();

		SelectorContext context = null;
		WorkflowCase workflowCase = workflowWork.getCase();
		WorkflowCaseContext caseContext = workflowCase.getCaseContext();

		List rejectTrans = new ArrayList();

		int sizeTrans = transList.size();
		if (sizeTrans > 0)
		{
			WorkflowTransition transition = null;

			for (int i = 0; i < sizeTrans; ++i)
			{
				transition = (WorkflowTransition) transList.get(i);

				WorkflowCondition conditon = transition.getWorkflowCondition();

				transition = (WorkflowTransition) transList.get(i);
				context = createSelectorConext(caseContext, transition);

				if (conditon == null)
				{
					outTrans.add(transition);
					log.debug("ACCEPT transition:" + transition + " | condition: " + conditon);
				}
				else
				{
					if (evalutor.evalute(workflowWork, transition, context))
					{
						log.debug("ACCEPT transition:" + transition + " | condition: " + conditon);
						try
						{
							updateSelectorContext(
								workflowSession,
								workflowProcess,
								caseDatabase,
								userTransaction,
								context,
								caseContext,
								false);
						}
						catch (Throwable e)
						{
							WorkflowException ex = new WorkflowException("can not filter outgoing transitions");
							ex.setRootCause(e);
							throw ex;
						}

						outTrans.add(transition);
					}
					else
					{
						log.debug("REJECT transition:" + transition + " | condition: " + conditon);
						rejectTrans.add(transition);

					} //~end if (selector.evalute(workflowWork, transition, context))

				} //~end if (conditon == null)

			} //~end for (int i = 0; i < sizeTrans; ++i)
			updateSelectorContext(
				workflowSession,
				workflowProcess,
				caseDatabase,
				userTransaction,
				context,
				caseContext,
				true);

			try
			{
				JoinActivityFinder.signalOrJoinActivity(userTransaction, workflowWork, outTrans, rejectTrans);
			}
			catch (SQLException e)
			{
				WorkflowException ex = new WorkflowException("can not filter outgoing transitions");
				ex.setRootCause(e);

				ex.printStackTrace();

				throw ex;
			}
		}
		return outTrans;
	}

	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	private static SelectorContext createSelectorConext(
		WorkflowCaseContext caseContext,
		WorkflowTransition workflowTransition)
		throws WorkflowException
	{
		SelectorContext scriptContext = new SelectorContext();
		Enumeration enum = caseContext.getParameterNames();
		while (enum.hasMoreElements())
		{
			String paramName = (String) enum.nextElement();
			try
			{
				fillSelectorContext(scriptContext, caseContext.getParameterByName(paramName).copy());
			}
			catch (CloneNotSupportedException e)
			{
				throw new WorkflowException(e);
			}
		}

		return scriptContext;
	}

	private static void fillSelectorContext(SelectorContext context, WorkflowParameter worlflowParameter)
		throws WorkflowException
	{
		context.addParameter(worlflowParameter);
	}

	private static void updateSelectorContext(
		WorkflowSession workflowSession,
		WorkflowProcess workflowProcess,
		WorkflowCaseDatabase caseDatabase,
		WorkflowTransaction userTransaction,
		SelectorContext context,
		WorkflowCaseContext caseContext,
		boolean update)
		throws WorkflowException
	{
		Enumeration enum = caseContext.getParameterNames();
		WorkflowParameter workflowParmeter = null;
		WorkflowParameter scriptParameter = null;
		while (enum.hasMoreElements())
		{
			String paramName = (String) enum.nextElement();
			workflowParmeter = caseContext.getParameterByName(paramName);
			scriptParameter = (WorkflowParameter) context.getParameter(paramName);
			if (workflowParmeter == null || scriptParameter == null)
			{
				continue;
			}

			Object value = workflowParmeter.getValue();
			Object otherValue = scriptParameter.getValue();

			if ((value != null && !value.equals(otherValue)) || (otherValue != null && !otherValue.equals(value)))
			{
				workflowParmeter.setValue(scriptParameter.getValue());
				if (update)
				{
					caseDatabase.updateParameter(
						workflowSession,
						userTransaction,
						workflowProcess,
						caseContext.getWorkflowCase(),
						workflowParmeter);
				}
			}
		}
	}
}
