/// <summary>
 /// set event new schedule created
 /// </summary>
 /// <param name="e"></param>
 private void OnNewScheduleCreated(SchedulingInformationEventArgs e)
 {
     if (NewScheduleCreatedEvent != null)
     {
         NewScheduleCreatedEvent(this, e);
     }
 }
        //==================================
        #endregion

        #region Scheduling algorithms
        //==================================
        /// <summary>
        /// The main method of creating scheduler
        /// </summary>
        private void Schedule(bool reschedule = false)
        {
            lock (_unscheduledRequests)
            {
                lock (_scheduledRequests)
                {
                    // Set need reschedule to false in order to avoid more schedule from other threads
                    _needReschedule = false;

                    #region Manage history and find services to schedule
                    // ------------------------------------

                    // Move pending uninitialized services to the unscheduled list so they can be rescheduled
                    foreach (ServiceInstance request in _scheduledRequests.RemoveNotActivated())
                    {
                        if (request.SchedulingInfo.RequestedTime + request.SchedulingInfo.MaxDeviationAfter > DateTime.Now)
                        {
                            _unscheduledRequests.Add(request);
                        }
                        else
                        {
                            AsLockable(request).Unlock(_instanceLock);
                            request.SchedulingInfo.SchedulingStatus = SchedulingStatus.CouldNotBeScheduled;
                            AsLockable(request).Lock(_instanceLock);
                        }
                    }

                    // Get Services for next time line
                    foreach (ServiceInstance request in GetServicesForTimeLine(reschedule))
                    {
                        _unscheduledRequests.Add(request);
                    }

                    // Copy unscheduled requests to an ordered list
                    var servicesForNextTimeLine = new List <ServiceInstance>(_unscheduledRequests
                                                                             .OrderBy(schedulingdata => schedulingdata.SchedulingInfo.RequestedTime));

                    // ------------------------------------
                    #endregion

                    #region Find Match services
                    // ------------------------------------

                    //Same services or same services with same profile
                    foreach (ServiceInstance serviceInstance in servicesForNextTimeLine)
                    {
                        //Get all services with same configurationID
                        var requestsWithSameConfiguration = _scheduledRequests.GetWithSameConfiguration(serviceInstance);

                        //Get all services with same profileID
                        var requestsWithSameProfile = _scheduledRequests.GetWithSameProfile(serviceInstance);

                        //Find the first available time this service with specific service and profile
                        TimeSpan avgExecutionTime = GetAverageExecutionTime(serviceInstance.Configuration.ServiceName, Convert.ToInt32(serviceInstance.Configuration.Profile.Parameters["AccountID"]), _percentile);

                        DateTime baseStartTime       = (serviceInstance.SchedulingInfo.RequestedTime < DateTime.Now) ? DateTime.Now : serviceInstance.SchedulingInfo.RequestedTime;
                        DateTime baseEndTime         = baseStartTime.Add(avgExecutionTime);
                        DateTime calculatedStartTime = baseStartTime;
                        DateTime calculatedEndTime   = baseEndTime;

                        bool found = false;
                        while (!found)
                        {
                            IOrderedEnumerable <ServiceInstance> whereToLookNext = null;

                            int countedPerConfiguration = requestsWithSameConfiguration.Count(s => (calculatedStartTime >= s.SchedulingInfo.ExpectedStartTime && calculatedStartTime <= s.SchedulingInfo.ExpectedEndTime) || (calculatedEndTime >= s.SchedulingInfo.ExpectedStartTime && calculatedEndTime <= s.SchedulingInfo.ExpectedEndTime));
                            if (countedPerConfiguration < serviceInstance.Configuration.Limits.MaxConcurrentGlobal)
                            {
                                int countedPerProfile = requestsWithSameProfile.Count(s => (calculatedStartTime >= s.SchedulingInfo.ExpectedStartTime && calculatedStartTime <= s.SchedulingInfo.ExpectedEndTime) || (calculatedEndTime >= s.SchedulingInfo.ExpectedStartTime && calculatedEndTime <= s.SchedulingInfo.ExpectedEndTime));
                                if (countedPerProfile < serviceInstance.Configuration.Limits.MaxConcurrentPerProfile)
                                {
                                    AsLockable(serviceInstance).Unlock(_instanceLock);
                                    serviceInstance.SchedulingInfo.ExpectedStartTime = calculatedStartTime;
                                    serviceInstance.SchedulingInfo.ExpectedEndTime   = calculatedEndTime;
                                    serviceInstance.SchedulingInfo.SchedulingStatus  = Services.SchedulingStatus.Scheduled;
                                    AsLockable(serviceInstance).Lock(_instanceLock);

                                    found = true;
                                }
                                else
                                {
                                    whereToLookNext = requestsWithSameProfile;
                                }
                            }
                            else
                            {
                                whereToLookNext = requestsWithSameConfiguration;
                            }

                            if (!found)
                            {
                                if (whereToLookNext == null)
                                {
                                    throw new Exception("This should not have happened.");
                                }

                                calculatedStartTime = whereToLookNext.Where(s => s.SchedulingInfo.ExpectedEndTime >= calculatedStartTime).Min(s => s.SchedulingInfo.ExpectedEndTime);
                                if (calculatedStartTime < DateTime.Now)
                                {
                                    calculatedStartTime = DateTime.Now;
                                }

                                //Get end time
                                calculatedEndTime = calculatedStartTime.Add(avgExecutionTime);

                                ////remove unfree time from servicePerConfiguration and servicePerProfile
                                if (calculatedStartTime <= _timeLineTo)
                                {
                                    requestsWithSameConfiguration = from s in requestsWithSameConfiguration
                                                                    where s.SchedulingInfo.ExpectedEndTime > calculatedStartTime
                                                                    orderby s.SchedulingInfo.ExpectedStartTime
                                                                    select s;

                                    requestsWithSameProfile = from s in requestsWithSameProfile
                                                              where s.SchedulingInfo.ExpectedEndTime > calculatedStartTime
                                                              orderby s.SchedulingInfo.ExpectedStartTime
                                                              select s;
                                }
                            }
                        }

                        if (serviceInstance.SchedulingInfo.ExpectedDeviation <= serviceInstance.SchedulingInfo.MaxDeviationAfter || serviceInstance.SchedulingInfo.MaxDeviationAfter == TimeSpan.Zero)
                        {
                            AsLockable(serviceInstance).Unlock(_instanceLock);
                            serviceInstance.SchedulingInfo.SchedulingStatus = SchedulingStatus.Scheduled;
                            serviceInstance.StateChanged += new EventHandler(Instance_StateChanged);
                            AsLockable(serviceInstance).Lock(_instanceLock);

                            // Legacy stuff
                            //TODO: MAXEXECUTIONTIME
                            TimeSpan maxExecutionTime = TimeSpan.FromMilliseconds(avgExecutionTime.TotalMilliseconds * double.Parse(AppSettings.Get(this, "MaxExecutionTimeProduct")));
                            serviceInstance.Configuration.Limits.MaxExecutionTime = maxExecutionTime;
                            _scheduledRequests.Add(serviceInstance);
                            _unscheduledRequests.Remove(serviceInstance);
                        }
                    }
                    #endregion
                }


                SchedulingInformationEventArgs args = new SchedulingInformationEventArgs();
                args.ScheduleInformation = new List <ServiceInstance>();
                foreach (var scheduleService in _scheduledRequests)
                {
                    args.ScheduleInformation.Add(scheduleService);
                }
                OnNewScheduleCreated(args);
                NotifyServicesToRun();
            }
        }