package org.jboss.cache.pojo;

import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertTrue;

import java.util.*;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.cache.Fqn;
import org.jboss.cache.pojo.impl.ReferenceImpl;
import org.jboss.cache.pojo.test.*;
import org.testng.annotations.*;

/**
 * Test case for finding back-references.
 *
 * @author Dan Berindei <dan.berindei@gmail.com>
 */

@Test(groups = { "functional" })
public class FindReferencesTest
{
   Log log = LogFactory.getLog(FindReferencesTest.class);
   PojoCache cache_;

   @BeforeMethod(alwaysRun = true)
   protected void setUp() throws Exception
   {
      log.info("setUp() ....");
      String configFile = "META-INF/local-service.xml";
      boolean toStart = false;
      cache_ = PojoCacheFactory.createCache(configFile, toStart);
      cache_.start();
   }

   @AfterMethod(alwaysRun = true)
   protected void tearDown() throws Exception
   {
      cache_.stop();
   }

   private Person createPerson(String id, String name, int age, Address address)
   {
      Person p = new Person(null);
      p.setName(name);
      p.setAge(age);
      p.setAddress(address);
      cache_.attach(id, p);
      return p;
   }

   private Address createAddress()
   {
      Address add = new Address();
      add.setZip(95123);
      add.setCity("San Jose");
      return add;
   }

   public void testImmediateType() throws Exception
   {
      log.info("testImmediateType() ....");
      String test = "This is a test";
      cache_.attach("/test", test);

      assertEquals("Fqn", null, cache_.getInternalFqn(test));
      assertEquals("List", Collections.EMPTY_LIST, cache_.getReferences(test));
   }

   public void testNotAttached() throws Exception
   {
      log.info("testImmediateType() ....");
      Address address = createAddress();

      assertEquals("Fqn", null, cache_.getInternalFqn(address));
      assertEquals("List", Collections.EMPTY_LIST, cache_.getReferences(address));
   }

   public void testSingleExternalReference() throws Exception
   {
      log.info("testNoReferences() ....");
      Person joe = createPerson("/person/joe", "Joe Black", 32, null);

      Fqn<?> joesInternalFqn = cache_.getInternalFqn(joe);
      assertTrue("Internal Fqn not null", joesInternalFqn != null);

      Collection<Reference> addressReferences = cache_.getReferences(joe);
      assertEquals("Size", 1, addressReferences.size());
      assertEquals("Reference", new ReferenceImpl(Fqn.fromString("/person/joe"), null),
            addressReferences.iterator().next());
   }

   public void testSingleInternalReference() throws Exception
   {
      log.info("testSingleReference() ....");
      Address address = createAddress();
      Person joe = createPerson("/person/joe", "Joe Black", 32, address);

      Fqn<?> joesInternalFqn = cache_.getInternalFqn(joe);
      Collection<Reference> addressReferences = cache_.getReferences(address);

      assertEquals("Size", 1, addressReferences.size());
      assertEquals("Reference", new ReferenceImpl(joesInternalFqn, "address"),
            addressReferences.iterator().next());
   }

   public void testMultipleInternalReferences() throws Exception
   {
      log.info("testMultipleReferences() ....");
      Address address = createAddress();
      Person joe = createPerson("/person/joe", "Joe Black", 32, address);
      Person jane = createPerson("/person/jane", "Jane Black", 32, address);

      Fqn<?> joesInternalFqn = cache_.getInternalFqn(joe);
      Fqn<?> janesInternalFqn = cache_.getInternalFqn(jane);
      HashSet<Reference> expectedReferences = new HashSet<Reference>(Arrays.<Reference> asList(
            new ReferenceImpl(joesInternalFqn, "address"), new ReferenceImpl(janesInternalFqn, "address")));

      Set<Reference> addressReferences = new HashSet<Reference>(cache_.getReferences(address));

      assertEquals("Reference Fqns", expectedReferences, addressReferences);
   }

   public void testDoubleReferenceFromSameObject()
   {
      log.info("testDoubleReferenceFromSameObject() ...");

      DoubleRef doubleRef = new DoubleRef();
      cache_.attach("/doubleref", doubleRef);

      Fqn<?> sourceFqn = cache_.getInternalFqn(doubleRef);
      HashSet<Reference> expectedReferences = new HashSet<Reference>(Arrays.<Reference> asList(
            new ReferenceImpl(sourceFqn, "one"), new ReferenceImpl(sourceFqn, "two")));

      Student student = doubleRef.getOne();
      Set<Reference> references = new HashSet<Reference>(cache_.getReferences(student));

      assertEquals("Reference Fqns", expectedReferences, references);

      // removing one of the references
      doubleRef.setOne(null);
      Collection<Reference> references2 = cache_.getReferences(student);

      assertEquals("Size", 1, references2.size());
      assertEquals("Reference Fqn", new ReferenceImpl(sourceFqn, "two"), references2.iterator().next());

      // removing the last reference
      doubleRef.setTwo(null);
      Collection<Reference> references3 = cache_.getReferences(student);

      assertEquals("Size", 0, references3.size());
   }

}