public bool IsTimeToRun(
			Job job,
			DateTime now,
			DateTime? minPossibleTime = null
		)
		{
			DateTime searchOrigin = job.LastRan ?? job.Settings.StartDate.Subtract(new TimeSpan(1));

			if (minPossibleTime != null && minPossibleTime > searchOrigin)
			{
				searchOrigin = minPossibleTime.Value;
			}

			if (now < searchOrigin)
			{
				return false;
			}

			DateTime nextAfterLast;

			if (!job.Settings.GetNextTime(searchOrigin, out nextAfterLast))
			{
				return false;
			}

			if (nextAfterLast > now)
			{
				return false;
			}

			return true;
		}
		private void Run(
			Job      job,
			DateTime executeTime,
			Action   action)
		{
			RunJob(job, executeTime, action);
			OnJobStarted(job, executeTime);
		}
		public void SaveScheduleSettings(long templateNodeId, Job job)
		{
			ScheduleSettingsRow row = this.CreateRow(templateNodeId, job);

			this._currentStorage.ScheduleSettingsTable.InsertOrUpdateRowForSure(row);

			Cache.Remove(GetCacheKey(templateNodeId));
		}
		public void RunIfTime(
			Job       job,
			Action    action = null,
			DateTime? minPossibleTime = null
		)
		{
			DateTime now = DateTime.Now;

			RunIfTime(job, now, action, minPossibleTime);
		}
		private void OnJobStarted(
			Job      job,
			DateTime executeTime
		)
		{
			var handler = JobStarted;

			if (handler != null)
			{
				handler(this, new JobEventArgs(job, executeTime));
			}
		}
		public EmailNotificationTask(
			List<EmailNotificationTask> allEmailTask,
			Job                         job,
			VisualizeProcessor          visualizeProcessor
		)
		{
			this._allEmailTask              = allEmailTask;
			this._job                       = job;
			this._visualizeProcessor        = visualizeProcessor;
			this._linkedResources           = new List<LinkedResource>();
			this._messageHtml               = string.Empty;
			this._EmailNotificationSettings = Program.Model.Settings.EmailNotificationSettings;

			this._allEmailTask.Add(this);
		}
		public void TestMethod1()
		{
			AbstractJobProcessor jobProcessor = new SimpleJobProcessor();
			var ranCount = 0;

			var job = new Job();
			jobProcessor.JobStarted += delegate { ranCount++; };

			job.Settings = new ScheduleSettings();
			job.Settings.StartDate = new DateTime(1980, 2, 26);
			job.Settings.EndDate = new DateTime(2980, 2, 26);
			job.Settings.HasEndDate = true;
			job.Settings.SetFrequency(ReccurPeriodTimeUnit.Daily, 1);

			job.Settings.DailyFrequency.PeriodTimeUnit = DailyFrequency.TimeUnit.Hour;
			job.Settings.DailyFrequency.PeriodTimeUnitCount = 1;

			jobProcessor.RunIfTime(job, new DateTime(year: 1980, month: 2, day: 25, hour: 23, minute: 30, second: 0));
			Assert.AreEqual(0, ranCount);

			jobProcessor.RunIfTime(job, new DateTime(1980, 2, 26, 0, 30, 0));
			Assert.AreEqual(1, ranCount);
			jobProcessor.RunIfTime(job, new DateTime(1980, 2, 26, 0, 30, 0));
			Assert.AreEqual(1, ranCount);

			jobProcessor.RunIfTime(job, new DateTime(1980, 2, 26, 1, 0, 0));
			Assert.AreEqual(2, ranCount);

			jobProcessor.RunIfTime(job, new DateTime(1979, 2, 26, 0, 30, 0));
			Assert.AreEqual(2, ranCount);

			jobProcessor.RunIfTime(job, new DateTime(2000, 2, 26, 0, 30, 0));
			Assert.AreEqual(3, ranCount);

			DateTime nextDateTime;

			Assert.IsTrue(job.Settings.GetNextTime(new DateTime(1980, 2, 25), out nextDateTime));
			Assert.AreEqual(new DateTime(1980, 2, 26), nextDateTime);

			Assert.IsTrue(job.Settings.GetNextTime(new DateTime(1980, 2, 26), out nextDateTime));
			Assert.AreEqual(new DateTime(1980, 2, 26, 1, 0, 0), nextDateTime);

			Assert.IsFalse(job.Settings.GetNextTime(new DateTime(2980, 2, 27, 0, 0, 0), out nextDateTime));
		}
		public void TestComplicated()
		{
			AbstractJobProcessor jobProcessor = new SimpleJobProcessor();
			var job = new Job();

			var runTimes = new List<DateTime>();
			jobProcessor.JobStarted += (object s, JobEventArgs a) => runTimes.Add(a.DateTime);

			job.Settings = new ScheduleSettings();
			job.Settings.StartDate = new DateTime(2014, 11, 20);
			job.Settings.SetFrequency(ReccurPeriodTimeUnit.Weekly, 1);
			job.Settings.ActiveWeekDays.Add(DayOfWeek.Monday);
			job.Settings.ActiveWeekDays.Add(DayOfWeek.Wednesday);

			job.Settings.DailyFrequency.PeriodTimeUnit = DailyFrequency.TimeUnit.Hour;
			job.Settings.DailyFrequency.PeriodTimeUnitCount = 7;
			job.Settings.DailyFrequency.EndingAt = new TimeSpan(hours: 20, minutes: 0, seconds: 0);

			job.Settings.EndDate = new DateTime(2015, 5, 10);
			job.Settings.HasEndDate = true;

			var allDates = new HashSet<DateTime>();

			for (var dt = new DateTime(2014, 10, 20).AddMinutes(-1);
				dt < new DateTime(2015, 10, 2); dt = dt.AddMinutes(15))
			{
				jobProcessor.RunIfTime(job, dt);

				if (dt < job.Settings.StartDate)
					continue;

				if (job.Settings.ActiveWeekDays.Contains(dt.DayOfWeek) && !allDates.Contains(dt.Date)
					&& dt <= job.Settings.EndDate
					&& dt.TimeOfDay <= job.Settings.DailyFrequency.EndingAt.Value)
				{
					allDates.Add(dt.Date);
				}
			}

			var runDates = runTimes.Select(dt => dt.Date).Distinct().ToList();

			Assert.IsTrue(allDates.SequenceEqual(runDates));
			Assert.AreEqual(runDates.Count * 3, runTimes.Count);
		}
		public void RunIfTime(
			Job       job,
			DateTime  now,
			Action    action = null,
			DateTime? minPossibleTime = null
		)
		{
			if (IsTimeToRun(job, now, minPossibleTime))
			{
				if (action == null)
				{
					Run(job, now, () => { });
				}
				else
				{
					Run(job, now, action);
				}
			}
		}
		protected override void RunJob(
			Job      job,
			DateTime executeTime,
			Action   action
		)
		{
			long settingsId = job.SettingsId;

			Task.Factory.StartNew(() =>
			{
				this._storage.ScheduleSettingsTable.SaveLastRun(
					settingsId,
					executeTime
				);

				action();

				job.LastRan = executeTime;

				this._storage.NodeInstances.SaveScheduledUpdateTime(
					job.NodeInfo,
					DateTime.Now
				);
			});
		}
		private TreeNode FindJobbedUiTreeNode(Job job)
		{
			var nodesList = new List<TreeNode>();

			foreach (TreeNode currentNode in treeTemplate.Nodes)
			{
				nodesList.AddRange(GetEnabledTreenodesForTimingTick(currentNode));
			}

			foreach (var treeNode in nodesList)
			{
				var node = treeNode.Tag as ConcreteTemplateNodeDefinition;

				if (node == null)
				{
					continue;
				}

				TemplateNodeUpdateJob tjob = node.TemplateNode.GetRefreshJob(true).FirstOrDefault(j => j == job);

				if (tjob != null && !tjob.IsEmpty())
				{
					return treeNode;
				}
			}

			return null;
		}
		private ScheduleSettingsRow CreateRow(long templateNodeId, Job job)
		{
			return new ScheduleSettingsRow
			{
				TemplateNodeId                = templateNodeId,
				TemplateNodesScheduleUserId   = job.Settings.Id,
				TemplateNodesScheduleUserName = job.Settings.Name,
				IsSendMessage                 = job.Settings.IsSendMessage,
				Enabled                       = job.Settings.Enabled,
				ActiveWeekDays                = string.Join(",", job.Settings.ActiveWeekDays),
				DayNumber                     = job.Settings.DayOfMonth.DayNumber,
				EndDate                       = job.Settings.HasEndDate ? (DateTime?)job.Settings.EndDate : null,
				EndingAt                      = job.Settings.DailyFrequency.EndingAt.ToTicks(),
				LastRan                       = job.LastRan,
				OccursOnceDateTime            = job.Settings.OccursOnceDateTime,
				OccursOnceDateTimeEnabled     = job.Settings.OccursOnceDateTimeEnabled,
				OccursOnceTime                = job.Settings.DailyFrequency.OccursOnceTime.ToTicks(),
				PeriodTimeUnit                = job.Settings.DailyFrequency.PeriodTimeUnit.HasValue ? (int?)job.Settings.DailyFrequency.PeriodTimeUnit.Value : null,
				PeriodTimeUnitCount           = job.Settings.DailyFrequency.PeriodTimeUnitCount,
				StartDate                     = job.Settings.StartDate,
				StartingAt                    = job.Settings.DailyFrequency.StartingAt.ToTicks(),
				TimeUnit                      = job.Settings.ReccurPeriod.With(r => (int)r.TimeUnit),
				TimeUnitCount                 = job.Settings.ReccurPeriod.With(r => r.TimeUnitCount),
				OccursOnce                    = job.Settings.DailyFrequency.OccursOnce
			};
		}
		public void TestOvernight()
		{
			AbstractJobProcessor jobProcessor = new SimpleJobProcessor();

			var runs = new List<DateTime>();
			var job = new Job();
			jobProcessor.JobStarted += (object s, JobEventArgs a) => runs.Add(a.DateTime);

			job.Settings = new ScheduleSettings();
			job.Settings.StartDate = new DateTime(2014, 11, 20);
			job.Settings.ReccurPeriod = new TimeUnitBasedPeriod(ReccurPeriodTimeUnit.Daily, 1);

			job.Settings.DailyFrequency.PeriodTimeUnit = DailyFrequency.TimeUnit.Minute;
			job.Settings.DailyFrequency.PeriodTimeUnitCount = 1;

			DateTime next;
			Assert.IsTrue(job.Settings.GetNextTime(new DateTime(2014, 11, 21, 23, 58, 30), out next));
			Assert.AreEqual(new DateTime(2014, 11, 21, 23, 59, 0), next);

			Assert.IsTrue(job.Settings.GetNextTime(new DateTime(2014, 11, 21, 23, 59, 30), out next));
			Assert.AreEqual(new DateTime(2014, 11, 22, 0, 0, 0), next);

			job.Settings.DailyFrequency.StartingAt = TimeSpan.FromSeconds(5);
			job.Settings.DailyFrequency.EndingAt = new TimeSpan(23, 59, 59, 59, 9999);

			Assert.IsTrue(job.Settings.GetNextTime(new DateTime(2014, 11, 21, 23, 58, 30), out next));
			Assert.AreEqual(new DateTime(2014, 11, 21, 23, 59, 05), next);

			Assert.IsTrue(job.Settings.GetNextTime(new DateTime(2014, 11, 21, 23, 59, 30), out next));
			Assert.AreEqual(new DateTime(2014, 11, 22, 0, 0, 05), next);

			for (var dt = new DateTime(2014, 11, 21, 23, 57, 30);
				dt < new DateTime(2014, 11, 22, 0, 1, 30);
				dt = dt.AddSeconds(1))
			{
				jobProcessor.RunIfTime(job, dt);
			}

			Assert.AreEqual(5, runs.Count);
			Assert.AreEqual(runs.Last().Date, new DateTime(2014, 11, 22));

			runs.Clear();

			// every seccond for several days
			for (var dt = new DateTime(2014, 11, 22, 23, 57, 30);
				dt < new DateTime(2014, 12, 15, 9, 10, 30);
				dt = dt.AddSeconds(1))
			{
				jobProcessor.RunIfTime(job, dt);
			}

			var runDated = runs.Select(t => t.Date).Distinct().ToList();

			//Assert.AreEqual(`);

			Assert.AreEqual(runs.Last().Date, new DateTime(2014, 12, 15));

			for (var i = 0; i < runs.Count - 1; i++)
			{
				Assert.IsTrue(runs[i + 1] - runs[i] < new TimeSpan(0, 1, 1));
			}
		}
		public void TestRunOnce()
		{
			var job = new Job();

			job.Settings = new ScheduleSettings();
			job.Settings.OccursOnceDateTime = new DateTime(2015, 1, 15, 9, 0, 0);
			job.Settings.OccursOnceDateTimeEnabled = true;

			DateTime next;
			Assert.IsTrue(job.Settings.GetNextTime(new DateTime(2015, 1, 15, 8, 0, 0), out next));
			Assert.AreEqual(new DateTime(2015, 1, 15, 9, 0, 0), next);
			Assert.IsFalse(job.Settings.GetNextTime(new DateTime(2015, 1, 15, 10, 0, 0), out next));
		}
		public void TestMonthly()
		{
			AbstractJobProcessor jobProcessor = new SimpleJobProcessor();

			var runs = new List<DateTime>();
			var job = new Job();
			jobProcessor.JobStarted += (object s, JobEventArgs a) => runs.Add(a.DateTime);

			job.Settings = new ScheduleSettings();
			job.Settings.StartDate = new DateTime(2014, 11, 20);
			job.Settings.ReccurPeriod = new TimeUnitBasedPeriod(ReccurPeriodTimeUnit.Monthly, 2);
			job.Settings.DayOfMonth.DayNumber = 15;

			job.Settings.DailyFrequency.OccursOnceTime = new TimeSpan(hours: 8, minutes: 0, seconds: 0);
			job.Settings.DailyFrequency.OccursOnce = true;

			jobProcessor.RunIfTime(job, new DateTime(2014, 11, 20));
			Assert.AreEqual(0, runs.Count);
			jobProcessor.RunIfTime(job, new DateTime(2015, 1, 15, 7, 0, 0));
			Assert.AreEqual(0, runs.Count);

			DateTime nextRun;
			Assert.IsTrue(job.Settings.GetNextTime(new DateTime(2015, 1, 15, 7, 0, 0), out nextRun));
			Assert.AreEqual(new DateTime(2015, 1, 15, 8, 0, 0), nextRun);

			Assert.IsTrue(job.Settings.GetNextTime(new DateTime(2015, 1, 14, 8, 0, 0), out nextRun));
			Assert.AreEqual(new DateTime(2015, 1, 15, 8, 0, 0), nextRun);

			Assert.IsTrue(job.Settings.GetNextTime(new DateTime(2015, 1, 14, 9, 0, 0), out nextRun));
			Assert.AreEqual(new DateTime(2015, 1, 15, 8, 0, 0), nextRun);

			jobProcessor.RunIfTime(job, new DateTime(2015, 1, 15, 9, 0, 0));
			Assert.AreEqual(1, runs.Count);
			jobProcessor.RunIfTime(job, new DateTime(2015, 1, 20, 0, 0, 0));
			Assert.AreEqual(1, runs.Count);
			jobProcessor.RunIfTime(job, new DateTime(2015, 1, 20, 10, 0, 0));
			Assert.AreEqual(1, runs.Count);
		}
		protected abstract void RunJob(
			Job      job,
			DateTime executeTime,
			Action   action
		);
		public JobEventArgs(Job job, DateTime dateTime)
		{
			this.Job      = job;
			this.DateTime = dateTime;
		}
		protected override void RunJob(Job job, DateTime executeTime, Action action)
		{
			action();

			job.LastRan = executeTime;
		}
		public void AddEmailTask(ConcreteTemplateNodeDefinition definition, Job job)
		{
			Func<NodeDataProvider> getNodeDataProviderFunc =
				() => new NodeDataProvider(this._model, definition);

			Task<NodeDataProvider> queryReportTask = Task.Factory.StartNew(getNodeDataProviderFunc);

			EmailNotificationTask emlTask = new EmailNotificationTask(
				this._allEmailTask,
				job,
				this._model.VisualizeProcessor
			);

			queryReportTask.ContinueWith(emlTask.onQueryResult);
		}
		public void TestDailyHourly()
		{
			AbstractJobProcessor jobProcessor = new SimpleJobProcessor();

			var runTimes = new List<DateTime>();
			var job = new Job();
			jobProcessor.JobStarted += (object s, JobEventArgs a) => runTimes.Add(a.DateTime);

			job.Settings = new ScheduleSettings();
			job.Settings.StartDate = new DateTime(2014, 11, 20);
			job.Settings.ReccurPeriod = new TimeUnitBasedPeriod(ReccurPeriodTimeUnit.Daily, 1);

			job.Settings.DailyFrequency.PeriodTimeUnit = DailyFrequency.TimeUnit.Hour;
			job.Settings.DailyFrequency.PeriodTimeUnitCount = 1;

			for (var dt = new DateTime(2014, 11, 20); dt < new DateTime(2014, 11, 23); dt = dt.AddMinutes(0.5))
			{
				jobProcessor.RunIfTime(job, dt);
			}

			Assert.AreEqual(3 * 24, runTimes.Count);
		}