/// <summary>
        /// Transition completed - consume input tokens, produce output tokens
        /// and cancel all transitions that share the same tokens.
        /// Also, if some tokens were waiting, put them in 'READY' state.
        /// Wow, looks quite complex.
        /// TODO: fix case when transition completion removes token from 
        /// some or-join checklist
        /// </summary>
        /// <param name="at"></param>
        private void AfterTransitionCompleted(string correlationId)
        {
            lock (this)
            {
                TaskShell at = GetActiveTransition(correlationId);
                if (at == null) throw new Exception("Invalid correlation Id");
                log.Info("Transition completed: {0}", at);
                if (at.Status != TransitionStatus.ENABLED && at.Status != TransitionStatus.STARTED) throw new Exception("Invalid transition status");
                Task tsk = Definition.GetTask(at.TaskId);
                if (at.Status == TransitionStatus.ENABLED)
                {
                    AfterTransitionSelected(correlationId);
                }
                Debug.Assert(at.Status == TransitionStatus.STARTED);

                //1. transfer task output data from transition
                at.TransferTaskOutputDataToParent(GetProcessVariablesContainer());
                ValidateProcessInternalData();
                at.Status = TransitionStatus.COMPLETED;

                //handle cancel set
                if (tsk.CancelSet.Count > 0)
                {
                    log.Info("Handling cancel set of task {0}", tsk.Id);
                    foreach (string plid in tsk.CancelSet)
                    {
                        RemoveAllTokensInPlace(plid);
                    }
                }

                //and produce output tokens
                int cnt = 0;
                if (tsk.SplitType == JoinType.AND)
                {
                    foreach (Flow fl in tsk.FlowsOut)
                    {
                        if (fl.InputCondition != null && fl.InputCondition.Length > 0) throw new Exception();
                        AddToken(fl.To.Id);
                        cnt++;
                    }
                }
                else if (tsk.SplitType == JoinType.XOR)
                {
                    IList<Flow> flows = tsk.FlowsOutOrdered;
                    for (int i = 0; i < flows.Count; i++)
                    {
                        if (i == flows.Count - 1)
                        {
                            //last flow - the default one. Always add a token if we are here
                            AddToken(flows[i].To.Id);
                            cnt++;
                        }
                        else
                        {
                            if (EvaluateFlowInputCondition(flows[i]))
                            {
                                AddToken(flows[i].To.Id);
                                cnt++;
                                break;
                            }
                        }
                    }
                }
                else if (tsk.SplitType == JoinType.OR)
                {
                    IList<Flow> flows = tsk.FlowsOutOrdered;
                    for (int i = 0; i < flows.Count; i++)
                    {
                        if (EvaluateFlowInputCondition(flows[i]))
                        {
                            AddToken(flows[i].To.Id);
                            cnt++;
                        }
                    }
                    if (cnt == 0)
                    {
                        //we haven't created any tokens, created the default one
                        AddToken(flows[flows.Count - 1].To.Id);
                        cnt++;
                    }
                }
                else throw new Exception();
                if (cnt == 0) throw new Exception("Transition completion did not produce any tokens");

                //5 notify others
                ActiveTransitionCompleted compl = new ActiveTransitionCompleted();
                compl.CorrelationId = at.CorrelationId;
                compl.InstanceId = this.InstanceId;
                compl.TaskId = at.TaskId;
                compl.TaskType = tsk.GetType().Name;
                compl.TimeStamp = DateTime.Now;
                compl.DefinitionId = this.ProcessDefinitionId;
                NotifyProcessEvent(compl);
            }
        }
        /// <summary>
        /// Transition completed - consume input tokens, produce output tokens
        /// and cancel all transitions that share the same tokens.
        /// Also, if some tokens were waiting, put them in 'READY' state.
        /// Wow, looks quite complex.
        /// </summary>
        /// <param name="at"></param>
        private void AfterTransitionCompleted(string correlationId)
        {
            TaskShell at = _activeTransitions[correlationId];
            log.Info("Transition completed: {0}", at.CorrelationId);
            Task tsk = Definition.GetTask(at.TaskId);
            //1 select the transition for processing
            bool found = false;
            foreach (string t in at.Tokens)
            {
                Token tok = GetToken(t);
                Debug.Assert(tok.Status == TokenStatus.LOCKED_ENABLED ||
                    tok.Status == TokenStatus.LOCKED_ALLOCATED);
                if (tok.Status == TokenStatus.LOCKED_ENABLED)
                {
                    found = true;
                    break;
                }
            }
            if (found)
            {
                AfterTransitionSelected(correlationId);
            }
            IList<TaskShell> sharedTrans = GetSharedActiveTransitionsForTransition(at);
            Debug.Assert(sharedTrans.Count == 0); //after transition selected there should be no shared trans.
            foreach (string tokid in at.Tokens)
            {
                Token tok = GetToken(tokid);
                Debug.Assert(tok.Status == TokenStatus.LOCKED_ALLOCATED);
            }

            //at.TaskCompleted();
            //at.Status = TransitionStatus.COMPLETED;
            //2 retrieve data from transition
            TransferDataFromTransition(at);
            //3 cancel set handling
            if (tsk.CancelSet.Count > 0)
            {
                log.Debug("Transition {0} ({1}) has cancel set with {2} elements", tsk.Id, at.CorrelationId, tsk.CancelSet.Count);
                foreach (string placeId in tsk.CancelSet)
                {
                    IList<Token> tokensInPlace = GetTokensInPlace(placeId);
                    foreach (Token tok in tokensInPlace)
                    {
                        if (tok.Status == TokenStatus.READY ||
                            tok.Status == TokenStatus.LOCKED_ENABLED ||
                            tok.Status == TokenStatus.LOCKED_ALLOCATED ||
                            tok.Status == TokenStatus.WAITING)
                        {
                            log.Debug("Cancelling token {0} in place {1}", tok.TokenId, placeId);
                            CancelToken(tok);
                        }
                    }
                }
            }
            //4 move the tokens
            UpdateNetStatusAfterTransition(at);
            //5 notify others
            ActiveTransitionCompleted compl = new ActiveTransitionCompleted();
            compl.CorrelationId = at.CorrelationId;
            compl.InstanceId = this.InstanceId;
            compl.TaskId = at.TaskId;
            compl.TaskType = tsk.GetType().Name;
            compl.TimeStamp = DateTime.Now;
            compl.DefinitionId = this.ProcessDefinitionId;
            NotifyProcessEvent(compl);
        }