content: /*
   * The MIT License (MIT)
   *
   * Copyright (c) 2011-2021 Broad Institute, Aiden Lab, Rice University, Baylor College of Medicine
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   *
   *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   *  FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
   *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   *  THE SOFTWARE.
   */
  
  package juicebox.track;
  
  import juicebox.HiC;
  import juicebox.MainWindow;
  import juicebox.gui.SuperAdapter;
  import org.apache.commons.io.FilenameUtils;
  import org.broad.igv.util.ResourceLocator;
  import org.broad.igv.util.Utilities;
  import org.w3c.dom.*;
  import org.xml.sax.SAXException;
  
  import javax.swing.*;
  import javax.xml.parsers.DocumentBuilderFactory;
  import javax.xml.parsers.ParserConfigurationException;
  import java.awt.event.ActionEvent;
  import java.io.File;
  import java.io.IOException;
  import java.io.InputStream;
  import java.net.URISyntaxException;
  import java.net.URL;
  import java.util.ArrayList;
  import java.util.Enumeration;
  import java.util.LinkedHashSet;
  import java.util.List;
  import java.util.jar.JarEntry;
  import java.util.jar.JarFile;
  
  
  /**
   * @author jrobinso
   */
  public class LoadAction extends AbstractAction {
  
      private static final long serialVersionUID = 9000034;
      private final JFrame parentFrame;
      private final MainWindow mainWindow;
      private final HiC hic;
      private Runnable repaint1DLayersPanel = null;
  
  
      public LoadAction(String s, MainWindow mainWindow, HiC hic, Runnable repaint1DLayersPanel) {
          super(s);
          this.parentFrame = mainWindow;
          this.mainWindow = mainWindow;
          this.hic = hic;
          this.repaint1DLayersPanel = repaint1DLayersPanel;
      }
  
      private static Document createMasterDocument(String xmlUrl, JFrame parentFrame) throws ParserConfigurationException {
  
          StringBuffer buffer = new StringBuffer();
  
          Document masterDocument = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
  
          try {
              Document xmlDocument = readXMLDocument(xmlUrl, buffer);
  
              if (xmlDocument != null) {
                  Element global = xmlDocument.getDocumentElement();
                  masterDocument.appendChild(masterDocument.importNode(global, true));
              } else {
                  masterDocument = null;
              }
          } catch (Exception e) {
              String message = "Cannot create an XML Document from " + xmlUrl;
              System.err.println(message + " " + e.getLocalizedMessage());
          }
  
          if (buffer.length() > 0) {
              String message = "<html>The following urls could not be processed due to load failures:<br>" + buffer.toString();
              JOptionPane.showMessageDialog(parentFrame, message);
          }
  
          return masterDocument;
  
      }
  
      private static Document readXMLDocument(String url, StringBuffer errors) {
          InputStream is;
          Document xmlDocument = null;
          is = LoadAction.class.getResourceAsStream(url);
          if (is == null) {
              System.err.println(url + " doesn't exist, so cannot read default annotations");
              return null;
          }
          try {
              xmlDocument = Utilities.createDOMDocumentFromXmlStream(is);
  
              xmlDocument = resolveIncludes(xmlDocument, errors);
  
          } catch (SAXException e) {
              System.err.println("Invalid XML resource: " + url + " " + e.getLocalizedMessage());
              errors.append(url).append("<br><i>").append(e.getMessage());
          } catch (java.net.SocketTimeoutException e) {
              System.err.println("Connection time out " + e.getLocalizedMessage());
              errors.append(url).append("<br><i>Connection time out");
          } catch (IOException e) {
              System.err.println("Error accessing " + url + " " + e.getLocalizedMessage());
              errors.append(url).append("<br><i>").append(e.getMessage());
          } catch (ParserConfigurationException e) {
              System.err.println("Parser configuration error for:" + url + " " + e.getLocalizedMessage());
              errors.append(url).append("<br><i>").append(e.getMessage());
          } finally {
              try {
                  is.close();
              } catch (IOException e) {
                  System.err.println("Error closing stream for: " + url + " " + e.getLocalizedMessage());
              }
          }
          return xmlDocument;
      }
  
      private static Document resolveIncludes(Document document, StringBuffer errors) {
  
          NodeList includeNodes = document.getElementsByTagName("Include");
          if (includeNodes.getLength() == 0) {
              return document;
          }
  
          int size = includeNodes.getLength();
          // Copy the nodes as we'll be modifying the tree.  This is neccessary!
          Node[] tmp = new Node[size];
          for (int i = 0; i < size; i++) {
              tmp[i] = includeNodes.item(i);
          }
  
          for (Node item : tmp) {
              NamedNodeMap nodeMap = item.getAttributes();
              if (nodeMap == null) {
                  System.out.println("XML node " + item.getNodeName() + " has no attributes");
              } else {
                  Attr path = (Attr) item.getAttributes().getNamedItem("path");
                  if (path == null) {
                      System.out.println("XML node " + item.getNodeName() + " is missing a path attribute");
                  } else {
                      Node parent = item.getParentNode();
  
                      //System.out.println("Loading node " + path.getValue());
                      Document doc = readXMLDocument(path.getValue(), errors);
                      if (doc != null) {
                          Element global = doc.getDocumentElement();
                          Node expandedNode = parent.getOwnerDocument().importNode(global, true);
                          parent.replaceChild(expandedNode, item);
                      }
                  }
              }
          }
  
  
          return document;
  
      }
  
      private String getXmlUrl() {
          String genome = hic.getDataset().getGenomeId();
          if (genome == null) {
              genome = "hg19";
          }
  
          return "tracksMenu_" + genome + ".xml";
      }
  
      @Override
      public void actionPerformed(ActionEvent evt) {
          if (hic.getDataset() == null) {
              JOptionPane.showMessageDialog(parentFrame, "File must be loaded to load annotations",
                      "Error", JOptionPane.ERROR_MESSAGE);
              return;
          }
          String genome = hic.getDataset().getGenomeId();
          if (genome == null) {
              genome = "hg19";
          }
  
          String xmlURL = "tracksMenu_" + genome + ".xml";
          safeLoadNodes(xmlURL);
      }
  
      private void safeLoadNodes(final String xmlFile) {
          Runnable runnable = new Runnable() {
              @Override
              public void run() {
                  List<ResourceLocator> locators = unsafeLoadNodes(xmlFile);
                  if (locators != null && !locators.isEmpty()) {
                      // TODO MSS
                      hic.unsafeLoadHostedTracks(locators);
                  }
                  if (repaint1DLayersPanel != null) {
                      repaint1DLayersPanel.run();
                  }
              }
          };
          mainWindow.executeLongRunningTask(runnable, "safe load nodes");
      }
  
      public void checkBoxesForReload(String track) {
          // TODO MSS
          ResourceTree resourceTree = hic.getResourceTree();
          try {
              if (resourceTree == null) {
                  Document tempDoc = createMasterDocument(getXmlUrl(), parentFrame);
                  resourceTree = new ResourceTree(hic, tempDoc);
                  resourceTree.checkTrackBoxesForReloadState(track.trim());
              }
          } catch (Exception e) {
              e.printStackTrace();
          }
          resourceTree.checkTrackBoxesForReloadState(track);
      }
  
      private ArrayList<String> getAvaliableXmlResourses() throws Exception {
  
          String resourses_path = "juicebox/track";
          ArrayList<String> resourses = new ArrayList<>();
          File jarFile = new File(LoadAction.class.getProtectionDomain().getCodeSource()
                  .getLocation().getPath());
          if(jarFile.isFile()) {  // Run with JAR file
              System.out.println("Within JAR");
              final JarFile jar = new JarFile(jarFile);
              final Enumeration<JarEntry> entries = jar.entries(); //gives ALL entries in jar
              while(entries.hasMoreElements()) {
                  String name = entries.nextElement().getName();
                  if (name.startsWith(resourses_path + "/tracksMenu") &&
                          name.endsWith(".xml")) { //filter according to the path
                      File f = new File(name);
                      resourses.add(f.getName());
                  }
              }
              jar.close();
          } else { // Run with IDE
              System.out.println("Within IDE");
              URL url = MainWindow.class.getResource("/" + resourses_path);
              if (url != null) {
                  try {
                      final File apps = new File(url.toURI());
                      for (File app : apps.listFiles()) {
                          System.out.println(app.toString());
                          if (app.toString().endsWith(".xml")) {
                              resourses.add(FilenameUtils.getName(app.getPath()));
                          }
                      }
                  } catch (URISyntaxException ex) {
                      // never happens
                      ex.fillInStackTrace();
                  }
              }
          }
          return resourses;
      }
  
  
  
      private List<ResourceLocator> unsafeLoadNodes(String xmlFile) {
  
          // TODO MSS
          ResourceTree resourceTree = hic.getResourceTree();
  
          try {
              if (resourceTree == null) {
                  Document masterDocument = createMasterDocument(xmlFile, parentFrame);
                  if (masterDocument == null){
                      ArrayList<String> avaliable_resourses = getAvaliableXmlResourses();
                      if (avaliable_resourses.size()>0) {
                          String[] options = new String[avaliable_resourses.size()];
                          options = avaliable_resourses.toArray(options);
                          String selected_option = (String) JOptionPane.showInputDialog(
                                  null,
                                  "No tracks found for the genome \n " +
                                          "Would you like to use one of avaliable tracks below?" +
                                          hic.getDataset().getGenomeId(),
                                  "Please select genome",
                                  JOptionPane.QUESTION_MESSAGE,
                                  null,
                                  options,
                                  options[0]
                          );
                          if (selected_option != null) {
                              System.out.println("Using genome data from " +
                                      selected_option);
                              masterDocument = createMasterDocument(selected_option,
                                      parentFrame);
                          }
                      }
                  }
                  resourceTree = new ResourceTree(hic, masterDocument);
              }
          } catch (Exception e) {
              System.err.println("Could not load from server" + e.getLocalizedMessage());
              SuperAdapter.showMessageDialog("Could not load from server: " + e.getMessage());
              return null;
          }
  
          resourceTree.showResourceTreeDialog(parentFrame);
  
          LinkedHashSet<ResourceLocator> selectedLocators = resourceTree.getLocators();
          LinkedHashSet<ResourceLocator> deselectedLocators = resourceTree.getDeselectedLocators();
          List<ResourceLocator> newLoadList = new ArrayList<>();
  
          boolean repaint = false;
  
          if (selectedLocators != null) {
              for (ResourceLocator locator : selectedLocators) {
                  try {
  
                      if (locator.getType() != null && locator.getType().equals("norm")) {
                          hic.loadCoverageTrack(locator.getPath());
                      } else if (locator.getType() != null && locator.getType().equals("eigenvector")) {
                          hic.loadEigenvectorTrack();
  
                      } else newLoadList.add(locator);
  
                  } catch (Exception e) {
                      System.err.println("Could not load selected locator" + e.getLocalizedMessage());
                      SuperAdapter.showMessageDialog("Could not load selection: " + e.getMessage());
                      deselectedLocators.add(locator);
                  }
              }
          }
  
          if (deselectedLocators != null) {
              for (ResourceLocator locator : deselectedLocators) {
                  System.out.println("Removing " + locator.getName());
                  hic.removeTrack(locator);
                  resourceTree.remove(locator);
              }
          }
          if (repaint) {
              mainWindow.repaint();
          }
          //hic.setShowLoops(true);
          return newLoadList;
      }
  
  }
  