/*******************************************************************************
 * Copyright © 2004 VHA. All rights reserved
 ******************************************************************************/
package gov.va.med.esr.common.persistent.hibernate.lookup;

import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;

// Library classes
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Cache;
import net.sf.ehcache.Element;
import org.hibernate.cache.entry.CacheEntry;
import org.hibernate.util.ArrayHelper;

import gov.va.med.fw.model.lookup.AbstractLookup;
import gov.va.med.fw.model.lookup.Lookup;

import gov.va.med.esr.common.persistent.lookup.LookupsDAO;
import gov.va.med.esr.common.util.AbstractCommonTestCase;
import gov.va.med.esr.common.model.lookup.*;

/**
 * @author Ghazenfer Mansoor
 * @history Rewritten DNS   CHENJ2, DNS   lev
 */
public class LookupsTest extends AbstractCommonTestCase {
	/**
	 * An instance of TEST_LOOKUPS_PROPERTIES
	 */
	private static final String TEST_LOOKUPS_PROPERTIES = "test_lookups";

	/**
	 * An instance of LOOKUP_CLASS_PROPERTY
	 */
	private static final String LOOKUP_CLASS_PROPERTY = "lookup.class";

	/**
	 * An instance of daoMap
	 */
	private Map daoMap = null;
	
	/**
	 * An instance of cacheManager
	 */
	private CacheManager cacheManager = null;

	/**
	 * A default constructor 
	 */
	public LookupsTest() {
		super();
	}
	
	/**
	 * @see gov.va.med.esr.common.util.AbstractCommonTestCase#customSetUp()
	 */
	protected void customSetUp() throws Exception {
		super.customSetUp();

		// can't use auto-wire by type because there are more than one
		// map defined in a common context - VL
		this.daoMap = (Map)this.applicationContext.getBean( "lookupDAOMap", Map.class );

		// Get an EHCacheManager to check if the caches work 
		this.cacheManager = 
			(CacheManager)this.applicationContext.getBean("ehCacheManager", CacheManager.class );
	}

	/**
	 * Test all the lookups defined in lookupDAOMap
	 * 
	 * @throws Exception
	 */
	public void testAllLookupsInDAOMap() throws Exception {
		loadAllLookups(false);
	}

	/**
	 * Test all the lookups defined in the properties files.
	 * 
	 * @throws Exception
	 */
	public void testLookupsFromFile() throws Exception {
		ResourceBundle bundle = ResourceBundle.getBundle(TEST_LOOKUPS_PROPERTIES);
		Enumeration keys = bundle.getKeys();
		while (keys.hasMoreElements()) {
			String key = (String) keys.nextElement();
			if (!LOOKUP_CLASS_PROPERTY.equals(key))
				loadLookup( Class.forName( key ) );
		}
	}

	/**
	 * Test a lookup defined in a property file with key lookup.class
	 * 
	 * @throws Exception
	 */
	public void testIndividualLookup() throws Exception {
		ResourceBundle bundle = ResourceBundle.getBundle(TEST_LOOKUPS_PROPERTIES);
		loadLookup( Class.forName( bundle.getString( LOOKUP_CLASS_PROPERTY ) ) );
	}

	public void testActiveLookupsDAO() throws Exception {
		loadLookup( EligibilityType.class, true );
	}

	public void testOrderByDescriptionLookupsDAO() throws Exception {
		loadLookup( EGTSettingType.class, true );
	}

	public void testSDSOrderByNameLookupsDAO() throws Exception {
		loadLookup( VeteranIdentifierType.class, true );
	}

	public void testOrderByCodeLookupsDAO() throws Exception {
		loadLookup( EnrollmentPriorityGroup.class, true );
	}

	public void testDefaultLoadWReverseSort() throws Exception {
		// to test this one, add this line to the lookup DAO entry:
		// <property name="reverseSort"><value>true</value></property>
		loadLookup( EGTSettingType.class, true);
	}

	public void testCustomQueries() throws Exception {
		// custom findAll() query; the other one is default
		loadLookup( VAFacility.class, true);
	}

	public void testIncludeValidCodes() throws Exception {
		// custom findAll() query; the other one is default
		loadLookup(PhoneType.class, true);
	}

	/*
	 * Test for the fix of WrongClassException problem. two different lookup with
	 * objects of same id. causes Wrong class exception
	 */
	public void testCacheWrongClassExceptionDAO() throws Exception {
		loadLookup( ReportScheduleType.class, true);
		loadLookup( ServiceBranch.class, true);
	}

