content: /*************************************************************************
  *                                                                        *
  *  This file is part of the 20n/act project.                             *
  *  20n/act enables DNA prediction for synthetic biology/bioengineering.  *
  *  Copyright (C) 2017 20n Labs, Inc.                                     *
  *                                                                        *
  *  Please direct all queries to act@20n.com.                             *
  *                                                                        *
  *  This program is free software: you can redistribute it and/or modify  *
  *  it under the terms of the GNU General Public License as published by  *
  *  the Free Software Foundation, either version 3 of the License, or     *
  *  (at your option) any later version.                                   *
  *                                                                        *
  *  This program is distributed in the hope that it will be useful,       *
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of        *
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
  *  GNU General Public License for more details.                          *
  *                                                                        *
  *  You should have received a copy of the GNU General Public License     *
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>. *
  *                                                                        *
  *************************************************************************/
  
  package com.act.biointerpretation.l2expansion;
  
  import chemaxon.calculations.clean.Cleaner;
  import chemaxon.formats.MolFormatException;
  import chemaxon.formats.MolImporter;
  import chemaxon.struc.RxnMolecule;
  import com.act.biointerpretation.mechanisminspection.ReactionRenderer;
  import org.apache.commons.lang3.StringUtils;
  import org.apache.logging.log4j.LogManager;
  import org.apache.logging.log4j.Logger;
  
  import java.io.File;
  import java.io.IOException;
  import java.util.HashMap;
  import java.util.Map;
  
  /**
   * Used to create drawings of a PredictionCorpus's predictions, for manual curation.
   */
  public class PredictionCorpusRenderer {
  
    private static final Logger LOGGER = LogManager.getFormatterLogger(PredictionCorpusRenderer.class);
  
    // The filename to which to print the corpus as a json file.
    private static final String PREDICTION_CORPUS_FILE_NAME = "predictions.json";
  
    // The filename to which to print the raw list of inchis for bing search analysis
    private static final String INCHI_LIST_FILE_NAME = "prediction_inchis";
  
    // Settings for printing images
    ReactionRenderer reactionRenderer;
  
    public PredictionCorpusRenderer(ReactionRenderer reactionRenderer) {
      this.reactionRenderer = reactionRenderer;
    }
  
    /**
     * Renders the prediction corpus into the specified directory.  Prints an image of each predicted reaction, prints
     * an image of each reactor used in the corpus, and prints the corpus itself to a json file.
     *
     * @param predictionCorpus The corpus to render.
     * @param imageDirectory The directory in which to put the files.
     */
    public void renderCorpus(L2PredictionCorpus predictionCorpus, File imageDirectory) throws IOException {
  
      // Build files for images and corpus
      Map<Integer, File> predictionFileMap = getPredictionFileMap(predictionCorpus, imageDirectory);
  
      File outCorpusFile = new File(imageDirectory, PREDICTION_CORPUS_FILE_NAME);
      File inchiListFile = new File(imageDirectory, INCHI_LIST_FILE_NAME);
  
      // Print reaction images to file.
      for (L2Prediction prediction : predictionCorpus.getCorpus()) {
        try {
          reactionRenderer.drawRxnMolecule(getRxnMolecule(prediction), predictionFileMap.get(prediction.getId()));
        } catch (IOException e) {
          LOGGER.error("Couldn't render prediction %d. %s", prediction.getId(), e.getMessage());
        }
      }
  
      // Print prediction corpus to file.
      try {
        predictionCorpus.writePredictionsToJsonFile(outCorpusFile);
      } catch (IOException e) {
        LOGGER.error("Couldn't print prediction corpus to file.");
      }
  
      try {
        L2InchiCorpus inchiCorpus = new L2InchiCorpus(predictionCorpus.getUniqueProductInchis());
        inchiCorpus.writeToFile(inchiListFile);
      } catch (IOException e) {
        LOGGER.error("Couldn't print product inchis to file.");
      }
    }
  
    /**
     * Create a file for each prediction drawing,and return a map from prediction ids to those files.
     *
     * @param predictionCorpus The prediction corpus.
     * @param imageDir The directory in which the files should be located.
     * @return A map from prediction id to the corresponding prediction's file.
     */
    private Map<Integer, File> getPredictionFileMap(L2PredictionCorpus predictionCorpus, File imageDir) {
      Map<Integer, File> fileMap = new HashMap<Integer, File>();
  
      for (L2Prediction prediction : predictionCorpus.getCorpus()) {
        String fileName = getPredictionFileName(prediction) + "." + reactionRenderer.getFormat();
        fileMap.put(prediction.getId(), new File(imageDir, fileName));
      }
  
      return fileMap;
    }
  
    private RxnMolecule getRxnMolecule(L2Prediction prediction)
        throws MolFormatException {
  
      RxnMolecule renderedReactionMolecule = new RxnMolecule();
  
      // Add substrates and products to molecule.
      for (String substrate : prediction.getSubstrateInchis()) {
        renderedReactionMolecule.addComponent(MolImporter.importMol(substrate), RxnMolecule.REACTANTS);
      }
      for (String product : prediction.getProductInchis()) {
        renderedReactionMolecule.addComponent(MolImporter.importMol(product), RxnMolecule.PRODUCTS);
      }
  
      // Calculate coordinates with a 2D coordinate system.
      Cleaner.clean(renderedReactionMolecule, 2, null);
  
      // Change the reaction arrow type.
      renderedReactionMolecule.setReactionArrowType(RxnMolecule.REGULAR_SINGLE);
  
      return renderedReactionMolecule;
    }
  
    private String getPredictionFileName(L2Prediction prediction) {
      return StringUtils.join("PREDICTION_", prediction.getId());
    }
  }
  