/**
  * Constructor, defaults to undirected graph.
  *
  * @param nodes_list a <code>List</code> of <code>giny.model.Node</code> objects for which
  *     distances will be calculated
  * @param distances the 2 dimensional array that will hold the calculated distances (possibly
  *     null)
  * @param perspective the <code>giny.model.GraphPerspective</code> in which the nodes reside
  * @deprecated Can't use this now that GraphPerspective uses root graph indices.
  */
 public NodeDistances(List nodesList, CyNetwork network, Map nodeIndexToMatrixIndexMap) {
   this.nodesList = nodesList;
   this.nodeIndexToMatrixIndexMap = nodeIndexToMatrixIndexMap;
   this.network = network;
   distances = new int[nodesList.size()][];
   directed = false;
 }
  /** {@inheritDoc} */
  @Override
  public Evidence[] mapEdgeToEvidence(Graph graph, Edge edge) {
    if (graph == null) throw new NullPointerException("graph cannot be null");
    if (edge == null) throw new NullPointerException("edge cannot be null");

    if (edge.metadata == null) return new Evidence[0];
    Object evidences = edge.metadata.get("evidences");
    if (evidences == null || !(evidences instanceof List)) return new Evidence[0];
    List<Map> evidenceMap = typedList(((List) evidences), Map.class);

    List<Evidence> evidenceList = new ArrayList<Evidence>();
    for (Map item : evidenceMap) {
      @SuppressWarnings("unchecked")
      Map<String, Object> evMap = (Map<String, Object>) item;

      Evidence ev = new Evidence();
      ev.belStatement = getOrEmptyString("bel_statement", evMap).replace("\\\"", "\"");
      ev.summaryText = getOrEmptyString("summary_text", evMap);
      Citation citation = new Citation();
      @SuppressWarnings("unchecked")
      Map<String, Object> citationMap = (Map<String, Object>) evMap.get("citation");
      if (citationMap != null) {
        citation.id = getOrEmptyString("id", citationMap);
        citation.type = getOrEmptyString("type", citationMap);
        citation.name = getOrEmptyString("name", citationMap);
      }
      ev.citation = citation;
      BiologicalContext context = new BiologicalContext();
      @SuppressWarnings("unchecked")
      Map<String, Object> contextMap = (Map<String, Object>) evMap.get("biological_context");
      if (contextMap != null) {
        context.speciesCommonName = getOrEmptyString("species_common_name", contextMap);
        context.ncbiTaxId = getOrZero("ncbi_tax_id", contextMap);
        Set<String> varying = new HashSet<String>(contextMap.keySet());
        varying.removeAll(asList("species_common_name", "ncbi_tax_id"));
        for (String key : varying) {
          context.variedAnnotations.put(key, contextMap.get(key));
        }
        ev.biologicalContext = context;
      }
      evidenceList.add(ev);
    }

    return evidenceList.toArray(new Evidence[evidenceList.size()]);
  }
  /** {@inheritDoc} */
  @Override
  public Evidence[] mapFromTable(CyEdge edge, CyTable table) {
    if (edge == null) throw new NullPointerException("edge cannot be null");
    if (table == null) throw new NullPointerException("table cannot be null");

    Set<String> nonAnnotationColumns =
        new HashSet<String>(
            asList(
                CyNetwork.SUID,
                NETWORK_SUID,
                NETWORK_NAME,
                EDGE_SUID,
                BEL_STATEMENT,
                SUMMARY_TEXT,
                CITATION_ID,
                CITATION_NAME,
                CITATION_TYPE,
                SPECIES));

    Collection<CyRow> evidenceRows = table.getMatchingRows(EDGE_SUID, edge.getSUID());
    List<Evidence> evidences = new ArrayList<Evidence>(evidenceRows.size());
    if (!evidenceRows.isEmpty()) {
      for (CyRow evRow : evidenceRows) {
        Evidence e = new Evidence();
        e.belStatement = evRow.get(BEL_STATEMENT, String.class);
        e.summaryText = evRow.get(SUMMARY_TEXT, String.class);
        e.citation = new Citation();
        e.citation.id = evRow.get(CITATION_ID, String.class);
        e.citation.name = evRow.get(CITATION_NAME, String.class);
        e.citation.type = evRow.get(CITATION_TYPE, String.class);
        e.biologicalContext = new BiologicalContext();
        e.biologicalContext.speciesCommonName = evRow.get(SPECIES, String.class);
        e.biologicalContext.variedAnnotations = new HashMap<String, Object>();
        for (Entry<String, Object> columnValue : evRow.getAllValues().entrySet()) {
          if (nonAnnotationColumns.contains(columnValue.getKey())) continue;
          e.biologicalContext.variedAnnotations.put(columnValue.getKey(), columnValue.getValue());
        }
        evidences.add(e);
      }
    }

    return evidences.toArray(new Evidence[evidences.size()]);
  }
  /**
   * Calculates the node distances.
   *
   * @return the <code>int[][]</code> array of calculated distances or null if the task was canceled
   *     or there was an error
   */
  public int[][] calculate() {
    int currentProgress = 0;
    this.maxValue = distances.length;

    CyNode[] nodes = new CyNode[nodesList.size()];

    // TODO: REMOVE
    // System.err.println( "Calculating all node distances.. for: "
    // +nodesList.size()+" and "+nodes.length );

    // We don't have to make new Integers all the time, so we store the index
    // Objects in this array for reuse.
    Integer[] integers = new Integer[nodes.length];

    // Fill the nodes array with the nodes in their proper index locations.
    // int index;
    CyNode from_node;

    for (int i = 0; i < nodes.length; i++) {

      from_node = (CyNode) nodesList.get(i);
      if (from_node == null) {
        continue;
      }
      int index = ((Integer) nodeIndexToMatrixIndexMap.get(from_node.getSUID())).intValue();
      // index = ((Integer) nodeIndexToMatrixIndexMap.get(new
      // Integer(from_node.getRootGraphIndex()))).intValue();

      if ((index < 0) || (index >= nodes.length)) {
        System.err.println(
            "WARNING: GraphNode \""
                + from_node
                + "\" has an index value that is out of range: "
                + index
                + ".  Graph indices should be maintained such "
                + "that no index is unused.");
        return null;
      }
      if (nodes[index] != null) {
        System.err.println(
            "WARNING: GraphNode \""
                + from_node
                + "\" has an index value ( "
                + index
                + " ) that is the same as "
                + "that of another GraphNode ( \""
                + nodes[index]
                + "\" ).  Graph indices should be maintained such "
                + "that indices are unique.");
        return null;
      }
      nodes[index] = from_node;
      Integer in = new Integer(index);
      integers[index] = in;
    }

    LinkedList queue = new LinkedList();
    boolean[] completed_nodes = new boolean[nodes.length];
    Iterator neighbors;
    CyNode to_node;
    CyNode neighbor;
    int neighbor_index;
    int to_node_distance;
    int neighbor_distance;
    for (int from_node_index = 0; from_node_index < nodes.length; from_node_index++) {

      if (this.interrupted) {
        // The task was canceled
        this.distances = null;
        return this.distances;
      }

      from_node = nodes[from_node_index];

      if (from_node == null) {
        // Make the distances in this row all Integer.MAX_VALUE.
        if (distances[from_node_index] == null) {
          distances[from_node_index] = new int[nodes.length];
        }
        Arrays.fill(distances[from_node_index], Integer.MAX_VALUE);
        continue;
      }

      // TODO: REMOVE
      //  System.err.print( "Calculating node distances from graph node " +
      //                  from_node );
      // System.err.flush();

      // Make the distances row and initialize it.
      if (distances[from_node_index] == null) {
        distances[from_node_index] = new int[nodes.length];
      }
      Arrays.fill(distances[from_node_index], Integer.MAX_VALUE);
      distances[from_node_index][from_node_index] = 0;

      // Reset the completed nodes array.
      Arrays.fill(completed_nodes, false);

      // Add the start node to the queue.
      queue.add(integers[from_node_index]);

      while (!(queue.isEmpty())) {

        if (this.interrupted) {
          // The task was canceled
          this.distances = null;
          return this.distances;
        }

        int index = ((Integer) queue.removeFirst()).intValue();
        if (completed_nodes[index]) {
          continue;
        }
        completed_nodes[index] = true;

        to_node = nodes[index];
        to_node_distance = distances[from_node_index][index];

        if (index < from_node_index) {
          // Oh boy.  We've already got every distance from/to this node.
          int distance_through_to_node;
          for (int i = 0; i < nodes.length; i++) {
            if (distances[index][i] == Integer.MAX_VALUE) {
              continue;
            }
            distance_through_to_node = to_node_distance + distances[index][i];
            if (distance_through_to_node <= distances[from_node_index][i]) {
              // Any immediate neighbor of a node that's already been
              // calculated for that does not already have a shorter path
              // calculated from from_node never will, and is thus complete.
              if (distances[index][i] == 1) {
                completed_nodes[i] = true;
              }
              distances[from_node_index][i] = distance_through_to_node;
            }
          } // End for every node, update the distance using the distance from
          // to_node.
          // So now we don't need to put any neighbors on the queue or
          // anything, since they've already been taken care of by the previous
          // calculation.
          continue;
        } // End if to_node has already had all of its distances calculated.

        neighbors = getNeighbors(to_node).iterator();

        // neighbors = perspective.neighborsList(to_node).iterator();

        while (neighbors.hasNext()) {

          if (this.interrupted) {
            this.distances = null;
            return this.distances;
          }

          neighbor = (CyNode) neighbors.next();

          neighbor_index = ((Integer) nodeIndexToMatrixIndexMap.get(neighbor.getSUID())).intValue();

          // neighbor_index = ((Integer) nodeIndexToMatrixIndexMap.get(new
          // Integer(neighbor.getRootGraphIndex()))).intValue();

          // If this neighbor was not in the incoming List, we cannot include
          // it in any paths.
          if (nodes[neighbor_index] == null) {
            distances[from_node_index][neighbor_index] = Integer.MAX_VALUE;
            continue;
          }

          if (completed_nodes[neighbor_index]) {
            // We've already done everything we can here.
            continue;
          }

          neighbor_distance = distances[from_node_index][neighbor_index];

          if ((to_node_distance != Integer.MAX_VALUE)
              && (neighbor_distance > (to_node_distance + 1))) {
            distances[from_node_index][neighbor_index] = (to_node_distance + 1);
            queue.addLast(integers[neighbor_index]);
          }

          // TODO: REMOVE
          // System.out.print( "." );
          // System.out.flush();

        } // For each of the next nodes' neighbors
        // TODO: REMOVE
        // System.out.print( "|" );
        // System.out.flush();
      } // For each to_node, in order of their (present) distances

      // TODO: REMOVE
      /*
      System.err.println( "done." );
      */
      // Calculate Percentage.  This must be a value between 0..100.
      int percentComplete = (int) (((double) currentProgress / maxValue) * 100);

      //  Estimate Time Remaining
      long timeRemaining = maxValue - currentProgress;

      //  Update the Task Monitor.
      //  This automatically updates the UI Component w/ progress bar.
      if (taskMonitor != null) {
        taskMonitor.setProgress(percentComplete);
        taskMonitor.setStatusMessage(
            "Calculating Node Distances: " + currentProgress + "of " + maxValue);
        // taskMonitor.setEstimatedTimeRemaining(timeRemaining);
      }

      currentProgress++;
    } // For each from_node

    // TODO: REMOVE
    // System.err.println( "..Done calculating all node distances." );

    return distances;
  } // calculate