package com.orientechnologies.orient.core.iterator;

import java.util.HashSet;
import java.util.Set;

import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx;
import com.orientechnologies.orient.core.metadata.schema.OClass;
import com.orientechnologies.orient.core.metadata.schema.OSchema;
import com.orientechnologies.orient.core.metadata.schema.OType;
import com.orientechnologies.orient.core.record.impl.ODocument;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

/**
 * @author Artem Loginov
 */
@Test
public class ClassIteratorTest {
  private static final boolean       RECREATE_DATABASE = true;
  private static ODatabaseDocumentTx db                = null;
  private Set<String>                names;

  @BeforeMethod
  public void setUp() throws Exception {
    initializeDatabase();

    // Insert some data
    names = new HashSet<String>();
    names.add("Adam");
    names.add("Bob");
    names.add("Calvin");
    names.add("Daniel");

    for (String name : names) {
      createPerson(name);
    }
  }

  @AfterClass
  public void tearDown() throws Exception {
    if (!db.isClosed())
      db.close();
  }

  @Test
  public void testIteratorShouldReuseRecordWithoutNPE() {

    // Use class iterator.
    // browseClass() returns all documents in RecordID order
    // (including subclasses, which shouldn't exist for Person)
    final ORecordIteratorClass<ODocument> personIter = db.browseClass("Person");

    // Setting this to true causes the bug. Setting to false it works fine.
    personIter.setReuseSameRecord(true);

    int docNum = 0;
    // Explicit iterator loop.
    while (personIter.hasNext()) {
      final ODocument personDoc = personIter.next();
      Assert.assertTrue(names.contains(personDoc.field("First")));
      Assert.assertTrue(names.remove(personDoc.field("First")));
      System.out.printf("Doc %d: %s\n", docNum++, personDoc.toString());
    }

    Assert.assertTrue(names.isEmpty());
  }

  @Test
  public void testIteratorShouldReuseRecordWithoutNPEUsingForEach() throws Exception {
    // Use class iterator.
    // browseClass() returns all documents in RecordID order
    // (including subclasses, which shouldn't exist for Person)
    final ORecordIteratorClass<ODocument> personIter = db.browseClass("Person");

    // Setting this to true causes the bug. Setting to false it works fine.
    personIter.setReuseSameRecord(true);

    // Shorthand iterator loop.
    int docNum = 0;
    for (final ODocument personDoc : personIter) {
      Assert.assertTrue(names.contains(personDoc.field("First")));
      Assert.assertTrue(names.remove(personDoc.field("First")));

      System.out.printf("Doc %d: %s\n", docNum++, personDoc.toString());
    }

    Assert.assertTrue(names.isEmpty());
  }

  private static void initializeDatabase() {
    db = new ODatabaseDocumentTx("memory:" + ClassIteratorTest.class.getSimpleName());
    if (db.exists() && RECREATE_DATABASE) {
      db.open("admin", "admin");
      db.drop();
      System.out.println("Dropped database.");
    }
    if (!db.exists()) {
      db.create();
      System.out.println("Created database.");

      final OSchema schema = db.getMetadata().getSchema();

      // Create Person class
      final OClass personClass = schema.createClass("Person");
      personClass.createProperty("First", OType.STRING).setMandatory(true).setNotNull(true).setMin("1");

      System.out.println("Created schema.");
    } else {
      db.open("admin", "admin");
    }
  }

  private static void createPerson(final String first) {
    // Create Person document
    final ODocument personDoc = db.newInstance("Person");
    personDoc.field("First", first);
    personDoc.save();
  }
}

@@ -418,29 +418,215 @@
 ore.
-record.impl.ODocument
+metadata.schema.clusterselection.ODefaultClusterSelectionStrategy;%0D%0Aimport com.orientechnologies.orient.core.record.impl.ODocument;%0D%0Aimport com.orientechnologies.orient.core.storage.OStorage.LOCKING_STRATEGY
 ;%0D%0Ai
@@ -3125,32 +3125,1327 @@
 pty());%0D%0A  %7D%0D%0A%0D%0A
+  @Test%0D%0A  public void testDescendentOrderIteratorWithMultipleClusters() throws Exception %7B%0D%0A    final OClass personClass = db.getMetadata().getSchema().getClass(%22Person%22);%0D%0A%0D%0A    // empty old cluster but keep it attached%0D%0A    personClass.truncate();%0D%0A%0D%0A    // reload the data in a new 'test' cluster%0D%0A    int testClusterId = db.addCluster(%22test%22);%0D%0A    personClass.addClusterId(testClusterId);%0D%0A    personClass.setClusterSelection(new ODefaultClusterSelectionStrategy());%0D%0A    personClass.setDefaultClusterId(testClusterId);%0D%0A%0D%0A    for (String name : names) %7B%0D%0A      createPerson(name);%0D%0A    %7D%0D%0A%0D%0A    // Use descending class iterator.%0D%0A    final ORecordIteratorClass%3CODocument%3E personIter =%0D%0A        new ORecordIteratorClassDescendentOrder%3CODocument%3E(%0D%0A            db, db, %22Person%22, true, true, false, LOCKING_STRATEGY.DEFAULT);%0D%0A%0D%0A    personIter.setRange(null, null); // open range%0D%0A%0D%0A    int docNum = 0;%0D%0A    // Explicit iterator loop.%0D%0A    while (personIter.hasNext()) %7B%0D%0A      final ODocument personDoc = personIter.next();%0D%0A      Assert.assertTrue(names.contains(personDoc.field(%22First%22)));%0D%0A      Assert.assertTrue(names.remove(personDoc.field(%22First%22)));%0D%0A      System.out.printf(%22Doc %25d: %25s%5Cn%22, docNum++, personDoc.toString());%0D%0A    %7D%0D%0A%0D%0A    Assert.assertTrue(names.isEmpty());%0D%0A  %7D%0D%0A%0D%0A
   private static

Add test to demonstrate hang using DESC iterator with class that has more than one cluster