/// <summary>
        /// Get results from paged query for NuGet v3
        /// </summary>
        /// <typeparam name="B">Response body type</typeparam>
        /// <typeparam name="R">Result type</typeparam>
        /// <param name="request">Current request</param>
        /// <param name="getTotalResultsCountFromResponse">Delegate to get total result count from first response. Return 0 or a negative number to indicate failure.</param>
        /// <param name="getResultsFromResponse">Delegate to get results from a response body.</param>
        /// <param name="getPackageQuery">Delegate to get the next query, skipping a given number of results.</param>
        /// <param name="parseResponseBody">Delegate to parse the response string body to a response object body.</param>
        /// <returns>All results.</returns>
        public static IEnumerable <R> GetResults <B, R>(RequestWrapper request, Func <B, long> getTotalResultsCountFromResponse, Func <B, IEnumerable <IEnumerable <R> > > getResultsFromResponse, Func <long, string> getNextResponseQuery, Func <string, B> parseResponseBody, int pageCount)
        {
            ProgressTracker progressTracker        = new ProgressTracker(ProgressTracker.GetRandomId(), 0, 100);
            int             childProgressTrackerId = request.StartProgress(progressTracker.ProgressID, Resources.Messages.NuGetServerReadStarted);
            long            total                   = -1;
            long            resultCount             = 0;
            bool            successful              = true;
            bool            trackIndividualProgress = false;
            TaskGroup <IEnumerable <IEnumerable <R> > > taskGroup = new TaskGroup <IEnumerable <IEnumerable <R> > >();

            System.Threading.CancellationTokenSource cancelToken = new System.Threading.CancellationTokenSource();
            string query   = getNextResponseQuery(resultCount);
            string content = new StreamReader(NuGetClient.DownloadDataToStream(query, request)).ReadToEnd();

            B response = parseResponseBody(content);

            total = getTotalResultsCountFromResponse(response);
            if (total < 0)
            {
                total = -2;
                request.Warning(Messages.FailedToParseTotalHitsCount, query);
                successful = false;
            }
            else
            {
                request.Progress(childProgressTrackerId, 0, String.Format(CultureInfo.CurrentCulture, Resources.Messages.NuGetServerReadProgress, resultCount, total));
                // When the result count is low enough, track individual results. Otherwise, track by page.
                trackIndividualProgress = total <= (pageCount * 2);
                taskGroup.Add(Task.Factory.StartNew <IEnumerable <IEnumerable <R> > >(() => { return(getResultsFromResponse(response)); }, cancelToken.Token));
            }

            if (total >= 0)
            {
                long numberOfPages = total / pageCount + (total % pageCount == 0 ? 0 : 1);
                long pageSkipCount = 0;
                for (long pageNum = 1; pageNum < numberOfPages; pageNum++)
                {
                    pageSkipCount += pageCount;
                    string pageQuery = getNextResponseQuery(pageSkipCount);
                    taskGroup.Add(Task.Factory.StartNew <IEnumerable <IEnumerable <R> > >((q) =>
                    {
                        string pageContent = new StreamReader(NuGetClient.DownloadDataToStream((string)q, request)).ReadToEnd();
                        B pageResponse     = parseResponseBody(pageContent);
                        return(getResultsFromResponse(pageResponse));
                    }, pageQuery, cancelToken.Token));
                }
            }

            while (taskGroup.HasAny)
            {
                if (request.IsCanceled())
                {
                    cancelToken.Cancel();
                    successful = false;
                    break;
                }

                IEnumerable <IEnumerable <R> > resultCollections = taskGroup.WaitAny();
                foreach (IEnumerable <R> resultCollection in resultCollections)
                {
                    resultCount++;
                    // If trackIndividualProgress == false, this progress will be reported at the end
                    if (trackIndividualProgress)
                    {
                        // Report an individual package is done processing
                        request.Progress(childProgressTrackerId, progressTracker.ConvertPercentToProgress(((double)resultCount) / total),
                                         String.Format(CultureInfo.CurrentCulture, Resources.Messages.NuGetServerReadProgress, resultCount, total));
                    }

                    foreach (R result in resultCollection)
                    {
                        yield return(result);
                    }
                }

                if (!trackIndividualProgress)
                {
                    // Report that this page is done processing
                    request.Progress(childProgressTrackerId, progressTracker.ConvertPercentToProgress(((double)resultCount) / total),
                                     String.Format(CultureInfo.CurrentCulture, Resources.Messages.NuGetServerReadProgress, resultCount, total));
                }
            }

            request.CompleteProgress(childProgressTrackerId, successful);
            request.CompleteProgress(progressTracker.ProgressID, successful);
        }