package org.jboss.cache.search;

import org.testng.annotations.*;
import org.apache.lucene.search.Query;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.jboss.cache.Fqn;
import org.jboss.cache.Cache;
import org.jboss.cache.DefaultCacheFactory;
import org.jboss.cache.search.test.Person;
import org.jboss.cache.search.helper.IndexCleanUp;


/**
 * @author Navin Surtani (<a href="mailto:nsurtani@redhat.com">nsurtani@redhat.com</a>)
 */
@Test(groups = "functional")
public class LazyQueryResultIteratorTest
{
   SearchableCache searchableCache;
   QueryParser parser = null;
   Query luceneQuery = null;
   CacheQuery cacheQuery = null;
   LazyQueryResultIterator iterator = null;
   int fetchSize = 1;
   Person person1, person2, person3, person4, person5, person6, person7, person8, person9, person10;
   StringBuilder builder;


   @BeforeTest
   public void setUpBeforeTest() throws ParseException
   {
      Cache coreCache = new DefaultCacheFactory().createCache();
      searchableCache = new SearchableCacheFactory().createSearchableCache(coreCache, Person.class);

      person1 = new Person();
      person2 = new Person();
      person3 = new Person();
      person4 = new Person();
      person5 = new Person();
      person6 = new Person();
      person7 = new Person();
      person8 = new Person();
      person9 = new Person();
      person10 = new Person();

      person1.setBlurb("cat");
      person2.setBlurb("cat");
      person3.setBlurb("cat");
      person4.setBlurb("cat");
      person5.setBlurb("cat");
      person6.setBlurb("cat");
      person7.setBlurb("cat");
      person8.setBlurb("cat");
      person9.setBlurb("cat");
      person10.setBlurb("cat");

      searchableCache.put(Fqn.fromString("/a"), "key1", person1);
      searchableCache.put(Fqn.fromString("/a"), "key2", person2);
      searchableCache.put(Fqn.fromString("/a"), "key3", person3);
      searchableCache.put(Fqn.fromString("/a"), "key4", person4);
      searchableCache.put(Fqn.fromString("/a"), "key5", person5);
      searchableCache.put(Fqn.fromString("/a"), "key6", person6);
      searchableCache.put(Fqn.fromString("/a"), "key7", person7);
      searchableCache.put(Fqn.fromString("/a"), "key8", person8);
      searchableCache.put(Fqn.fromString("/a"), "key9", person9);
      searchableCache.put(Fqn.fromString("/a"), "key10", person10);


   }

   @AfterTest
   public void tearDownAfterTest()
   {
      IndexCleanUp.cleanUpIndexes();
   }

   @BeforeMethod
   public void setUp() throws ParseException
   {
      parser = new QueryParser("blurb", new StandardAnalyzer());
      luceneQuery = parser.parse("cat");
      cacheQuery = searchableCache.createQuery(luceneQuery);
      iterator = (LazyQueryResultIterator) cacheQuery.lazyIterator();

   }


   @AfterMethod
   public void tearDown()
   {
      iterator = null;
      parser = null;
      luceneQuery = null;
      cacheQuery = null;
   }

   public void testJumpToResult() throws IndexOutOfBoundsException
   {
      iterator.jumpToResult(0);
      assert iterator.isFirst();

      iterator.jumpToResult(1);
      assert iterator.isAfterFirst();

      iterator.jumpToResult(9);
      assert iterator.isLast();

      iterator.jumpToResult(8);
      assert iterator.isBeforeLast();
   }

   public void testFirst()
   {
      assert iterator.isFirst() : "We should be pointing at the first element";
      Object next = iterator.next();
      assert next == person1;
      assert !iterator.isFirst();

      iterator.first();

      assert iterator.isFirst() : "We should be pointing at the first element";
      next = iterator.next();
      assert next == person1;
      assert !iterator.isFirst();

   }

   public void testLast()
   {
      //Jumps to the last element
      iterator.last();

      //Makes sure that the iterator is pointing at the last element.
      assert iterator.isLast();

      iterator.first();

      //Check that the iterator is NOT pointing at the last element.
      assert !iterator.isLast();
   }

   public void testAfterFirst()
   {
      //Jump to the second element.
      iterator.afterFirst();

      //Check this
      assert iterator.isAfterFirst();

      //Previous element in the list
      Object previous = iterator.previous();

      //Check that previous is the first element.
      assert previous == person2;

      //Make sure that the iterator isn't pointing at the second element.
      assert !iterator.isAfterFirst();

   }

   public void testBeforeLast()
   {
      //Jump to the penultimate element.
      iterator.beforeLast();

      //Check this
      assert iterator.isBeforeLast();

      //Next element - which should be the last.
      Object next = iterator.next();

      //Check that next is the penultimate element.
      assert next == person9;

      //Make sure that the iterator is not pointing at the penultimate element.
      assert !iterator.isBeforeLast();
   }

   public void testIsFirst()
   {
      iterator.first();
      assert iterator.isFirst();

      iterator.next();
      assert !iterator.isFirst();
   }

   public void testIsLast()
   {
      iterator.last();
      assert iterator.isLast();

      iterator.previous();
      assert !iterator.isLast();
   }

   public void testIsAfterFirst()
   {
      iterator.afterFirst();
      assert iterator.isAfterFirst();

      iterator.previous();
      assert !iterator.isAfterFirst();
   }

   public void testIsBeforeLast()
   {
      iterator.beforeLast();
      assert iterator.isBeforeLast();
   }

   public void testNextAndHasNext()
   {
      iterator.first();

      // This is so that we can "rebuild" the keystring for the nextAndHasNext and the previousAndHasPrevious methods.
      builder = new StringBuilder();

      for (int i = 1; i <= 10; i++)
      {
         builder.delete(0, 4);      // In this case we know that there are 4 characters in this string. so each time we come into the loop we want to clear the builder.
         builder.append("key");
         builder.append(i);
         String keyString = builder.toString();
         Object expectedValue = searchableCache.get("/a", keyString);
         assert iterator.hasNext(); // should have next as long as we are less than the number of elements.

         Object next = iterator.next();

         assert expectedValue == next; // tests next()
     }
      assert !iterator.hasNext(); // this should now NOT be true.
   }

   public void testPreviousAndHasPrevious()
   {
      iterator.last();

      // This is so that we can "rebuild" the keystring for the nextAndHasNext and the previousAndHasPrevious methods.
      builder = new StringBuilder();      

      for (int i = 10; i >= 1; i--)
      {
         builder.delete(0, 5);      // In this case we know that there are 4 characters in this string. so each time we come into the loop we want to clear the builder.
         builder.append("key");
         builder.append(i);
         String keyString = builder.toString();


         Object expectedValue = searchableCache.get("/a", keyString);

         assert iterator.hasPrevious(); // should have previous as long as we are less than the number of elements.

         Object previous = iterator.previous();

         assert expectedValue == previous; // tests previous()
     }
      assert !iterator.hasPrevious(); // this should now NOT be true.

   }

   public void testNextIndex()
   {
      iterator.first();
      assert iterator.nextIndex() == 1;

      iterator.last();
      assert iterator.nextIndex() == 10; //Index will be the index of the last element + 1.

   }

   public void testPreviousIndex()
   {
      iterator.first();
      assert iterator.previousIndex() == -1;

      iterator.last();
      assert iterator.previousIndex() == 8; //Index will be that of the last element - 1.
   }

}


