/// <summary>
        /// Get a pre-formatted html string which contains PTF conf and test result stats from specified log file.
        /// </summary>
        /// <param name="filename">file name of log file</param>
        /// <param name="logAnalyzer">Test log file analyzer</param>
        /// <returns>a pre-formatted html contains PTF conf and test result stats.</returns>
        private string GetLogStatsHTML(string filename, XmlLogAnalyzer logAnalyzer)
        {
            StringBuilder logOutput = new StringBuilder(Resource.LogTable);
            StringBuilder protocols = new StringBuilder();
            uint verifiedRequirements = 0;
            foreach (string protocol in logAnalyzer.Protocols)
            {
                protocols.Append(protocol + ';');
            }
            logOutput.Replace(@"$PROTOCOL$", HttpUtility.HtmlEncode(protocols.ToString()));
            logOutput.Replace(@"$FILENAME$", HttpUtility.HtmlEncode(Path.GetFileName(filename)));

            // get all PTF confs from log file
            StringBuilder config = new StringBuilder();
            foreach (KeyValuePair<string, string> ptfconf in logAnalyzer.PTFConfigurations)
            {
                config.Append(@"
            <tr>
            <td>
                " + HttpUtility.HtmlEncode(ptfconf.Key) + @"</td>
            <td>
                " + HttpUtility.HtmlEncode(ptfconf.Value) + @"</td>
            </tr>");
            }
            logOutput.Replace(@"$CONFIG$", config.ToString());

            // start output test result from log file
            StringBuilder testresult = new StringBuilder();
            uint total = 0;
            if (logAnalyzer.TestResult.ContainsKey("TestsExecuted"))
            {
                total = uint.Parse(logAnalyzer.TestResult["TestsExecuted"]);
            }
            foreach (KeyValuePair<string, string> result in logAnalyzer.TestResult)
            {
                if (result.Key == "TimeStamp")
                {
                    testresult.Append(@"
            <tr>
                <td>
                    " + HttpUtility.HtmlEncode(result.Key) + @"</td>
                <td>
                    " + HttpUtility.HtmlEncode(result.Value) + @"</td>
            </tr>");
                    continue;
                }
                uint actual = uint.Parse(result.Value);
                float percent = (total == 0) ? 0 : actual / (float)total;
                testresult.Append(@"
            <tr>
            <td>
                " + HttpUtility.HtmlEncode(result.Key) + @"</td>
            <td>
                " + actual + " (" + percent.ToString("P") + @")</td>
            </tr>");
            }
            logOutput.Replace(@"$TESTRESULT$", testresult.ToString());

            foreach (KeyValuePair<string, string> kvp in logAnalyzer.CoveredRequirements)
            {
                string req_id = tableAnalyzer.GetRequirementId(kvp.Key, false);
                //the requirement id in log file should always the full req id.
                if (!string.IsNullOrEmpty(req_id))
                {

                    if (tableAnalyzer.RequirementsToVerify.ContainsKey(req_id))
                    {
                        verifiedRequirements++;
                    }
                    else if (tableAnalyzer.RequirementsNotToVerify.ContainsKey(req_id))
                    {
                        LogTraceWarning("The test log file covers an unverifiable requirement : " + kvp.Key);
                    }
                    else
                    {
                        LogTraceWarning("The test log file covers a deleted requirement : " + kvp.Key);
                    }
                }
                else
                {
                    LogTraceWarning("The test log file covers a non-existed requirement :" + kvp.Key);
                }
            }
            logOutput.Replace(@"$Verified$", verifiedRequirements.ToString("D"));
            return logOutput.ToString();
        }
        /// <summary>
        /// Generate a html result page from given information in <c ref="param"></c>
        /// </summary>
        private void Generate()
        {
            string tempFile = string.Empty;

            try
            {
                Dictionary<string, List<string>> globalVerifiedRequirements =
                    new Dictionary<string, List<string>>();
                Dictionary<string, string> globleVerifiedRequirementsTimestamp =
                    new Dictionary<string, string>();
                Dictionary<string, string> inconsistencyErrors =
                    new Dictionary<string, string>();
                StringBuilder sb = new StringBuilder();

                Dictionary<string, ExclRequirementInfo> globalExcludedRequirements
                    = new Dictionary<string, ExclRequirementInfo>();

                // write html header
                sb.Append(Resource.HtmlTemplateHeader);

                foreach (string filename in param.XmlLogs)
                {
                    // get xml log analyzer
                    XmlLogAnalyzer logAnalyzer = new XmlLogAnalyzer(filename);
                    // get failed test cases and exclued requirements
                    foreach (KeyValuePair<string, string> kvp in logAnalyzer.ExcludedRequirements)
                    {
                        //the requirement covered in one passed case, it will never be excluded.
                        if (!logAnalyzer.CoveredRequirements.ContainsKey(kvp.Key) &&
                            !globalExcludedRequirements.ContainsKey(kvp.Key))
                        {
                            //add test case which the req belong to
                            //add the test case result
                            //add the timestamp
                            //add the log file name which contain the req
                            globalExcludedRequirements[kvp.Key] =
                                new ExclRequirementInfo(kvp.Value,
                                    logAnalyzer.AllTestCases[kvp.Value],
                                    logAnalyzer.ExcludedRequirementsTimestamp[kvp.Key],
                                    filename);
                        }
                    }

                    foreach (KeyValuePair<string, string> kvp in logAnalyzer.CoveredRequirements)
                    {
                        string rsId = tableAnalyzer.GetRequirementId(kvp.Key, false);
                        if (!string.IsNullOrEmpty(rsId))
                        {
                            //the logged ID should be 1-1 mapping to the Req_ID
                             if (tableAnalyzer.RequirementsToVerify.ContainsKey(rsId))
                            {

                                //unverified requirements were captured.
                                if (string.Compare(tableAnalyzer.RequirementVerifications[rsId], VerificationValues.UNVERIFIED, true) == 0)
                                {
                                    if (!inconsistencyErrors.ContainsKey(rsId))
                                        inconsistencyErrors.Add(rsId, tableAnalyzer.RequirementVerifications[rsId]);
                                    continue;
                                }

                                if (!globalVerifiedRequirements.ContainsKey(rsId))
                                {
                                    globalVerifiedRequirements[rsId] = new List<string>();
                                    globalVerifiedRequirements[rsId].Add(filename);
                                    if (!globleVerifiedRequirementsTimestamp.ContainsKey(rsId))
                                    {
                                        globleVerifiedRequirementsTimestamp[rsId] = kvp.Value;
                                    }
                                }
                                else
                                {
                                    if (!globalVerifiedRequirements[rsId].Contains(filename))
                                        globalVerifiedRequirements[rsId].Add(filename);
                                    else
                                        continue;
                                }
                            }
                            else
                            {
                                //non-testable, deleted, non-exist requirements were captured.
                                if (string.Compare(tableAnalyzer.RequirementVerifications[rsId], VerificationValues.ADAPTER, true) != 0 &&
                                    string.Compare(tableAnalyzer.RequirementVerifications[rsId], VerificationValues.TESTCASE, true) != 0)
                                {
                                    if (!inconsistencyErrors.ContainsKey(rsId))
                                        inconsistencyErrors.Add(rsId, tableAnalyzer.RequirementVerifications[rsId]);
                                }
                            }
                        }
                        else
                        {
                            if (!inconsistencyErrors.ContainsKey(kvp.Key))
                            {
                                inconsistencyErrors.Add(kvp.Key, VerificationValues.NONEXIST);
                            }
                        }
                    }

                    // write whole table of ptfconf/test result from log to html
                    string logOutput = GetLogStatsHTML(filename, logAnalyzer);
                    sb.Append(logOutput);

                    // output a line break to make source clear
                    sb.Append("\r\n");

                    // write a horizontal line to separate results from different logs
                    sb.Append("<hr />");
                }

                // write html footer
                sb.Append(Resource.HtmlTemplateFooter);

                sb.Replace("$GLOBALSTAT$",
                    GetGlobalStat(globalVerifiedRequirements, globleVerifiedRequirementsTimestamp));

                if (verboseMode && globalExcludedRequirements.Count > 0)
                {
                    sb.Replace("$EXCLUDEDREQUIREMENTS$",
                        GetExcludedRequirements(globalExcludedRequirements, inconsistencyErrors));
                }
                else
                {
                    sb.Replace("$EXCLUDEDREQUIREMENTS$", string.Empty);
                }

                sb.Replace("$GLOBALRSINCONSISTENCY$",
                    GetGlobalRSInconsistenies(
                    tableAnalyzer.RSValicationErrors,
                    tableAnalyzer.RSValidationWarnings));

                sb.Replace("$GLOBALINCONSISTENCY$",
                    GetGlobalInconsistencise(globalVerifiedRequirements, inconsistencyErrors));

                tempFile = Path.GetTempFileName();
                using (StreamWriter sw = new StreamWriter(tempFile, false, Encoding.UTF8))
                {
                    sw.Write(sb.ToString());
                }

                string outputDirectory = Path.GetDirectoryName(Path.GetFullPath(param.OutputFile));
                if (!Directory.Exists(outputDirectory))
                {
                    throw new InvalidOperationException(
                        string.Format("Test report output directory does not exist: {0}",
                        outputDirectory)
                        );
                }
                // move temp file to output file.
                File.Delete(param.OutputFile);
                File.Move(tempFile, param.OutputFile);
                Console.WriteLine("[NOTICE] Report file generated: \r\n" +
                    "  " + param.OutputFile);

                if (verboseMode)
                {
                    string fileName = "ReqCoveredStatus.xml";
                    string outputDir = Path.GetDirectoryName(param.OutputFile);
                    string outputPath = Path.Combine(outputDir, fileName);
                    DumpReqsCaptureStatus(globalVerifiedRequirements, inconsistencyErrors, outputPath);
                    Console.WriteLine("[NOTICE] Requirement coverage report generated: \r\n" +
                    "  " + outputPath);
                }
            }
            catch (Exception)
            {
                throw;
            }
            finally
            {
                // delete temporary file
                if (!String.IsNullOrEmpty(tempFile) && File.Exists(tempFile))
                {
                    File.Delete(tempFile);
                }
            }
        }