package org.jboss.cache.search;

import org.jboss.cache.Fqn;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author Navin Surtani (<a href="mailto:nsurtani@redhat.com">nsurtani@redhat.com</a>)
 *         <p/>
 *         Test class for the QueryResultIteratorImpl
 */

@Test(groups = "functional")
public class QueryResultIteratorImplTest
{
   List<CacheEntityId> ids;
   Map<CacheEntityId, Object> dummyResults;
   QueryResultIterator iterator;
   int fetchSize = 1;

   @BeforeMethod
   public void setUp()
   {
      // create a set of dummy cache entity IDs
      ids = new ArrayList();
      ids.add(new CacheEntityId(Fqn.fromString("/a"), "key1"));
      ids.add(new CacheEntityId(Fqn.fromString("/b"), "key2"));
      ids.add(new CacheEntityId(Fqn.fromString("/c"), "key3"));
      ids.add(new CacheEntityId(Fqn.fromString("/d"), "key4"));
      ids.add(new CacheEntityId(Fqn.fromString("/e"), "key5"));
      ids.add(new CacheEntityId(Fqn.fromString("/f"), "key6"));
      ids.add(new CacheEntityId(Fqn.fromString("/g"), "key7"));
      ids.add(new CacheEntityId(Fqn.fromString("/h"), "key8"));
      ids.add(new CacheEntityId(Fqn.fromString("/i"), "key9"));
      ids.add(new CacheEntityId(Fqn.fromString("/j"), "key10"));

      // create some dummy data
      dummyResults = new HashMap<CacheEntityId, Object>();
      int counter = 0;

      for (CacheEntityId id : ids)
      {
         // for each cache entity ID, create a dummy result that will be returned when loading it.
         dummyResults.put(id, "Result number " + counter++);
      }

      // now create a dummy entity loader
      CacheEntityLoader dummyLoader = new DummyEntityLoader(ids, dummyResults);

      iterator = new QueryResultIteratorImpl(ids, dummyLoader, fetchSize);
   }

   @AfterMethod
   public void tearDown()
   {
      ids = null;
      dummyResults = null;
      iterator = null;
   }

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

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

      iterator.jumpToResult((ids.size() - 1));
      assert iterator.isLast();

      iterator.jumpToResult(ids.size() - 2);
      assert iterator.isBeforeLast();
   }

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

      iterator.first();

      assert iterator.isFirst() : "We should be pointing at the first element";
      next = iterator.next();
      assert next == dummyResults.get(ids.get(0));
      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();

      Object next = iterator.next();

      //Returns the size of the list of ids.
      int size = ids.size();

      //Makes sure that previous is the last element.
      assert next == dummyResults.get(ids.get(size - 1));

      //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 == dummyResults.get(ids.get(1));

      //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.
      int size = ids.size();
      assert next == dummyResults.get(ids.get(size - 2));

      //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();
      for (int i = 0; i < ids.size(); i++)
      {
         System.out.println("Loop number count: - " + (i+1));
         Object expectedValue = dummyResults.get(ids.get(i));
         assert iterator.hasNext(); // should have next as long as we are less than the number of elements.
         assert expectedValue == iterator.next(); // tests next()
      }
      assert !iterator.hasNext(); // this should now NOT be true.
   }

   public void testPreviousAndHasPrevious()
   {
      iterator.last();
      for (int i = ids.size() - 1; i >= 0; i--)
      {
         Object expectedValue = dummyResults.get(ids.get(i));
         assert iterator.hasPrevious(); // should have previous as long as we are more than the number of elements.
         assert expectedValue == iterator.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() == ids.size();

   }

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

      iterator.last();
      assert iterator.previousIndex() == (ids.size() - 2);
   }

   public static class DummyEntityLoader extends CacheEntityLoader
   {
      private List<CacheEntityId> allKnownIds;
      private Map<CacheEntityId, Object> dummyValues;

      public DummyEntityLoader(List<CacheEntityId> allKnownIds, Map<CacheEntityId, Object> dummyValues)
      {
         // use a null as a cache since we won't ever need to refer to the cache
         super(null);

         this.allKnownIds = allKnownIds;
         this.dummyValues = dummyValues;
      }

      @Override
      public List<Object> load(List<CacheEntityId> ids)
      {
         List<Object> resultsToReturn = new ArrayList<Object>(ids.size());
         // iterate through the list of ids we are looking for
         for (CacheEntityId id : ids)
         {
            resultsToReturn.add(dummyValues.get(id));
         }

         return resultsToReturn;
      }

      @Override
      public Object load(CacheEntityId id)
      {
         return dummyValues.get(id);
      }
   }
}
