/*
        public BizFlow GetFlow(string flowId, bool loadAllInfo = true)
        {
            BizFlow flow = repoBizFlow.Query(o => o.FlowId == flowId).FirstOrDefault();
            if (flow == null)
                throw new NullReferenceException(string.Format("Flow {0} not found!", flowId));

            if (loadAllInfo)
            {
                LoadBizFlowAllInfo(flow);
            }

            return flow;
        }

        private void LoadBizFlowAllInfo(BizFlow flow)
        {
            flow.States = repoFlowState.Query(o => o.FlowId == flow.FlowId).OrderBy(o => o.Code).ToArray();
            flow.Operations = repoFlowOp.Query(o => o.FlowId == flow.FlowId).OrderBy(o => o.Code).ToArray();
            flow.Incomes = repoFlowIncome.Query(o => o.FlowId == flow.FlowId).OrderBy(o => o.Code).ToArray();
            flow.Outcomes = repoFlowOutcome.Query(o => o.FlowId == flow.FlowId).OrderBy(o => o.Code).ToArray();
            flow.NextBizFlows = repoNextFlow.Query(o => o.FlowId == flow.FlowId).ToArray();

            flow.States.ForEach(it =>
            {
                it.Operations = repoStateOp.Query(op => op.StateId == it.StateId).ToArray();
                it.Incomes = repoStateIncome.Query(op => op.StateId == it.StateId).ToArray();
                it.Outcomes = repoStateOutcome.Query(op => op.StateId == it.StateId).ToArray();

                it.Operations.ForEach(o =>
                {
                    o.Operation = flow.Operations.FirstOrDefault(op => op.OperationId == o.OperationId);
                });

                it.Incomes.ForEach(o =>
                {
                    o.Income = flow.Incomes.FirstOrDefault(d => d.IncomeId == o.IncomeId);
                });

                it.Outcomes.ForEach(o =>
                {
                    o.Outcome = flow.Outcomes.FirstOrDefault(d => d.OutcomeId == o.OutcomeId);
                });
            });

            flow.NextBizFlows.ForEach(it =>
            {
                it.FromBizFlow = flow;
                it.ToBizFlow = repoBizFlow.Query(o => o.FlowId == it.NextFlowId).FirstOrDefault();
            });
        }

        public IEnumerable<TargetFlow> LoadFlows(string clientId, string flowCodeOrTargetType, DateTime? beginTime, DateTime? endTime, bool? started, bool? completed, FlowResult? result, bool loadAllInfo = true)
        {
            var query = repoTargetFlow.Query(o => o.Flow.ClientId == clientId && (o.Flow.Code == flowCodeOrTargetType || o.Flow.TargetType == flowCodeOrTargetType));
            if (beginTime.HasValue)
            {
                query = query.Where(o => o.CreateTime >= beginTime.Value);
            }
            if (endTime.HasValue)
            {
                query = query.Where(o => o.CreateTime <= endTime.Value);
            }
            if (started.HasValue)
            {
                query = query.Where(o => o.HasStarted == started.Value);
            }
            if (completed.HasValue)
            {
                query = query.Where(o => o.HasCompleted == completed.Value);
            }
            if (result.HasValue)
            {
                query = query.Where(o => o.Result.Value == result.Value);
            }
            var tflows = query.ToArray();
            if (loadAllInfo)
            {
                tflows.ForEach(o => LoadTargetFlowAllInfo(o));
            }
            return tflows;
        }

        public TargetFlow LoadFlow(string clientId, string flowCodeOrTargetType, string targetId, bool loadAllInfo = true)
        {
            var tflowId = repoTargetFlow.Query(o => o.Flow.ClientId == clientId && (o.Flow.Code == flowCodeOrTargetType || o.Flow.TargetType == flowCodeOrTargetType) && o.TargetId == targetId).Select(o => o.TargetFlowId).FirstOrDefault();
            if (string.IsNullOrEmpty(tflowId))
                return null;

            return LoadFlow(tflowId, loadAllInfo);
        }

        public TargetFlow LoadFlow(string targetFlowId, bool loadAllInfo = true)
        {
            var tflow = repoTargetFlow.Query(o => o.TargetFlowId == targetFlowId).FirstOrDefault();
            if (tflow == null)
                return null;

            if (tflow != null && loadAllInfo)
            {
                LoadTargetFlowAllInfo(tflow);
            }

            return tflow;
        }

        private void LoadTargetFlowAllInfo(TargetFlow tflow)
        {
            tflow.Flow = GetFlow(tflow.FlowId, true);
            tflow.TreatedStates = repoTargetState.Query(o => o.TargetFlowId == tflow.TargetFlowId).ToArray();
            tflow.TreatedStates.ForEach(o =>
            {
                o.TargetIncomes = repoTargetIncome.Query(d => d.TargetStateId == o.TargetStateId).ToArray();
                o.TargetOutcomes = repoTargetOutcome.Query(d => d.TargetStateId == o.TargetStateId).ToArray();
                o.State = tflow.Flow.States.FirstOrDefault(s => s.StateId == o.StateId);
                o.Operation = tflow.Flow.Operations.FirstOrDefault(p => p.OperationId == o.OperationId);
                o.TargetIncomes.ForEach(obj =>
                {
                    obj.Income = tflow.Flow.Incomes.FirstOrDefault(d => d.IncomeId == obj.IncomeId);
                });
                o.TargetOutcomes.ForEach(obj =>
                {
                    obj.Outcome = tflow.Flow.Outcomes.FirstOrDefault(d => d.OutcomeId == obj.OutcomeId);
                });
            });
            if (!string.IsNullOrEmpty(tflow.LastTargetFlowId))
            {
                tflow.LastTargetFlow = repoTargetFlow.Query(o => o.TargetFlowId == tflow.LastTargetFlowId).FirstOrDefault();
            }
        }

        public TargetState LoadState(string targetStateId, bool loadAllInfo = true)
        {
            var tstate = repoTargetState.Query(o => o.TargetStateId == targetStateId).OrderByDescending(o => o.OperateTime).FirstOrDefault();
            if (tstate == null)
                throw new NullReferenceException(string.Format("Target Flow state {0} not found!", targetStateId));

            if (loadAllInfo)
            {
                LoadTargetStateAllInfo(tstate);
            }

            return tstate;
        }

        private void LoadTargetStateAllInfo(TargetState tstate)
        {
            tstate.TargetFlow = repoTargetFlow.Query(o => o.TargetFlowId == tstate.TargetFlowId).FirstOrDefault();
            tstate.State = repoFlowState.Query(o => o.StateId == tstate.StateId).FirstOrDefault();
            if (tstate.State != null)
            {
                tstate.State.Operations = repoStateOp.Query(o => o.StateId == tstate.StateId).ToArray();
                tstate.State.Operations.ForEach(op =>
                {
                    op.State = tstate.State;
                    op.Operation = repoFlowOp.Query(o => o.OperationId == op.OperationId).FirstOrDefault();
                });

                tstate.State.Incomes = repoStateIncome.Query(o => o.StateId == tstate.StateId).ToArray();
                tstate.State.Incomes.ForEach(obj =>
                {
                    obj.State = tstate.State;
                    obj.Income = repoFlowIncome.Query(o => o.IncomeId == obj.IncomeId).FirstOrDefault();
                });

                tstate.State.Outcomes = repoStateOutcome.Query(o => o.StateId == tstate.StateId).ToArray();
                tstate.State.Outcomes.ForEach(obj =>
                {
                    obj.State = tstate.State;
                    obj.Outcome = repoFlowOutcome.Query(o => o.OutcomeId == obj.OutcomeId).FirstOrDefault();
                });
            }
            tstate.FromTargetStates = repoNextTargetState.Query(o => o.NextTargetStateId == tstate.TargetStateId).ToList();
            tstate.FromTargetStates.ForEach(obj =>
            {
                obj.ToTargetState = tstate;
                obj.FromTargetState = repoTargetState.Query(o => o.TargetStateId == obj.TargetStateId).FirstOrDefault();
            });
            tstate.ToTargetStates = repoNextTargetState.Query(o => o.TargetStateId == tstate.TargetStateId).ToList();
            tstate.ToTargetStates.ForEach(obj =>
            {
                obj.FromTargetState = tstate;
                obj.ToTargetState = repoTargetState.Query(o => o.TargetStateId == obj.NextTargetStateId).FirstOrDefault();
            });
            tstate.TargetIncomes = repoTargetIncome.Query(o => o.TargetStateId == tstate.TargetStateId).ToList();
            tstate.TargetOutcomes = repoTargetOutcome.Query(o => o.TargetStateId == tstate.TargetStateId).ToList();
            if (!string.IsNullOrEmpty(tstate.OperationId))
                tstate.Operation = repoFlowOp.Query(o => o.OperationId == tstate.OperationId).FirstOrDefault();
        }
        */
        /*
        public IEnumerable<TargetState> GetCurrentStates(string targetFlowId, bool loadAllInfo = true)
        {
            var tflow = LoadFlow(targetFlowId, loadAllInfo);
            if (tflow == null)
                throw new NullReferenceException(string.Format("Target Flow {0} not found!", targetFlowId));

            var tstates = repoTargetState.Query(o => o.TargetFlowId == tflow.TargetFlowId && o.StateStatus == StateStatus.None).OrderByDescending(o => o.OperateTime).ToArray();
            if (tstates == null || tstates.Count() <= 0)
                tstates = repoTargetState.Query(o => o.TargetFlowId == tflow.TargetFlowId && o.State.StateType == StateType.End).ToArray();
            if (tstates == null || tstates.Count() <= 0)
                tstates = repoTargetState.Query(o => o.TargetFlowId == tflow.TargetFlowId && o.State.StateType == StateType.Begin).ToArray();

            if (loadAllInfo)
            {
                tstates.ForEach(o => LoadTargetStateAllInfo(o));
            }

            return tstates;
        }

        public IEnumerable<FlowOperation> GetNextOperations(string targetFlowId)
        {
            var tStates = GetCurrentStates(targetFlowId, false);
            List<FlowOperation> operations = new List<FlowOperation>();
            TargetState paralleTargetState = null;
            foreach(var tstate in tStates)
            {
                switch(tstate.StateStatus)
                {
                    case StateStatus.Started:
                        var ops = GetStateOperations(tstate.StateId);
                        operations.AddRange(ops);
                        break;
                    case StateStatus.Finished:
                        break;
                    case StateStatus.None:
                        var state = repoFlowState.Query(o => o.StateId == tstate.StateId).FirstOrDefault();
                        if(state.StateType == StateType.ParallelStop)
                        {
                            paralleTargetState = tstate;
                            if (!state.AllParallelStateShouldBeEnd.HasValue || !state.AllParallelStateShouldBeEnd.Value)
                            {
                                var currOps = GetStateOperations(tstate.StateId);
                                operations.AddRange(currOps);
                            }
                        }
                        break;
                }
            }
            // 添加未开始执行的并行操作
            if (paralleTargetState != null)
            {
                var nts = repoNextTargetState.Query(o => o.NextTargetStateId == paralleTargetState.TargetStateId).FirstOrDefault();
                var beginParallelTState = repoTargetState.Query(o => o.TargetStateId == nts.ParallelTargetStateId).FirstOrDefault();
                var beginParallelOpids = repoStateOp.Query(o => o.StateId == beginParallelTState.StateId).Select(o => o.OperationId);
                var startedOpids = repoTargetState.Query(o => o.TargetFlowId == paralleTargetState.TargetFlowId && beginParallelOpids.Contains(o.OperationId)).Select(o => o.OperationId);
                var notStartedOpids = beginParallelOpids.Except(startedOpids);
                var notStartedOps = repoFlowOp.Query(o => notStartedOpids.Contains(o.OperationId)).ToArray();
                operations.AddRange(notStartedOps);
            }

            return operations;
        }

        private IEnumerable<FlowOperation> GetStateOperations(string stateId)
        {
            var opids = repoStateOp.Query(o => o.StateId == stateId).Select(o => o.OperationId);
            var ops = repoFlowOp.Query(o => opids.Contains(o.OperationId)).ToArray();
            return ops;
        }
        */
        public TargetState StartFlow(StartFlowInfo info)
        {
            BizFlow flow = repoBizFlow.Query(o => o.ClientId == info.ClientId && (o.Code == info.FlowCodeOrTargetType || o.TargetType == info.FlowCodeOrTargetType)).FirstOrDefault();
            if (flow == null)
                throw new NullReferenceException(string.Format("Flow {0} not found!", info.FlowCodeOrTargetType));

            TargetFlow tflow = repoTargetFlow.Query(o => o.FlowId == flow.FlowId && o.TargetId == info.TargetId).FirstOrDefault();
            if (tflow == null)
            {
                tflow = new TargetFlow()
                {
                    TargetFlowId = idGenerator.NewId(),
                    FlowId = flow.FlowId,
                    TargetId = info.TargetId,
                    FlowCode = info.TargetFlowCode,
                    Title = info.FlowTitle,
                    Message = info.FlowMessage,
                    HasStarted = false,
                    HasCompleted = false,
                    CreatorId = info.UserId,
                    CreatorName = info.UserName,
                    CreateTime = info.OperationTime
                };

                if (!string.IsNullOrEmpty(info.LastTargetFlowId))
                {
                    tflow.LastTargetFlowId = info.LastTargetFlowId;

                    NextTargetFlowCreatedMessage msg = new NextTargetFlowCreatedMessage()
                    {
                        FinishedTargetFlow = repoTargetFlow.Query(o => o.TargetFlowId == info.LastTargetFlowId).FirstOrDefault(),
                        CreatedTargetFlow = tflow,
                        OperateTime = info.OperationTime,
                        OperatorId = info.UserId,
                        OperatorName = info.UserName
                    };
                    try
                    {
                        MessageManager.Publish<NextTargetFlowCreatedMessage>(msg);
                    }
                    catch (Exception ex)
                    {
                        //throw ex;
                    }
                }

                repoTargetFlow.Insert(tflow);
            }

            var startState = repoFlowState.Query(o => o.FlowId == tflow.FlowId && o.StateType == StateType.Begin).FirstOrDefault();
            if (startState == null)
                throw new NullReferenceException(string.Format("Flow {0} has no start state defined.", tflow.FlowId));

            TargetState tstate = new TargetState()
            {
                TargetStateId = idGenerator.NewId(),
                TargetFlowId = tflow.TargetFlowId,
                StateId = startState.StateId,
                Title = info.StartTitle,
                Message = info.StartMessage,
                StateStatus = StateStatus.Started,
                OperateTime = info.OperationTime,
                OperatorId = info.UserId,
                OperatorName = info.UserName
            };

            if (startState.IntervalType.HasValue)
            {
                if (startState.ResponseIntervalValue.HasValue)
                    tstate.ResponseExpiryTime = info.OperationTime.Interval(startState.IntervalType, startState.ResponseIntervalValue);
                if (startState.TreatIntervalValue.HasValue)
                    tstate.TreatExpiryTime = info.OperationTime.Interval(startState.IntervalType, startState.ResponseIntervalValue);
            }

            tflow.HasStarted = true;

            try
            {
                trans.BeginTransaction();
                repoTargetFlow.Update(tflow);
                repoTargetState.Insert(tstate);
                trans.Commit();

            }
            catch (Exception ex)
            {
                trans.Rollback();
                throw ex;
            }

            TargetStateChangedMessage tscMsg = new TargetStateChangedMessage()
            {
                OldTargetStates = null,
                NewTargetState = tstate,
                OperationId = null,
                DataOperation = DataOperation.Insert,
                OperateTime = info.OperationTime,
                OperatorId = info.UserId,
                OperatorName = info.UserName
            };
            try
            {
                MessageManager.Publish<TargetStateChangedMessage>(tscMsg);
            }
            catch (Exception ex)
            {
                //throw ex;
            }

            //tstate.State = startState;
            //tstate.TargetFlow = tflow;
            //tstate.TargetFlow.Flow = flow;
            return tstate;
        }
 private TargetFlowInfo GetTargetFlow(TargetFlow tflow)
 {
     if (tflow != null)
     {
         if (!string.IsNullOrEmpty(tflow.LastTargetFlowId))
             tflow.LastTargetFlow = repoTargetFlow.Query(o => o.TargetFlowId == tflow.LastTargetFlowId).FirstOrDefault();
         TargetFlowInfo tfi = new TargetFlowInfo();
         tfi.TargetFlow = tflow;
         tfi.BizFlow = GetBizFlow(tfi.TargetFlow.FlowId);
         tfi.TargetStates = repoTargetState.Query(o => o.TargetFlowId == tflow.TargetFlowId).ToArray();
         tfi.TargetIncomes = repoTargetIncome.Query(o => o.TargetState.TargetFlowId == tflow.TargetFlowId).ToArray();
         tfi.TargetOutcomes = repoTargetOutcome.Query(o => o.TargetState.TargetFlowId == tflow.TargetFlowId).ToArray();
         tfi.NextTargetStates = repoNextTargetState.Query(o => o.FromTargetState.TargetFlowId == tflow.TargetFlowId).ToArray();
         return tfi;
     }
     return null;
 }