/**
         * Returns a list of start dates in the specified period represented by this recur. This method includes a base date
         * argument, which indicates the start of the fist occurrence of this recurrence. The base date is used to inject
         * default values to return a set of dates in the correct format. For example, if the search start date (start) is
         * Wed, Mar 23, 12:19PM, but the recurrence is Mon - Fri, 9:00AM - 5:00PM, the start dates returned should all be at
         * 9:00AM, and not 12:19PM.
         */

        private HashSet <DateTime> GetDates(IDateTime seed, DateTime periodStart, DateTime periodEnd, int maxCount, RecurrencePattern pattern,
                                            bool includeReferenceDateInResults)
        {
            var dates        = new HashSet <DateTime>();
            var originalDate = DateUtil.GetSimpleDateTimeData(seed);
            var seedCopy     = DateUtil.GetSimpleDateTimeData(seed);

            if (includeReferenceDateInResults)
            {
                dates.Add(seedCopy);
            }

            // optimize the start time for selecting candidates
            // (only applicable where a COUNT is not specified)
            if (pattern.Count == int.MinValue)
            {
                var incremented = seedCopy;
                IncrementDate(ref incremented, pattern, pattern.Interval);
                while (incremented < periodStart)
                {
                    seedCopy = incremented;
                    IncrementDate(ref incremented, pattern, pattern.Interval);
                }
            }

            var expandBehavior = RecurrenceUtil.GetExpandBehaviorList(pattern);

            var noCandidateIncrementCount = 0;
            var candidate = DateTime.MinValue;

            while (maxCount < 0 || dates.Count < maxCount)
            {
                if (pattern.Until != DateTime.MinValue && candidate != DateTime.MinValue && candidate > pattern.Until)
                {
                    break;
                }

                if (candidate != DateTime.MinValue && candidate > periodEnd)
                {
                    break;
                }

                if (pattern.Count >= 1 && dates.Count >= pattern.Count)
                {
                    break;
                }

                var candidates = GetCandidates(seedCopy, pattern, expandBehavior);
                if (candidates.Count > 0)
                {
                    noCandidateIncrementCount = 0;

                    foreach (var t in candidates.OrderBy(c => c).Where(t => t >= originalDate))
                    {
                        candidate = t;

                        // candidates MAY occur before periodStart
                        // For example, FREQ=YEARLY;BYWEEKNO=1 could return dates
                        // from the previous year.
                        //
                        // candidates exclusive of periodEnd..
                        if (pattern.Count >= 1 && dates.Count >= pattern.Count)
                        {
                            break;
                        }

                        if (candidate >= periodEnd)
                        {
                            continue;
                        }

                        if (pattern.Until == DateTime.MinValue || candidate <= pattern.Until)
                        {
                            dates.Add(candidate);
                        }
                    }
                }
                else
                {
                    noCandidateIncrementCount++;
                    if (_maxIncrementCount > 0 && noCandidateIncrementCount > _maxIncrementCount)
                    {
                        break;
                    }
                }

                IncrementDate(ref seedCopy, pattern, pattern.Interval);
            }

            return(dates);
        }
        /**
         * Returns a list of start dates in the specified period represented by this recur. This method includes a base date
         * argument, which indicates the start of the fist occurrence of this recurrence. The base date is used to inject
         * default values to return a set of dates in the correct format. For example, if the search start date (start) is
         * Wed, Mar 23, 12:19PM, but the recurrence is Mon - Fri, 9:00AM - 5:00PM, the start dates returned should all be at
         * 9:00AM, and not 12:19PM.
         */

        private HashSet <DateTime> GetDates(IDateTime seed, DateTime periodStart, DateTime periodEnd, int maxCount, IRecurrencePattern pattern,
                                            bool includeReferenceDateInResults)
        {
            var dates        = new HashSet <DateTime>();
            var originalDate = DateUtil.GetSimpleDateTimeData(seed);
            var seedCopy     = DateUtil.GetSimpleDateTimeData(seed);

            if (includeReferenceDateInResults)
            {
                dates.Add(seedCopy);
            }

            // optimize the start time for selecting candidates
            // (only applicable where a COUNT is not specified)
            if (pattern.Count == int.MinValue)
            {
                var incremented = seedCopy;
                IncrementDate(ref incremented, pattern, pattern.Interval);
                while (incremented < periodStart)
                {
                    seedCopy = incremented;
                    IncrementDate(ref incremented, pattern, pattern.Interval);
                }
            }

            var expandBehavior = RecurrenceUtil.GetExpandBehaviorList(pattern);

            var invalidCandidateCount     = 0;
            var noCandidateIncrementCount = 0;
            var candidate = DateTime.MinValue;

            while ((maxCount < 0) || (dates.Count < maxCount))
            {
                if (pattern.Until != DateTime.MinValue && candidate != DateTime.MinValue && candidate > pattern.Until)
                {
                    break;
                }

                if (candidate != DateTime.MinValue && candidate > periodEnd)
                {
                    break;
                }

                if (pattern.Count >= 1 && (dates.Count + invalidCandidateCount) >= pattern.Count)
                {
                    break;
                }

                var candidates = GetCandidates(seedCopy, pattern, expandBehavior);
                if (candidates.Count > 0)
                {
                    noCandidateIncrementCount = 0;

                    // sort candidates for identifying when UNTIL date is exceeded..
                    candidates.Sort();

                    foreach (var t in candidates.Where(t => t >= originalDate))
                    {
                        candidate = t;

                        // candidates MAY occur before periodStart
                        // For example, FREQ=YEARLY;BYWEEKNO=1 could return dates
                        // from the previous year.
                        //
                        // candidates exclusive of periodEnd..
                        if (candidate >= periodEnd)
                        {
                            invalidCandidateCount++;
                        }
                        else if (pattern.Count >= 1 && (dates.Count + invalidCandidateCount) >= pattern.Count)
                        {
                            break;
                        }
                        else if (pattern.Until == DateTime.MinValue || candidate <= pattern.Until)
                        {
                            var utcCandidate = DateUtil.FromTimeZoneToTimeZone(candidate, DateUtil.GetZone(seed.TzId), DateTimeZone.Utc).ToDateTimeUtc();
                            if (!dates.Contains(candidate) && (pattern.Until == DateTime.MinValue || utcCandidate <= pattern.Until))
                            {
                                dates.Add(candidate);
                            }
                        }
                    }
                }
                else
                {
                    noCandidateIncrementCount++;
                    if ((_maxIncrementCount > 0) && (noCandidateIncrementCount > _maxIncrementCount))
                    {
                        break;
                    }
                }

                IncrementDate(ref seedCopy, pattern, pattern.Interval);
            }

            // sort final list..
            return(dates);
        }