	/**
	 * @throws Exception
	 */
	public void testLookupCache() throws Exception {

		// First load of State lookup code
		long time = System.currentTimeMillis();
		loadLookup( MessageStatus.class, true);
		long lagTime = (System.currentTimeMillis() - time);
		
		logger.info( "Lag time " + lagTime );
		
		// Second load of state lookup codes
		time = System.currentTimeMillis();
		loadLookup( MessageStatus.class, true);
		long newLagTime = (System.currentTimeMillis() - time);
		
		// and better be faster
		assertTrue( lagTime > newLagTime );
		logger.info( "Old lag time " + lagTime );
		logger.info( "New lag time " + newLagTime );
		
		// See if we can data directly out of the cache
		String[] names = this.cacheManager.getCacheNames();
		for( int i=0; i<names.length; i++ ) {
			Cache cache = this.cacheManager.getCache( names[i] );
			if( cache.getSize() > 0 ) {
				List keys = cache.getKeys();
				Iterator iterator = keys != null ? keys.iterator() : null;
				while( iterator != null && iterator.hasNext() ) {
					Element data = cache.get( iterator.next() );
					Object obj = data.getObjectValue();
					CacheEntry entry = obj instanceof CacheEntry ? (CacheEntry)obj : null;
					String cacheName = entry != null ? entry.getSubclass() : null;
					if( MessageStatus.class.getName().equals( cacheName ) ) {
						logger.info( "Got a cache " + cacheName );
						if (entry != null) {
							logger.info( "Cached data " + ArrayHelper.toString( entry.getDisassembledState() ) );
						}
					}
				}
			}
		}
	}
	
	/**
	 * Test to check if the query is working
	 * 
	 * @throws Exception
	 */
	public void testQueryCache() throws Exception {
		// Get the cache for State lookup code
		// State uses a custom query so a result is stored
		// a query.StandardLookupCache
		Cache cache = this.cacheManager.getCache( State.class.getName() );

		// Better not have anything in the cache
		long stat = cache.getSize();
		assertTrue( stat == 0 );

		// First load of State lookup code
		long time = System.currentTimeMillis();
		loadLookup( State.class, true);
		long lagTime = (System.currentTimeMillis() - time);
		
		// This is the first time so there should not be any hit
		stat = cache.getHitCount();
		assertTrue( stat == 0 );

		// but the cache size should not be 0
		stat = cache.getSize();
		assertTrue( stat > 0 );
		
		logger.info( "Cache size " + stat );
		logger.info( "Lag time " + lagTime );
		
		// Second load of state lookup codes
		time = System.currentTimeMillis();
		loadLookup( State.class, true);
		long newLagTime = (System.currentTimeMillis() - time);
		
		// Now there better not be any new element in the cache
		assertTrue( stat == cache.getSize() );

		// and there better be faster
		assertTrue( lagTime > newLagTime );
		logger.info( "Old lag time " + lagTime );
		logger.info( "New lag time " + newLagTime );
	}

	private void loadLookup( Class lookupClass ) throws Exception {
		loadLookup( lookupClass, false );
	}
	
	private void loadLookup( Class lookupClass, boolean checkByCode ) throws Exception {
		
		String lookupName = lookupClass.getName();
		LookupsDAO lookupsDAO = (LookupsDAO) daoMap.get(lookupName);
		
		logger.info( "Loading lookup: [ " + lookupName + " ] using dao: " );
		
		List lookupList = lookupsDAO.findAll(lookupClass);
		AbstractLookup lookupResult = null;

		if (checkByCode) {
			for (Iterator il = lookupList.iterator(); il.hasNext();) {
				Lookup lookup = (Lookup) il.next();
				lookupResult = lookupsDAO.getByCode(lookupClass, lookup.getCode());
				logger.info("getByCode(" + lookup.getCode() + ") = " + lookupResult);
				
				lookupResult = lookupsDAO.getByName(lookupClass, "VPID");
				logger.info("getByCode(" + lookup.getCode() + ") = " + lookupResult);
				
			}
		}
		else {
			String code = ((Lookup) lookupList.get(0)).getCode();
			lookupResult = lookupsDAO.getByCode(lookupClass, code);
			logger.info("getByCode(" + code + ") = " + lookupResult);
		}
	}
	
	private void loadAllLookups(boolean checkByCode) throws Exception {
		int count = 0;
		for (Iterator i = daoMap.keySet().iterator(); i.hasNext();) {
			String lookupName = (String)i.next();
			loadLookup( Class.forName( lookupName ), checkByCode );
			count++;
		}
		logger.info("Loaded " + count + " lookups");
	}
}