//
        // Returns stats about the jira use in this repository
        //
        private static JiraState.JiraStatistics GetJiraStats(SvnLogs.Log[] revisionLogs, Simple credentials)
        {
            // Starting to review logs
            s_logger.Log("Starting to extract the Jira stats");
            Display.Start(Display.State.QueryingReviewboard, revisionLogs.Length);

            // Try to query the server for our review state
            try
            {
                JiraState.JiraStatistics results = JiraState.GetJiraStatistics(revisionLogs, credentials, s_logger, (currentCount) =>
                {
                    Display.Update(currentCount, revisionLogs.Length);
                });

                // Did we fail
                if (results == null)
                {
                    s_errorMessage = @"Unable to generate the Jira stats against the Jira server";
                    return(null);
                }

                // Return what we have
                s_logger.Log("Jira stats generated");
                return(results);
            }
            catch (Exception generalException)
            {
                String exceptionMessage = (generalException.InnerException == null ? generalException.Message : generalException.InnerException.Message);
                s_errorMessage = "Unable to generate the Jira stats against the Jira server\n\n" + exceptionMessage;
                return(null);
            }
        }
        //
        // Updates the list of invalid and excessive Jira logs
        //
        private static string UpdateJiraLists(string outputContent, JiraState.JiraStatistics jiraStats)
        {
            outputContent = CreateJiraTable(outputContent, "___EXCESSIVE_JIRA_LOGS___", "Excessive Jira Logs", jiraStats.m_validJiras, 4);
            outputContent = CreateJiraTable(outputContent, "___INVALID_JIRA_LOGS___", "Invalid Jira Tickets", jiraStats.m_invalidJiras, 0);

            // Return our output
            return(outputContent);
        }
        //
        // Displays information about the Jiras in the commit logs
        //
        private static string UpdateJiraStatistics(string outputContent, JiraState.JiraStatistics jiraStats)
        {
            // Update our values
            outputContent = outputContent.Replace(@"___COMMITS_WITHOUT_JIRAS___", jiraStats.m_commitsWithoutJiras.ToString());

            int   commitCount       = jiraStats.m_commitsWithoutJiras + jiraStats.m_commitsWithJiras;
            float percentageNoJiras = ((float)jiraStats.m_commitsWithoutJiras / (float)commitCount) * 100.0f;

            outputContent = outputContent.Replace(@"___COMMITS_WITHOUT_JIRAS_PERCENTAGE___", percentageNoJiras.ToString(PercentageFormat));

            // How many Jira's per review
            int   jiraCount     = jiraStats.m_invalidJiras.Count + jiraStats.m_validJiras.Count;
            float jiraPerCommit = (jiraCount == 0 ? 0 : jiraStats.m_commitsWithJiras / (float)jiraCount);

            outputContent = outputContent.Replace(@"___AVERAGE_JIRAS_PER_COMMIT___", jiraPerCommit.ToString(RatioFormat));

            // Return our updated report
            return(outputContent);
        }
        //
        // Generates the final report
        //
        public static bool Generate(string reportName, RevisionList.Revisions revisions, SvnLogs.Log[] revisionLogs, ReviewState.GetCommitStatsResult commitStats, ReviewState.ReviewStatistics[] reviewStats, JiraState.JiraStatistics jiraStats, Logging logger, TimeSpan reviewTime)
        {
            // Start with the clean template
            string outputContent = Properties.Resources.ReportTemplate;

            try
            {
                // Spin through and update various sections
                outputContent = UpdateOverview(outputContent, reportName, revisionLogs, revisions.Url, reviewTime);
                outputContent = UpdateCommitStatistics(outputContent, commitStats);
                outputContent = UpdateReviewStatistics(outputContent, reviewStats);
                outputContent = UpdateAverageResults(outputContent, reviewStats);
                outputContent = UpdateJiraStatistics(outputContent, jiraStats);
                outputContent = UpdateReviewLists(outputContent, reviewStats);
                outputContent = UpdateJiraLists(outputContent, jiraStats);
                outputContent = UpdateCopyrightSection(outputContent);

                // Display the content
                DisplayReport(outputContent, reportName, revisions.Url);
            }
            catch (Exception e)
            {
                logger.Log("Exception raised when generating report\n\n{0}\n", e.Message);
            }

            // Done
            return(true);
        }
        //
        // Generates the report
        //
        private static bool CreateReviewReport(string reportName, RevisionList.Revisions revisions, SvnLogs.Log[] revisionLogs, ReviewState.GetCommitStatsResult commitStats, ReviewState.ReviewStatistics[] reviewStats, JiraState.JiraStatistics jiraStats, Stopwatch reviewTimer)
        {
            // We're now generating
            s_logger.Log("Starting to generate review report");
            Display.Start(Display.State.CreatingResults);

            // Try and generate the report
            bool generated = Report.Generate(reportName, revisions, revisionLogs, commitStats, reviewStats, jiraStats, s_logger, reviewTimer.Elapsed);

            if (generated == false)
            {
                s_errorMessage = @"Unable to generate the Review Report";
            }

            // Return our results
            return(generated);
        }
        //
        // Start the process of generating the stats
        //
        public static void Start(Form owner, string fileList, string reportName, Logging logger, bool injectPaths, GenerationFinished generationFinished)
        {
            // Track our properties
            s_logger = logger;

            // Kick off the background threads
            BackgroundWorker updateThread = new BackgroundWorker();

            // Does the work of the request
            updateThread.DoWork += (object objectSender, DoWorkEventArgs args) =>
            {
                logger.Log(@"Starting stats generation");
                try
                {
                    // Check if we're authenticated
                    Simple reviewBoardCredentials = CheckRBServerAuthentication(owner);
                    if (reviewBoardCredentials == null)
                    {
                        return;
                    }
                    Simple jiraCredentials = CheckJiraServerAuthentication(owner);
                    if (reviewBoardCredentials == null)
                    {
                        return;
                    }

                    // Get the list of paths to review
                    string[] pathsToReview = ParseFileList(fileList, injectPaths);
                    if (pathsToReview == null)
                    {
                        return;
                    }

                    // Get the revision list for each path
                    RevisionList.Revisions[] revisionLists = RequestRevisionLists(pathsToReview);
                    if (revisionLists == null)
                    {
                        return;
                    }

                    // Spin through each revision list and do the work for each path selected
                    foreach (RevisionList.Revisions thisRevisionList in revisionLists)
                    {
                        // Identify which path we'll go through
                        s_logger.Log(@"Generating stats for\n{0}", thisRevisionList.Path);

                        // We need to time this review
                        Stopwatch stopWatch = new Stopwatch();
                        stopWatch.Start();

                        // Get the logs for the given set of revisions
                        SvnLogs.Log[] revisionLogs = GetLogsFromRevisions(thisRevisionList);
                        if (revisionLogs == null)
                        {
                            return;
                        }

                        ReviewState.GetCommitStatsResult commitStats = GetCommitStats(revisionLogs);
                        if (commitStats == null)
                        {
                            return;
                        }

                        // Verify would could get the review information
                        bool reviewInformationValid = CheckReviewInformation(commitStats);
                        if (reviewInformationValid == false)
                        {
                            return;
                        }

                        // Now generate information about the reviews
                        ReviewState.ReviewStatistics[] reviewStats = GetReviewStats(commitStats.Reviews, thisRevisionList.Path, reviewBoardCredentials);
                        if (reviewStats == null)
                        {
                            return;
                        }

                        // Get the Jira information from the commits
                        JiraState.JiraStatistics jiraStats = GetJiraStats(revisionLogs, jiraCredentials);
                        if (jiraStats == null)
                        {
                            return;
                        }

                        // Create the review
                        bool reportGenerated = CreateReviewReport(reportName, thisRevisionList, revisionLogs, commitStats, reviewStats, jiraStats, stopWatch);
                        if (reportGenerated == false)
                        {
                            return;
                        }
                    }
                }
                catch (Exception e)
                {
                    s_logger.Log("Exception raised when generating review stats\n\n{0}\n", e.Message);
                    s_errorMessage = string.Format("Unable to generate review statistics for the given path\n\n{0}", e.Message);
                }
            };

            // Called when it's all been generated
            updateThread.RunWorkerCompleted += (object objectSender, RunWorkerCompletedEventArgs args) =>
            {
                s_logger.Log("Review generation complete");

                // Inform the user something went wrong?
                if (string.IsNullOrEmpty(s_errorMessage) == false)
                {
                    s_logger.Log(@"* Error encountered when running\n{0}\n", s_errorMessage);
                    MessageBox.Show(s_errorMessage, @"Stats Generation Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }

                // Done
                generationFinished();
            };

            // Off it goes
            updateThread.RunWorkerAsync();
        }