private static void CalculatePlotTimesNotStarted(AsyncDashboardNotStartedViewModel.DatesSectionViewModel datesTab)
        {
            var plotTimes = new List<AsyncDashboardNotStartedViewModel.DatesSectionViewModel.PlotTime>();

            var dtCurrent = DateTime.Now;

            // 0. Start time
            if (datesTab.StartDate.HasValue) plotTimes.Add(new AsyncDashboardNotStartedViewModel.DatesSectionViewModel.PlotTime("Start Date", datesTab.StartDate.Value));

            // 1. Current time
            plotTimes.Add(new AsyncDashboardNotStartedViewModel.DatesSectionViewModel.PlotTime("Now", dtCurrent));

            if (datesTab.StartDate.HasValue)
            {
                datesTab.MinimumTime = TimeUtils.Min(dtCurrent, datesTab.StartDate.Value);
            }

            // 2. Code freeze time
            var codeFreeze = datesTab.CodeFreeze;
            if (codeFreeze.HasValue) plotTimes.Add(new AsyncDashboardNotStartedViewModel.DatesSectionViewModel.PlotTime("Code Freeze", codeFreeze.Value));

            datesTab.MinimumTime = (datesTab.StartDate.HasValue
                                        ? TimeUtils.Min(dtCurrent, datesTab.StartDate.Value)
                                        : (codeFreeze.HasValue
                                               ? TimeUtils.Min(dtCurrent, codeFreeze.Value)
                                               : dtCurrent)).AddDays(-7);

            // 3. Release date
            if (datesTab.Rollout != null)
            {
                plotTimes.Add(new AsyncDashboardNotStartedViewModel.DatesSectionViewModel.PlotTime("Release", datesTab.Rollout.Value));

                datesTab.MaximumTime = TimeUtils.Max(dtCurrent, datesTab.Rollout.Value).AddDays(7);

                datesTab.PlotTimes = plotTimes.OrderBy(plotTime => plotTime.DateTime).ToArray();
            }
        }
        public ActionResult AsyncDashboardNotStarted(int projectId, int milestoneId, int? subProjectParentCaseId = null)
        {
            var today = DateTime.Today;

            var model = new AsyncDashboardNotStartedViewModel();
            model.Setup(projectId, milestoneId, subProjectParentCaseId);

            string cacheKey = MsCacheKey.GenCaseSetKey(projectId, milestoneId, subProjectParentCaseId);
            model.MsCache.CreatedAt = MsCache.GetObjectExpirationTime(cacheKey);

            UpdateViewBagForProjectLayout(model);

            IList<Case> cases = new List<Case>();

            foreach (var casesWithMilestone in model.CaseSet.Cases.Where(casesWithMilestone => casesWithMilestone.IndexFixFor == milestoneId))
            {
                cases.Add(casesWithMilestone);
            }

            model.CasesSection.Total = cases.Count();
            model.CasesSection.Active = cases.Count(CaseUtils.IsActive);
            model.CasesSection.Resolved = cases.Count(CaseUtils.IsResolved);
            model.CasesSection.Verified = cases.Count(CaseUtils.IsResolvedVerified);
            model.CasesSection.Closed = cases.Count(CaseUtils.IsClosed);

            model.TimeSection.TotalEstimated = cases.Sum(@case => @case.HoursCurrentEstimate.GetValueOrDefault(0));
            model.TimeSection.Elapsed = cases.Sum(@case => @case.HoursElapsed.GetValueOrDefault(0));
            model.TimeSection.ActiveEstimated = cases.Where(CaseUtils.IsActive).Sum(@case => @case.HoursCurrentEstimate.GetValueOrDefault(0));
            model.TimeSection.ActiveRemaining = cases.Where(CaseUtils.IsActive).Sum(@case => Math.Max(
                0, @case.HoursCurrentEstimate.GetValueOrDefault(0) - @case.HoursElapsed.GetValueOrDefault(0)));

            model.EstimatesSection.WithEstimates = cases.Where(CaseUtils.IsActive).Count(@case => @case.HoursCurrentEstimate.GetValueOrDefault(0) > 0);
            model.EstimatesSection.WithoutEstimates = cases.Count(CaseUtils.IsActive) - model.EstimatesSection.WithEstimates;

            //at this point model.EstimatesSection.WithoutEstimates can be negative.
            //it is possible when cases which has subcases (IndexBugChildren.Count>0) are estimated too
            if (model.EstimatesSection.WithoutEstimates < 0)
                model.EstimatesSection.WithoutEstimates = 0;
            model.EstimatesSection.GoingOverEstimate = cases.Where(CaseUtils.IsActive).Count(
                @case => @case.HoursElapsed.GetValueOrDefault(0) > @case.HoursCurrentEstimate.GetValueOrDefault(0));

            model.AccuracySection.EstimatedTime = cases.Where(@case => CaseUtils.IsResolved(@case) || CaseUtils.IsClosed(@case)).Sum(@case => @case.HoursCurrentEstimate.GetValueOrDefault(0));
            model.AccuracySection.ActualTime = cases.Where(@case => CaseUtils.IsResolved(@case) || CaseUtils.IsClosed(@case)).Sum(@case => @case.HoursElapsed.GetValueOrDefault(0));

            Case projectParentCase = null;
            if (subProjectParentCaseId.HasValue)
            {
                var client = FogBugzGateway.GetClientForParallel();
                projectParentCase = client.GetCase(subProjectParentCaseId.Value);
            }

            foreach (var casesWithMilestone in model.CaseSet.Cases.Where(casesWithMilestone => casesWithMilestone.IndexFixFor == milestoneId))
            {
                cases.Add(casesWithMilestone);
            }

            var fbSchedule = FogBugzGateway.GetFbSchedule();

            if (model.CaseSet.Milestone != null)
            {
                model.DatesSection.StartDate = model.CaseSet.Milestone.DateStart;
                if (model.DatesSection.StartDate.HasValue)
                {
                    model.DatesSection.StartDateDaysRemaining = DateUtils.Difference(today, model.DatesSection.StartDate.Value);
                }

                model.DatesSection.CodeFreeze = (projectParentCase != null && projectParentCase.DateDue != null) ? projectParentCase.DateDue : MilestoneUtils.CodeFreezeCalculate(projectId, model.Milestone, model.SubProjectParentCaseId, model.CaseSet.Milestone.Name, CodeFreezeSubstringRegex);
                if (model.DatesSection.CodeFreeze.HasValue)
                {
                    model.DatesSection.CodeFreezeDaysRemaining = DateUtils.Difference(today, model.DatesSection.CodeFreeze.Value);

                    model.DatesSection.CodeFreezeHolidaysBefore = fbSchedule.GetSiteScheduledDays(today, model.DatesSection.CodeFreeze.Value).TimeOffRanges;
                }

                model.DatesSection.Rollout = projectParentCase != null ? (model.CaseSet.Milestone.Name.Contains("CF") ? DateUtils.GetCodeFreezeFromName(model.CaseSet.Milestone.Name, CodeFreezeSubstringRegex) : null) : model.CaseSet.Milestone.DateRelease;
                if (model.DatesSection.Rollout.HasValue)
                {
                    model.DatesSection.RolloutDaysRemaining = DateUtils.Difference(today, model.DatesSection.Rollout.Value);
                    model.DatesSection.RolloutHolidaysBefore = fbSchedule.GetSiteScheduledDays(today, model.DatesSection.Rollout.Value).TimeOffRanges;
                }

                CalculatePlotTimesNotStarted(model.DatesSection);

                model.DatesSection.IsActiveProject = today >= model.DatesSection.StartDate && today <= model.DatesSection.Rollout;
            }

            var persons = FogBugzGateway.GetPersons();
            var isLoadedByAdmin = FogBugzGateway.GetIsScheduleLoadedByAdmin();

            var projectStatusCalculator = new ProjectStatusCalculator(model.CaseSet, persons.ToList(), isLoadedByAdmin, fbSchedule);
            model.ProjectStatus = projectStatusCalculator.GetProjectStatus();

            return PartialView("_AsyncDashboardNotStarted", model);
        }