/**
   * Plots the chord transcriptions for all jobs, for each file.
   *
   * @param testSets the list of test sets.
   * @param results The results Object containing the data to plot.
   * @return an array of page items that will produce the plots.
   */
  @SuppressWarnings("unchecked")
  private PageItem[] plotTranscriptionForAllJobs(NemaEvaluationResultSet results) {
    NemaData groundtruth = null;

    /* Plot each result */
    Map<String, Map<NemaTrackList, List<NemaData>>> perTrackResults =
        results.getJobIdToPerTrackEvaluationAndResults();
    List<PageItem> plotItems = new ArrayList<PageItem>();

    Map<String, NemaData[]> trackIDToTranscripts = new HashMap<String, NemaData[]>();
    // get job names and sort
    List<String> jobNames = new ArrayList<String>(perTrackResults.keySet());
    Collections.sort(jobNames);

    for (Iterator<NemaTrackList> foldIt = results.getTestSetTrackLists().iterator();
        foldIt.hasNext(); ) {
      NemaTrackList testSet = foldIt.next();

      // map IDs for tracks to an array of NemaData Objects for each system, use nulls in case of
      // missing results
      for (Iterator<String> systemIt = perTrackResults.keySet().iterator(); systemIt.hasNext(); ) {
        String system = systemIt.next();
        int systemIdx = jobNames.indexOf(system);
        Map<NemaTrackList, List<NemaData>> sysResults = perTrackResults.get(system);
        List<NemaData> sysSetResults = sysResults.get(testSet);
        for (Iterator<NemaData> trackIt = sysSetResults.iterator(); trackIt.hasNext(); ) {
          NemaData track = trackIt.next();
          NemaData[] transcripts = trackIDToTranscripts.get(track.getId());
          if (transcripts == null) {
            transcripts = new NemaData[jobNames.size()];
            trackIDToTranscripts.put(track.getId(), transcripts);
          }
          transcripts[systemIdx] = track;
        }
      }
    }

    // iterate over tracks (in alphabetical order) and produce each plot
    List<String> trackIds = new ArrayList<String>(trackIDToTranscripts.keySet());
    Collections.sort(trackIds);

    for (Iterator<String> trackIt = trackIds.iterator(); trackIt.hasNext(); ) {
      String trackId = trackIt.next();
      NemaData[] transcripts = trackIDToTranscripts.get(trackId);
      getLogger().info("\t\tplotting track " + trackId + "...");

      // setup data-series to plot
      Map<String, double[]> series = new HashMap<String, double[]>(2);
      List<String> seriesNames = new ArrayList<String>(1 + transcripts.length);
      List<Boolean> isGroundtruth = new ArrayList<Boolean>(1 + transcripts.length);

      // setup time line for for X-axis
      double startTimeSecs = 0.0;
      double endTimeSecs = 0;

      if (results.getTrackIDToGT() != null) {
        groundtruth = results.getTrackIDToGT().get(trackId);
      }
      if (groundtruth == null) {
        getLogger().warning("No ground-truth found for '" + trackId + "' to be used in plotting");
      }
      double[][] rawGtData2D = null;
      String[] annotators = null;
      if (groundtruth != null && groundtruth.hasMetadata(NemaDataConstants.ONSET_DETECTION_DATA)) {
        rawGtData2D = (double[][]) groundtruth.getMetadata(NemaDataConstants.ONSET_DETECTION_DATA);

        if (groundtruth.hasMetadata(NemaDataConstants.ONSET_DETECTION_ANNOTATORS)) {
          annotators =
              groundtruth.getStringArrayMetadata(NemaDataConstants.ONSET_DETECTION_ANNOTATORS);
        }

        int numGt = rawGtData2D[0].length;
        for (int i = 0; i < numGt; i++) {
          double[] rawGtData = new double[rawGtData2D.length];
          int trim = -1;
          for (int j = 0; j < rawGtData2D.length; j++) {
            rawGtData[j] = rawGtData2D[j][i];
            if (Double.isNaN(rawGtData[j])) {
              trim = j;
              break;
            }
            endTimeSecs = Math.max(rawGtData[j], endTimeSecs);
          }
          if (trim != -1) {
            double[] trimmedData = new double[trim];
            for (int j = 0; j < trimmedData.length; j++) {
              trimmedData[j] = rawGtData[j];
            }
            rawGtData = trimmedData;
          }
          if (annotators != null) {
            String annotator = annotators[i];
            series.put(annotator, rawGtData);
            seriesNames.add(annotator);
          } else {
            series.put("Ground-truth " + i, rawGtData);
            seriesNames.add("Ground-truth " + i);
          }
          isGroundtruth.add(true);
        }
      } else {
        getLogger().warning("No ground-truth found for '" + trackId + "' to be used in plotting");
      }

      // iterate through systems
      for (int i = 0; i < transcripts.length; i++) {
        NemaData nemaData = transcripts[i];
        Object rawDataObj = nemaData.getMetadata(NemaDataConstants.ONSET_DETECTION_DATA);
        if (rawDataObj != null) {
          double[][] rawData2D = (double[][]) rawDataObj;
          double[] rawData = new double[rawData2D.length];
          for (int j = 0; j < rawData.length; j++) {
            rawData[j] = rawData2D[j][0];

            endTimeSecs = Math.max(rawData[j], endTimeSecs);
          }

          series.put(jobNames.get(i), rawData);
          seriesNames.add(jobNames.get(i));
          isGroundtruth.add(false);
        }
      }

      try {
        ProtovisOnsetPlotItem plot =
            new ProtovisOnsetPlotItem(
                // plotname
                "transcription_" + trackId,
                // plot caption
                "Onset transcriptions for track " + trackId,
                // start time for x axis
                startTimeSecs,
                // end time for x axis
                endTimeSecs,
                // map of annotator/system names to the data
                series,
                // ordered list of annotator/system names
                seriesNames,
                // flags indicating whether a series is ground-truth
                isGroundtruth,
                // Directory to write JS data files to
                outputDir);
        plotItems.add(plot);
      } catch (IOException e) {
        getLogger().log(Level.SEVERE, "Failed to plot results for track " + trackId, e);
      }
    }
    return plotItems.toArray(new PageItem[plotItems.size()]);
  }
  /**
   * Plots the onset transcriptions for each job, for each file
   *
   * @param jobId the jobId we wish to plot results for.
   * @param results The results Object containing the data to plot.
   * @return an array of page items that will produce the plots.
   */
  private PageItem[] plotTranscriptionForJob(String jobId, NemaEvaluationResultSet results) {
    NemaData result, groundtruth = null;

    /* Plot each result */
    Map<NemaTrackList, List<NemaData>> job_results = results.getPerTrackEvaluationAndResults(jobId);
    List<PageItem> plotItems = new ArrayList<PageItem>();

    for (Iterator<NemaTrackList> foldIt = job_results.keySet().iterator(); foldIt.hasNext(); ) {
      NemaTrackList testSet = foldIt.next();
      for (Iterator<NemaData> iterator = job_results.get(testSet).iterator();
          iterator.hasNext(); ) {
        result = iterator.next();
        if (results.getTrackIDToGT() != null) {
          groundtruth = results.getTrackIDToGT().get(result.getId());
        }
        if (groundtruth == null) {
          getLogger()
              .warning("No ground-truth found for '" + result.getId() + "' to be used in plotting");
        }

        //				File plotFile = new File(sysDir.getAbsolutePath()
        //						+ File.separator + "track_" + result.getId() + MELODY_PLOT_EXT);
        //				plotItems.add(plotFile);

        double[][] rawData2D =
            result.get2dDoubleArrayMetadata(NemaDataConstants.ONSET_DETECTION_DATA);
        double[][] rawGtData2D = null;
        if (groundtruth != null) {
          rawGtData2D =
              groundtruth.get2dDoubleArrayMetadata(NemaDataConstants.ONSET_DETECTION_DATA);
        }
        String[] annotators = null;
        if (groundtruth != null
            && groundtruth.hasMetadata(NemaDataConstants.ONSET_DETECTION_ANNOTATORS)) {
          annotators =
              groundtruth.getStringArrayMetadata(NemaDataConstants.ONSET_DETECTION_ANNOTATORS);
        }
        // setup time line for for X-axis
        double startTimeSecs = 0.0;
        double endTimeSecs = 0.0;

        // Load in the prediction data
        double[] rawData = new double[rawData2D.length];
        for (int i = 0; i < rawData.length; i++) {
          rawData[i] = rawData2D[i][0];
          if (rawData2D[i][0] > endTimeSecs) {
            endTimeSecs = rawData2D[i][0];
          }
        }

        // setup hash map for to plot
        Map<String, double[]> series = new HashMap<String, double[]>(rawGtData2D[0].length + 1);
        List<String> seriesNames = new ArrayList<String>(rawGtData2D[0].length + 1);
        List<Boolean> isGroundtruth = new ArrayList<Boolean>(rawGtData2D[0].length + 1);
        if (groundtruth != null) {
          for (int curGT = 0; curGT < rawGtData2D[0].length; curGT++) {
            ArrayList<Double> gtDataArr = new ArrayList<Double>();
            for (int t = 0; t < rawGtData2D.length; t++) {
              double onTime = rawGtData2D[t][curGT];
              if (!Double.isNaN(onTime)) {
                gtDataArr.add(new Double(onTime));
                if (onTime > endTimeSecs) {
                  endTimeSecs = onTime;
                }
              }
            }
            double[] rawGtData = new double[gtDataArr.size()];
            for (int t = 0; t < rawGtData.length; t++) {
              rawGtData[t] = gtDataArr.get(t).doubleValue();
            }
            if (groundtruth.hasMetadata(NemaDataConstants.ONSET_DETECTION_ANNOTATORS)) {
              String annotator = annotators[curGT];
              series.put(annotator, rawGtData);
              seriesNames.add(annotator);
            } else {
              series.put("Ground-truth " + curGT, rawGtData);
              seriesNames.add("Ground-truth " + curGT);
            }
            isGroundtruth.add(true);
          }
        }

        series.put(results.getJobName(jobId), rawData);
        seriesNames.add(results.getJobName(jobId));
        isGroundtruth.add(false);

        try {
          ProtovisOnsetPlotItem plot =
              new ProtovisOnsetPlotItem(
                  // plotname
                  results.getJobName(jobId) + "_onset_transcript_" + result.getId(),
                  // plot caption
                  results.getJobName(jobId) + ": Onset transcription for track " + result.getId(),
                  // start time for x axis
                  startTimeSecs,
                  // end time for x axis
                  endTimeSecs,
                  // map of annotator/system names to the data
                  series,
                  // ordered list of annotator/system names
                  seriesNames,
                  // flags indicating whether a series is ground-truth
                  isGroundtruth,
                  // Directory to write JS data files to
                  outputDir);
          plotItems.add(plot);
        } catch (IOException e) {
          getLogger()
              .log(
                  Level.SEVERE,
                  "Failed to plot results for job "
                      + results.getJobName(jobId)
                      + " ("
                      + jobId
                      + ") for track "
                      + result.getId(),
                  e);
        }
      }
    }
    return plotItems.toArray(new PageItem[plotItems.size()]);
  }