package org.gbif.checklistbank.neo.traverse;

import org.gbif.dwc.terms.DwcTerm;

import javax.annotation.Nullable;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterators;
import com.google.common.collect.PeekingIterator;
import com.yammer.metrics.Meter;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Path;
import org.neo4j.graphdb.Transaction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 */
public class TaxonWalker {

  private static final Logger LOG = LoggerFactory.getLogger(TaxonWalker.class);

  public static void walkAll(GraphDatabaseService db, StartEndHandler handler) {
    walkAccepted(db, handler, 10000, null);
  }

  /**
   * Walks allAccepted nodes in a transaction that is renewed in a given batchsize
   *
   * @param batchsize number of paths that should be walked within a single open transaction
   */
  public static void walkAccepted(
    GraphDatabaseService db,
    StartEndHandler handler,
    int batchsize,
    @Nullable Meter meter
  ) {
    Path lastPath = null;
    long counter = 0;
    Transaction tx = db.beginTx();
    try {
      for (Path p : TaxonomicIterator.allAccepted(db)) {
        if (batchsize > 0 && counter % batchsize == 0) {
          tx.success();
          tx.close();
          LOG.debug("Opening new transaction, record {}", counter);
          if (meter != null) {
            LOG.debug("Processing rate = {}", meter.getMeanRate());
          }
          tx = db.beginTx();
        }
        if (meter != null) {
          meter.mark();
        }
        logPath(p);
        if (lastPath != null) {
          PeekingIterator<Node> lIter = Iterators.peekingIterator(lastPath.nodes().iterator());
          PeekingIterator<Node> cIter = Iterators.peekingIterator(p.nodes().iterator());
          while (lIter.hasNext() && cIter.hasNext() && lIter.peek().equals(cIter.peek())) {
            lIter.next();
            cIter.next();
          }
          // only non shared nodes left.
          // first close allAccepted old nodes, then open new ones
          // reverse order for closing nodes...
          for (Node n : ImmutableList.copyOf(lIter).reverse()) {
            handler.end(n);
          }
          while (cIter.hasNext()) {
            handler.start(cIter.next());
          }

        } else {
          // only new nodes
          for (Node n : p.nodes()) {
            handler.start(n);
          }
        }
        lastPath = p;
        counter++;
      }
      // close all remaining nodes
      if (lastPath != null) {
        for (Node n : ImmutableList.copyOf(lastPath.nodes()).reverse()) {
          handler.end(n);
        }
      }

      tx.success();

    } finally {
      tx.close();
    }
  }

  private static void logPath(Path p) {
    StringBuilder sb = new StringBuilder();
    for (Node n : p.nodes()) {
      if (sb.length() > 0) {
        sb.append(" -- ");
      }
      sb.append((String) n.getProperty(DwcTerm.scientificName.simpleName()));
    }
    LOG.debug(sb.toString());
  }

}

@@ -1619,16 +1619,18 @@
         
+//
 logPath(
@@ -3043,16 +3043,27 @@
 leName()
+, %22no name%22
 ));%0A    

Remove path debug logging from metrics handler