/**
   * If the file containing the data information does exist, open the data from the {@link
   * #imageDataFile XML file}, otherwise, generate the data: first open the color information
   * ({@link StainMaker Mahalanobis Cubes}), then retrieve the list of project images ({@link
   * GemIdentTools.Thumbnails#GetImageList(String)}), then initialize data and the thread pool, then
   * thread a {@link ImageSetHeuristics.ImageInfoGatherer ImageInfoGatherer} to collect data for all
   * project images, and finally, it saves the data by serializing itself to the {@link
   * #imageDataFile XML file}. For discussion on the usefulness of this phenotype training helper,
   * consult section 3.2.3 of the manual.
   *
   * @see <a href="http://www.gemident.com/manual.html">the GemIdent manual</a>
   */
  public void run() {
    if (!IOTools.FileExists(imageDataFile)) {
      //			System.out.println("\nbegin image set heuristics");
      if (Run.it.imageset
          instanceof
          ImageSetInterfaceWithUserColors) { // this is ugly but conceptually it's the only way to
        // go I believe
        ((ImageSetInterfaceWithUserColors) Run.it.imageset).OpenMahalanobisCubes(null);
      }
      //			System.out.println("after Mahalanobis Cubes opened:"+Run.TimeElapsed(time_o)+"s");

      //			PrintFileHeader();
      String[] filenames = Thumbnails.GetImageList(Run.it.imageset.getHomedir());
      data = new HashMap<String, double[]>(filenames.length);

      postprocessPool = Executors.newFixedThreadPool(Run.it.num_threads);
      update = 100 / ((double) filenames.length);

      for (String filename : filenames) postprocessPool.execute(new ImageInfoGatherer(filename));
      postprocessPool.shutdown();
      try {
        postprocessPool.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS); // effectively infinity
      } catch (InterruptedException ignored) {
      }
      postprocessPool = null;

      IOTools.saveToXML(this, Run.it.imageset.getFilenameWithHomePath(imageDataFile));
      //			System.out.println("\nfinished image set heuristics\n\n\n\n");
    } else {
      progress.setVisible(false);
      ImageSetHeuristics open =
          (ImageSetHeuristics)
              IOTools.openFromXML(Run.it.imageset.getFilenameWithHomePath(imageDataFile));
      data = open.data;
    }
  }
  /**
   * Creates the portion of the html file that displays thumbnails for a given cluster
   *
   * @param out writes the html file to the hard disk
   * @param images the images in this cluster
   * @param c the cluster number
   * @param numThumbPerRow the maximum number of thumbnails to put on a row
   * @param tWidth the width of the thumbnails
   * @param tHeight the height of the thumbnails
   */
  private void CreateHTMLCluster(
      PrintWriter out, Set<String> images, int c, int numThumbPerRow, int tWidth, int tHeight) {
    out.print("<br>");
    out.print("\n");
    out.print("<br>");
    out.print("\n");
    out.print("<H3 align=center>Subset #" + c + "</H3>");
    out.print("\n");

    out.print("<table width=" + TableWidth + " align=center>");
    out.print("\n");

    Object[] imageArray = images.toArray();
    int numImages = imageArray.length;
    //		System.out.println("cluster:"+i+" "+images.size()+" images");
    for (int i = 0; i < numImages; ) {
      //			System.out.println("image:"+images.get(i));

      out.print("<tr>");
      out.print("\n");
      for (int w = 0; w < numThumbPerRow; w++) {
        if (i + w < numImages) {
          out.print("<td width=" + tWidth + " height=" + tHeight + " align=center>");
          out.print("\n");
          out.print(
              "<a href=\""
                  + imageArray[i + w]
                  + "\"><img src="
                  + Thumbnails.getThumbnailFilename((String) imageArray[i + w])
                  + " alt="
                  + imageArray[i + w]
                  + " width="
                  + tWidth
                  + " height="
                  + tHeight
                  + " hspace=0 vspace=0 border=0></a>");
          out.print("\n");
          out.print("</td>");
          out.print("\n");
        }
      }
      out.print("</tr>");
      out.print("\n");

      out.print("<tr>");
      out.print("\n");
      for (int w = 0; w < numThumbPerRow; w++) {
        if (i + w < numImages) {
          out.print("<td width=" + tWidth + " align=center>");
          out.print("\n");
          out.print("<a href=\"" + imageArray[i + w] + "\">" + imageArray[i + w] + "</a>");
          out.print("\n");
          out.print("</td>");
          out.print("\n");
        }
      }
      out.print("</tr>");
      out.print("\n");
      i += numThumbPerRow;
    }
    out.print("</table>");
    out.print("\n");
  }
  /**
   * Creates the global image composed of the thumbnails of the subimages, not valid for all image
   * sets
   *
   * @param out writes the html file to the hard disk
   * @param factor the {@link GemIdentImageSets.ImageSetInterface.Size Size} factor
   * @param clusters a list of the cluster sets
   * @param tWidth the width of the thumbnails
   * @param tHeight the height of the thumbnails
   */
  private void CreateHTMLForGlobal(
      PrintWriter out, int factor, ArrayList<Set<String>> clusters, int tWidth, int tHeight) {

    StringMatrix picFilenameTable = (StringMatrix) Run.it.imageset.getPicFilenameTable();

    int borderSize = Math.max(1, (int) Math.round(borderSizeXL / ((double) factor)));

    out.print("<br>");
    out.print("\n");
    out.print("<br>");
    out.print("\n");
    out.print("<table cellspacing=0 cellpadding=0 border=1 bordercolor=BLACK>");
    out.print("\n");

    for (int i = 0; i < picFilenameTable.getWidth(); i++) {
      out.print("<tr>");
      out.print("\n");
      for (int j = 0; j < picFilenameTable.getHeight(); j++) {
        String filename = picFilenameTable.get(i, j);
        if (filename != ImageSetInterface.PIC_NOT_PRESENT) {
          String rgbColorString = GetClusterColor(filename, clusters);
          out.print(
              "<td height="
                  + ((tHeight / factor) + 2 * borderSize)
                  + " width="
                  + ((tWidth / factor) + 2 * borderSize)
                  + " bgcolor=rgb("
                  + rgbColorString
                  + ") border=5 bordercolor=white>");
          if (Run.it.imageset.PicWasClassified(filename))
            out.print(
                "<a href=\""
                    + PostProcess.GetBothName(filename)
                    + ".jpg\"><img src="
                    + Thumbnails.getThumbnailFilename(filename)
                    + " alt="
                    + filename
                    + " width="
                    + (tWidth / factor)
                    + " height="
                    + (tHeight / factor)
                    + " border=0 hspace="
                    + borderSize
                    + " align=center valign=center)></a>");
          else
            out.print(
                "<a href=\""
                    + filename
                    + "\"><img src="
                    + Thumbnails.getThumbnailFilename(filename)
                    + " alt="
                    + filename
                    + " width="
                    + (tWidth / factor)
                    + " height="
                    + (tHeight / factor)
                    + " border=0 hspace="
                    + borderSize
                    + " align=center valign=center)></a>");
        } else out.print("<td>");
        out.print("</td>");
        out.print("\n");
      }
      out.print("</tr>");
      out.print("\n");
    }
    out.print("</table>");
    out.print("\n");
  }