package eu.dnetlib.enabling.resultset;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.matches;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

import eu.dnetlib.miscutils.cache.EhCache;

/**
 * Test ResultSetRegistryImpl.
 * 
 * @author marko
 * 
 */
@RunWith(MockitoJUnitRunner.class)
public class ResultSetRegistryImplTest {

	/**
	 * max in memory elements.
	 */
	private static final int MAX_IN_MEM = 100;

	/**
	 * default time.
	 */
	private static final int DEFAULT_TTI = 1000;

	/**
	 * default time to live.
	 */
	private static final int DEFAULT_TTL = 1000;

	/**
	 * class under test.
	 */
	private transient ResultSetRegistryImpl registry;

	/**
	 * mock of a resultset object.
	 */
	@Mock
	private transient ResultSet resultSet;

	/**
	 * ehcache manager.
	 */
	private final transient CacheManager cacheManager = CacheManager.create();

	/**
	 * Set up basic mocks.
	 */
	@Before
	public void setUp() {
		final Cache ehCache = new net.sf.ehcache.Cache("testCache", MAX_IN_MEM, false, false, DEFAULT_TTL, DEFAULT_TTI);
		cacheManager.addCache(ehCache);

		final Cache mtEhCache = new net.sf.ehcache.Cache("testMTCache", MAX_IN_MEM, false, false, DEFAULT_TTL, DEFAULT_TTI);
		cacheManager.addCache(mtEhCache);

		final EhCache<String, ResultSet> cache = new EhCache<String, ResultSet>(ehCache);
		cache.setCache(ehCache);

		final EhCache<String, Integer> mtCache = new EhCache<String, Integer>(mtEhCache);
		mtCache.setCache(mtEhCache);

		registry = new ResultSetRegistryImpl();
		registry.setCache(cache);
		registry.setMaxIdleTimeCache(mtCache);

		when(resultSet.getIdentifier()).thenReturn("123456");
		when(resultSet.isOpen()).thenReturn(true);
	}

	/**
	 * remove ehcache from ehcachemanager.
	 */
	@After
	public void tearDown() {
		cacheManager.removeAllCaches();
	}

	/**
	 * Test that registered resultsets are really registered.
	 */
	@Test
	public void testAddResultSet() {

		final String rsId = resultSet.getIdentifier();
		assertNotNull("the resultset should not be null", rsId);

		registry.addResultSet(resultSet);
		verify(resultSet).setIdentifier(matches("rs-" + registry.getIdGenerator().getRegEx()));

		final ResultSet res = registry.getResultSetById(rsId); // NOPMD
		assertNotNull("the resultset is not found", res);
		assertEquals("the resultsets don't have the same id", rsId, res.getIdentifier());
		assertEquals("the resultsets are not the same instance", resultSet, res);
	}

	/**
	 * Test that registered resultsets are really registered.
	 */
	@Test
	public void testAddResultSetWithId() {
		resultSet = mock(ResultSet.class);
		when(resultSet.getIdentifier()).thenReturn("123");

		final String rsId = resultSet.getIdentifier();

		registry.addResultSet(resultSet, rsId);
		verify(resultSet).setIdentifier(rsId);

		final ResultSet res = registry.getResultSetById(rsId); // NOPMD
		assertNotNull("the resultset is not found", res);
		assertEquals("the resultsets don't have the same id", rsId, res.getIdentifier());
		assertEquals("the resultsets are not the same instance", resultSet, res);
	}

	/**
	 * try to obtain a inexistent resultset.
	 */
	@Test
	public void testInexistentResultSet() {
		final String rsId = resultSet.getIdentifier();
		assertNull("inexisten resultset returns null", registry.getResultSetById(rsId));
	}

	/**
	 * test closing of resultset and its pruning from the registry.
	 */
	@Test
	public void testResultSetClose() {
		final String rsId = resultSet.getIdentifier();

		assertTrue("check if resultset is open", resultSet.isOpen());

		registry.addResultSet(resultSet);
		verify(resultSet).addObserver(registry);

		// simulate a resultset close on the mock
		when(resultSet.isOpen()).thenReturn(false);
		registry.update(resultSet, null);

		assertFalse("check if resultset is closed", resultSet.isOpen());
		assertNull("check if the object is pruned from the registry", registry.getResultSetById(rsId));
	}

	/**
	 * same as testResultSetClose() but with a real observer/observable mechanism, i.e. without resultset mock class.
	 * 
	 */
	@Test
	public void testWithRealObservable() {
		final LocalResultSetImpl rset = new LocalResultSetImpl(null);
		rset.setIdentifier("123456");
		resultSet = rset;

		final String rsId = resultSet.getIdentifier();

		assertFalse("check if resultset is not destroyed", resultSet.isDestroyed());

		registry.addResultSet(resultSet);
		resultSet.destroy();

		assertTrue("check if resultset is destroyed", resultSet.isDestroyed());
		assertNull("check if the object is pruned from the registry", registry.getResultSetById(rsId));
	}

}
