/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// given an array of strings, finds the longest common prefix
function findCommonPrefixLength(strs) {
  if (strs.every(str => str.includes("/pdfs/"))) {
    // In all cases for pdfpaint PDFs, return the full file name
    return strs[0].lastIndexOf("/") + 1;
  }

  if (strs.length < 2) {
    // only one page in the manifest
    // i.e. http://localhost/tests/perf-reftest/bloom-basic.html
    var place = strs[0].lastIndexOf("/");
    if (place < 0) {
      place = 0;
    }
    return place;
  }

  var len = 0;
  do {
    var newlen = len + 1;
    var newprefix = null;
    var failed = false;
    for (var i = 0; i < strs.length; i++) {
      if (newlen > strs[i].length) {
        failed = true;
        break;
      }

      var s = strs[i].substr(0, newlen);
      if (newprefix == null) {
        newprefix = s;
      } else if (newprefix != s) {
        failed = true;
        break;
      }
    }

    if (failed) {
      break;
    }

    len++;
  } while (true);
  return len;
}

// Constructor
function Report() {
  this.timeVals = {};
  this.totalCCTime = 0;
  this.showTotalCCTime = false;
}

Report.prototype.pageNames = function () {
  var retval = [];
  for (var page in this.timeVals) {
    retval.push(page);
  }
  return retval;
};

Report.prototype.getReport = function () {
  var report;
  var pages = this.pageNames();
  var prefixLen = findCommonPrefixLength(pages);

  report = "__start_tp_report\n";
  report += "_x_x_mozilla_page_load\n";
  report += "_x_x_mozilla_page_load_details\n";
  report += "|i|pagename|runs|\n";

  for (var i = 0; i < pages.length; i++) {
    // don't report any measurements that were reported for about:blank
    // some tests (like about-preferences) use it as a dummy test page
    if (pages[i] == "about:blank") {
      continue;
    }
    report +=
      "|" +
      i +
      ";" +
      pages[i].substr(prefixLen) +
      ";" +
      this.timeVals[pages[i]].join(";") +
      "\n";
  }
  report += "__end_tp_report\n";

  if (this.showTotalCCTime) {
    report += "__start_cc_report\n";
    report += "_x_x_mozilla_cycle_collect," + this.totalCCTime + "\n";
    report += "__end_cc_report\n";
  }
  var now = window.performance.now();
  report += "__startTimestamp" + now + "__endTimestamp\n"; // timestamp for determning shutdown time, used by talos

  return report;
};

Report.prototype.getReportSummary = function () {
  function average(arr) {
    var sum = 0;
    for (var i in arr) {
      sum += arr[i];
    }
    return sum / (arr.length || 1);
  }

  function median(arr) {
    if (!arr.length) {
      return 0;
    } // As good indication for "not available" as any other value.

    var sorted = arr.slice(0).sort();
    var mid = Math.floor(arr.length / 2);

    if (sorted.length % 2) {
      return sorted[mid];
    }

    return average(sorted.slice(mid, mid + 2));
  }

  // We use sample stddev and not population stddev because
  // well.. it's a sample and we can't collect all/infinite number of values.
  function stddev(arr) {
    if (arr.length <= 1) {
      return 0;
    }
    var avg = average(arr);

    var squareDiffArr = arr.map(function (v) {
      return Math.pow(v - avg, 2);
    });
    var sum = squareDiffArr.reduce(function (a, b) {
      return a + b;
    });
    var rv = Math.sqrt(sum / (arr.length - 1));
    return rv;
  }

  var report = "";
  var pages = this.pageNames();
  var prefixLen = findCommonPrefixLength(pages);

  report += "------- Summary: start -------\n";
  report += "Number of tests: " + pages.length + "\n";

  for (var i = 0; i < pages.length; i++) {
    var results = this.timeVals[pages[i]].map(function (v) {
      return Number(v);
    });

    report +=
      "\n[#" +
      i +
      "] " +
      pages[i].substr(prefixLen) +
      "  Cycles:" +
      results.length +
      "  Average:" +
      average(results).toFixed(2) +
      "  Median:" +
      median(results).toFixed(2) +
      "  stddev:" +
      stddev(results).toFixed(2) +
      " (" +
      ((100 * stddev(results)) / median(results)).toFixed(1) +
      "%)" +
      (results.length < 5
        ? ""
        : "  stddev-sans-first:" + stddev(results.slice(1)).toFixed(2)) +
      "\nValues: " +
      results
        .map(function (v) {
          return v.toFixed(1);
        })
        .join("  ") +
      "\n";
  }
  report += "-------- Summary: end --------\n";

  return report;
};

Report.prototype.recordTime = function (pageName, ms) {
  if (this.timeVals[pageName] == undefined) {
    this.timeVals[pageName] = [];
  }
  this.timeVals[pageName].push(ms);
};

Report.prototype.recordCCTime = function (ms) {
  this.totalCCTime += ms;
  this.showTotalCCTime = true;
};
