//----------------------------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.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.huihoo.commons.jdbc.JDBCUtilities;
import org.huihoo.workflow.SchemaContext;
import org.huihoo.workflow.WorkflowException;
import org.huihoo.workflow.WorkflowTransaction;
import org.huihoo.workflow.event.ActivityEvent;
import org.huihoo.workflow.server.WorkflowWork;
import org.huihoo.workflow.xpdl.ExtendedAttribute;
import org.huihoo.workflow.xpdl.WorkflowActivity;
import org.huihoo.workflow.xpdl.WorkflowProcess;
import org.huihoo.workflow.xpdl.WorkflowTransition;
import org.huihoo.workflow.xpdl.XPDLGlobals;
import org.huihoo.workflow.xpdl.activity.JoinType;
import org.huihoo.workflow.xpdl.activity.SplitType;

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

	public static void signalOrJoinActivity(
		WorkflowTransaction userTransaction,
		WorkflowWork workflowWork,
		List trueTransitions,
		List falseTransitions)
		throws SQLException, WorkflowException
	{
		WorkflowActivity srcActivity = workflowWork.getActivity();
		SplitType splitType = srcActivity.getSplitType();

		if (!SplitType.SPLIT_OR.equals(splitType))
		{
			//---------------------------------------------------------------------------------------
			//XPDL设计人员显式指定OR SPLIT ,WILLOW才会进行OR JOIN 检查处理.原因是OR JOIN检查很耗时
			//---------------------------------------------------------------------------------------

			return;
		}

		if (trueTransitions.size() == 0)
		{
			throw new WorkflowException("trueTransitions.size()==0 ");
		}

//		log.info(
//			"\n\n--------------------------------------------------------------------------\n"
//				+ "source activity==>"
//				+ srcActivity
//				+ "\ntrue transitions==>"
//				+ trueTransitions
//				+ "\nfalse transitions==>"
//				+ falseTransitions
//				+ "\n--------------------------------------------------------------------------\n\n");

		WorkflowProcess workflowProcess = workflowWork.getCase().getWorkflowProcess();

		WorkflowActivity trueJoinActivity = null;
		WorkflowActivity falseJoinActivity = null;
		WorkflowActivity orJoinActivity = null;

		List swap_trueTransitions = null;
		List swap_falseTransitions = null;

		//-------------------------------------------------------------------
		//  1.根据 TRUE 路由选择方案 设置TRUE TOKEN 传递路径
		//-------------------------------------------------------------------

		trueJoinActivity = ((WorkflowTransition) trueTransitions.get(0)).getToActivity();
		swap_trueTransitions = trueTransitions;

		boolean forceLoopTrueJoinActivity = false;
		boolean forceLoopFalseJoinActivity = false;

		LoopTrueJoinActivity : while (
			forceLoopTrueJoinActivity
				|| (trueJoinActivity != null && !JoinType.JOIN_OR.equals(trueJoinActivity.getJoinType())))
		{
			forceLoopTrueJoinActivity = false;
			orJoinActivity = null;

			//---------------------------------------------------------------------------
			//  1.1 根据 TRUE 路由选择方案, 遍历next Activity 没有 追溯到OR Join Activity
			//  [到达XPDL End Activity]
			//---------------------------------------------------------------------------
			swap_trueTransitions = trueJoinActivity.getValidOutgoingTransitions();
			if (swap_trueTransitions == null || swap_trueTransitions.size() == 0)
			{
				throw new WorkflowException("outgoingTransitions.size()==0," + trueJoinActivity);
			}

			//---------------------------------------------------------------------------
			//  1.2 根据 TRUE 路由选择方案, 遍历next Activity 没有 追溯到OR Join Activity
			//  [流程描述XPDL 逻辑错误]
			//---------------------------------------------------------------------------
			trueJoinActivity = nextPossibleJoinActivity(trueJoinActivity, swap_trueTransitions);
			if (trueJoinActivity == null)
			{
				throw new WorkflowException("nextOrJoinActivity is null");
			}

			//---------------------------------------------------------------------------
			//  1.3 根据 TRUE 路由选择方案, 选择的next Acitivity 不是 OR Join Activity
			//---------------------------------------------------------------------------
			if (!JoinType.JOIN_OR.equals(trueJoinActivity.getJoinType()))
			{
				//find next possible trueJoinActivity
				continue LoopTrueJoinActivity;
			}

			//---------------------------------------------------------------------------
			//  1.4 根据 TRUE 路由选择方案, 检索到 OR Join Activity
			//---------------------------------------------------------------------------
			//log.info("FOUND TRUE OR-JOIN-ACTIVITY : " +  trueJoinActivity.getInfo());

			//-------------------------------------------------------------------
			//  2.根据 FALSE 路由选择方案 设置FALSE TOKEN 传递路径
			//-------------------------------------------------------------------	
			//---------------------------------------------------------------------------
			//  2.1 根据 FALSE 路由选择方案, 分发Activity next 路由全部被选择
			//---------------------------------------------------------------------------
			swap_falseTransitions = falseTransitions;
			if (swap_falseTransitions == null || swap_falseTransitions.size() == 0)
			{
				//----------------------------------------
				// [结束]  检索到合法的 OR Join Activity
				//----------------------------------------
				//or-split's all outTransitions are selected to process
				orJoinActivity = trueJoinActivity;
				break LoopTrueJoinActivity;
			}

			falseJoinActivity = ((WorkflowTransition) swap_falseTransitions.get(0)).getToActivity();
			if (JoinType.JOIN_OR.equals(falseJoinActivity.getJoinType()))
			{
				//一个OR Split  Activity 直接后继 Activity 的 JoinType 不应该为 OR
				throw new WorkflowException("bad format XPDL data , OR Split Activity's next direct Activity's JoinType should not be 'OR'  ");
			}

			LoopFalseJoinActivity : while (
				forceLoopFalseJoinActivity
					|| (falseJoinActivity != null && !JoinType.JOIN_OR.equals(falseJoinActivity.getJoinType())))
			{
				forceLoopFalseJoinActivity = false;

				swap_falseTransitions = falseJoinActivity.getValidOutgoingTransitions();
				//---------------------------------------------------------------------------
				//  2.2 根据 FLASE 路由选择方案, 遍历 next Activity 没有 追溯到 OR Join Activity
				//  		[到达XPDL End Activity]
				//---------------------------------------------------------------------------
				if (swap_falseTransitions == null || swap_falseTransitions.size() == 0)
				{
					break LoopFalseJoinActivity;
				}

				falseJoinActivity = nextPossibleJoinActivity(falseJoinActivity, swap_falseTransitions);

				//---------------------------------------------------------------------------
				//  2.3 根据 FLASE 路由选择方案, 遍历next Activity 没有 追溯到 OR Join Activity
				//  		[流程描述XPDL 逻辑错误]
				//---------------------------------------------------------------------------
				if (falseJoinActivity == null)
				{
					break LoopFalseJoinActivity;
				}
				//---------------------------------------------------------------------------
				//  2.4 根据 FALSE 路由选择方案, 选择的next Acitivity 不是 OR Join Activity
				//			[继续]  移动 FALSE TOKEN ，选择下一个 OR Join Activity 进行测试
				//---------------------------------------------------------------------------
				if (!JoinType.JOIN_OR.equals(falseJoinActivity.getJoinType()))
				{
					//find next possible falseJoinActivity
					continue LoopFalseJoinActivity;
				}

				//---------------------------------------------------------------------------
				//  2.5 根据 FALSE 路由选择方案, 检索到 OR Join Activity
				//---------------------------------------------------------------------------
				//log.info("FOUND FALSE OR-JOIN-ACTIVITY : " + falseJoinActivity.getInfo());

				if (falseJoinActivity.getUUID().equals(trueJoinActivity.getUUID()))
				{
					//---------------------------------------------------------------------------
					//  2.6.1 根据 TRUE/FALSE 路由选择方案, 检索到 合法的 OR Join Activity
					// 				[结束]  检索到合法的 OR Join Activity
					//---------------------------------------------------------------------------

					//OR-JOIN Activity found
					orJoinActivity = falseJoinActivity;
					break LoopTrueJoinActivity;
				}
				else
				{
					//---------------------------------------------------------------------------
					//  2.6.2 根据 TRUE/FALSE 路由选择方案, 没有检索到 合法的 OR Join Activity
					// 				[继续]  移动 FALSE TOKEN ，选择下一个 OR Join Activity 进行测试
					//---------------------------------------------------------------------------
					forceLoopFalseJoinActivity = true;
					continue LoopFalseJoinActivity;
				}

			} //~end while (falseJoinActivity != null && !JoinType.JOIN_OR.equals(falseJoinActivity.getJoinType()))

			//---------------------------------------------------------------------------
			//  1.5 根据 TRUE/FALSE 路由选择方案, 没有检索到 OR Join Activity
			//  		[继续] 移动 TRUE TOKEN ，选择下一个 OR Join Activity 进行测试
			//---------------------------------------------------------------------------								
			//find next possible orJoinActivity
			forceLoopTrueJoinActivity = true;
			continue LoopTrueJoinActivity;

			//------------------------------------------------------------------------------				
		} //~end while (trueJoinActivity != null && !JoinType.JOIN_OR.equals(trueJoinActivity.getJoinType()))

		if (orJoinActivity == null)
		{
			throw new WorkflowException("orJoinActivity not found");
		}

		//log.info("orJoinActivity = ["+ orJoinActivity.getInfo()+"]");

		Connection conn = (Connection) userTransaction.getConnection();
		String packageId = workflowProcess.getWorkflowPackage().getUUID();
		String processId = workflowProcess.getUUID();
		String caseId = workflowWork.getCase().getUUID();

		int size = falseTransitions.size();
		for (int i = 0; i < size; ++i)
		{
			WorkflowTransition falseTransition = (WorkflowTransition) falseTransitions.get(i);
			WorkflowActivity falseActivity = falseTransition.getToActivity();
			WorkflowTransition falseJoinTransition = null;

			while (falseActivity != null)
			{
				if (falseActivity.getUUID().equals(orJoinActivity.getUUID()))
				{
					falseJoinTransition = falseTransition;
					break;
				}

				swap_falseTransitions = falseActivity.getValidOutgoingTransitions();
				if (swap_falseTransitions == null || swap_falseTransitions.size() == 0)
				{
					break;
				}
				else
				{
					falseTransition = ((WorkflowTransition) swap_falseTransitions.get(0));
					falseActivity = nextPossibleJoinActivity(falseActivity, swap_falseTransitions);
				}
			} //while (falseActivity != null)	

			if (falseJoinTransition != null)
			{
				//log.info("falseJoinTransition => " + falseJoinTransition.getInfo());

				//OR-JOIN Activity found,set IMAGINARY_DISPATCH_ACTIVITY falseJoinActivity#int_dispatch_eventType
				//insert into database table(rt_p_activity_dispatch)
				//@see  database table(rt_p_activity_dispatch)

				WorkflowActivity ins_falseActivity = falseJoinTransition.getFromActivity();
				String ins_falseActivityId = ins_falseActivity.getUUID();
				String ins_owner_processid = ins_falseActivity.getWorkflowProcess().getUUID();

				PreparedStatement pstmt = null;
				String strSQL = null;

				strSQL =
					"SELECT  int_dispatch_count  FROM  "
						+ userTransaction.getDatabase().getSchemaContext().getTableName(SchemaContext.SCHEMA_ACTIVITY_DISPATCH)
						+ " "
						+ "WHERE  vc_packageid=? "
						+ "AND    vc_processid=? "
						+ "AND    vc_caseid=?  "
						+ "AND    vc_owner_processid=? "
						+ "AND    vc_activityid=? ";

				pstmt = conn.prepareStatement(strSQL);
				pstmt.setString(1, packageId);
				pstmt.setString(2, processId);
				pstmt.setString(3, caseId);

				pstmt.setString(4, ins_owner_processid);
				pstmt.setString(5, ins_falseActivityId);
				ResultSet rs = pstmt.executeQuery();

				int int_dispatch_count = 0;
				if (rs.next())
				{
					int_dispatch_count = rs.getInt("int_dispatch_count");
				}

				JDBCUtilities.cleanupNoThrow(pstmt);
				pstmt = null;

				if (int_dispatch_count == 0)
				{
					strSQL =
						"INSERT INTO  "
							+ userTransaction.getDatabase().getSchemaContext().getTableName(SchemaContext.SCHEMA_ACTIVITY_DISPATCH)
							+ "(vc_packageid,vc_processid,vc_caseid,vc_owner_processid,vc_activityid,int_dispatch_count,int_dispatch_eventType) "
							+ " VALUES(?,?,?,?,?,?,?)";
					pstmt = conn.prepareStatement(strSQL);
					pstmt.setString(1, packageId);
					pstmt.setString(2, processId);
					pstmt.setString(3, caseId);

					pstmt.setString(4, ins_owner_processid);
					pstmt.setString(5, ins_falseActivityId);
					pstmt.setInt(6, int_dispatch_count + 1);
					pstmt.setInt(7, ActivityEvent.ACTIVITY_IMAGINARY_DISPATCHED);
					pstmt.execute();
				}
				else
				{
					strSQL =
						"UPDATE "
							+ userTransaction.getDatabase().getSchemaContext().getTableName(SchemaContext.SCHEMA_ACTIVITY_DISPATCH)
							+ " "
							+ "SET    int_dispatch_count=?  "
							+ "WHERE  vc_packageid=? "
							+ "AND    vc_processid=? "
							+ "AND    vc_caseid=?  "
							+ "AND    vc_owner_processid=? "
							+ "AND    vc_activityid=? "
							+ "AND    int_dispatch_eventType=? ";
					pstmt = conn.prepareStatement(strSQL);
					pstmt.setInt(1, int_dispatch_count + 1);
					pstmt.setString(2, packageId);
					pstmt.setString(3, processId);
					pstmt.setString(4, caseId);
					pstmt.setString(5, ins_owner_processid);
					pstmt.setString(6, ins_falseActivityId);
					pstmt.setInt(7, ActivityEvent.ACTIVITY_IMAGINARY_DISPATCHED);
					pstmt.execute();
				}

				JDBCUtilities.cleanupNoThrow(pstmt);
				pstmt = null;
			}

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

	}

	private static WorkflowActivity nextPossibleJoinActivity(WorkflowActivity fromActivity, List outTransitions)
	{
		WorkflowActivity joinActivity = null;

		if (outTransitions.size() == 1)
		{
			//log.info("[" + fromActivity + "] only one outgoing transition ,select as or-join-transition unconditionally");
			joinActivity = ((WorkflowTransition) outTransitions.get(0)).getToActivity();
		}
		else if (outTransitions.size() > 1)
		{

			String activity_dispatch_transition_id = null;
			ExtendedAttribute attribute = fromActivity.findExtendedAttribute(XPDLGlobals.ACTIVITY_DISPATCH_TRANSITION_ID);
			if (attribute != null && attribute.getValue() != null && attribute.getValue().length() > 0)
			{
				activity_dispatch_transition_id = attribute.getValue();
			}

			if (activity_dispatch_transition_id != null)
			{
				//log.info("["+ fromActivity+ "] select  or-join-transition by attribute '"+ XPDLGlobals.ACTIVITY_dispatch_TRANSITION_ID+ "'");
				WorkflowProcess owner_process = fromActivity.getWorkflowProcess();
				WorkflowTransition transition = owner_process.findTransition(activity_dispatch_transition_id);
				joinActivity = transition.getToActivity();
			}
			else
			{
				//log.info("[" + fromActivity + "] select first transition as or-join-transition");
				joinActivity = ((WorkflowTransition) outTransitions.get(0)).getToActivity();
			}
		}

		return joinActivity;
	}
}
