package org.infinispan.persistence.jpa;

import static org.testng.AssertJUnit.assertNotNull;

import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.configuration.global.GlobalConfiguration;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.marshall.persistence.PersistenceMarshaller;
import org.infinispan.marshall.persistence.impl.MarshalledEntryFactoryImpl;
import org.infinispan.persistence.DummyInitializationContext;
import org.infinispan.persistence.jpa.configuration.JpaStoreConfigurationBuilder;
import org.infinispan.persistence.spi.MarshallableEntry;
import org.infinispan.persistence.spi.MarshallableEntryFactory;
import org.infinispan.test.AbstractInfinispanTest;
import org.infinispan.test.TestingUtil;
import org.infinispan.test.fwk.TestCacheManagerFactory;
import org.infinispan.util.concurrent.BlockingManager;
import org.infinispan.util.concurrent.WithinThreadExecutor;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;

/**
 * This is a base class containing various unit tests for each and every different CacheStore implementations. If you
 * need to add Cache/CacheManager tests that need to be run for each cache store/loader implementation, then use
 * BaseCacheStoreFunctionalTest.
 *
 * @author <a href="mailto:rtsang@redhat.com">Ray Tsang</a>
 *
 */
public abstract class AbstractJpaStoreTest extends AbstractInfinispanTest {

   protected static final String PERSISTENCE_UNIT_NAME = "org.infinispan.persistence.jpa";

   protected EmbeddedCacheManager cm;

   protected JpaStore<Object, Object> cs;

   //protected TransactionFactory gtf = new TransactionFactory();

   protected PersistenceMarshaller marshaller;

   protected MarshallableEntryFactory entryFactory;
   protected BlockingManager blockingManager;

   protected AbstractJpaStoreTest() {
     // gtf.init(false, false, true, false);
   }

   protected EmbeddedCacheManager createCacheManager() {
      return TestCacheManagerFactory.createCacheManager(JpaSCI.INSTANCE, new ConfigurationBuilder());
   }

   protected JpaStore createCacheStore(GlobalConfiguration gc) {
      ConfigurationBuilder builder = new ConfigurationBuilder();
      builder.persistence().addStore(JpaStoreConfigurationBuilder.class)
            .persistenceUnitName(PERSISTENCE_UNIT_NAME)
            .entityClass(getEntityClass())
            .segmented(false);

      JpaStore store = new JpaStore();
      store.init(new DummyInitializationContext(builder.persistence().stores().get(0).create(), cm.getCache(),
            marshaller, null, entryFactory, new WithinThreadExecutor(), gc, blockingManager, null,
            cm.getGlobalComponentRegistry().getTimeService()));
      store.start();

      assertNotNull(store.getEntityManagerFactory());

      return store;
   }

   protected abstract Class<?> getEntityClass();

   @BeforeMethod(alwaysRun = true)
   public void setUp() throws Exception {
      try {
         cm = createCacheManager();
         marshaller = TestingUtil.extractPersistenceMarshaller(cm);
         entryFactory = new MarshalledEntryFactoryImpl(marshaller);
         blockingManager = TestingUtil.extractGlobalComponent(cm, BlockingManager.class);
         cs = createCacheStore(cm.getCacheManagerConfiguration());
         cs.clear();
      } catch (Exception e) {
         log.warn("Error during test setup", e);
         throw e;
      }
   }

   @AfterMethod(alwaysRun = true)
   public void stopMarshaller() {
      //marshaller.stop();
      if (cs != null) cs.stop();
      if (cm != null) cm.stop();
   }

   protected MarshallableEntry createEntry(Object key, Object value) {
      return entryFactory.create(key, value);
   }

   protected MarshallableEntry createEntry(TestObject obj) {
      return createEntry(obj.getKey(), obj.getValue());
   }
}
