/*
 * Created on Jan 11, 2005
 *
 * Test cases directly related Person
 */
package gov.va.med.esr.common.persistent.history;

import gov.va.med.fw.model.AbstractKeyedEntity;

import gov.va.med.esr.common.model.person.Person;
//import gov.va.med.esr.common.persistent.hibernate.AbstractKeyedEntityDAOTestCase;
import gov.va.med.esr.common.util.AbstractCommonTestCase;

import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.apache.commons.beanutils.NestedNullException;
import org.apache.commons.beanutils.PropertyUtilsBean;

import gov.va.med.fw.model.EntityKey;
import gov.va.med.fw.model.EntityKeyFactory;
import gov.va.med.fw.persistent.DAOException;

import gov.va.med.esr.service.impl.ChangeEvent;
import gov.va.med.esr.service.impl.HistoricalInfo;

/**
 * @author DNS   CHENJ2
 *
 * Abstract base classe for testing history
 **/
public abstract class AbstractHistoryTestCase extends AbstractCommonTestCase { //AbstractKeyedEntityDAOTestCase {

/*	protected DAOTestMode[] setUpTests() {
		return new DAOTestMode[] { TEST_NONE };
	}

	 (non-Javadoc)
	 * @see gov.va.med.esr.common.persistent.hibernate.AbstractKeyedEntityDAOTestCase#setUpTestObject()
	 
	protected AbstractKeyedEntity setUpTestObject() throws Exception {
		return buildPerson();
	}

	 (non-Javadoc)
	 * @see gov.va.med.esr.common.persistent.hibernate.AbstractKeyedEntityDAOTestCase#modifyTestObject(gov.va.med.fw.model.AbstractKeyedEntity)
	 
	protected void modifyTestObject(AbstractKeyedEntity obj) {
		Person p = (Person) obj;
		p.setSensitiveRecord(Boolean.TRUE);
		p.setVeteran(Boolean.TRUE);
	}
*/
	protected void customSetUp() throws Exception {
		super.customSetUp();
		loadHistoryDAO();
		if (getPersonId() != null) {
			// only if Person is involved
			personKey = EntityKeyFactory.createEntityKey(new BigDecimal(getPersonId()),
				getRootObjectClass());
		}
	}

	// TODO: data setup
	protected void getHistoryByChangeTime() {
		Object[] ces = retrievedChangeEvents.toArray();
		ChangeEvent ce = (ChangeEvent) ces[getHistoryChangeTimeIndex()];
		long t1 = System.currentTimeMillis();
		getHistoryByChangeTime(ce);
		long t2 = System.currentTimeMillis();
		System.out.println("Retrieved history entry, Elapsed time = " + (t2-t1));
	}
	
	/**
	 * Subclass can override
	 * @return
	 */
	protected int getHistoryChangeTimeIndex()
	{
		return retrievedChangeEvents.size()-1;
	}
	
	protected void getHistoryByChangeTime(ChangeEvent ce) {
		try {
			log("\nRetrieving history for changeTime:" + ce.getTimeStamp());
			HistoricalInfo retrieved = executeDAOGetHistoryByChangeTime(ce);
			assertNotNull("No history object returned", retrieved);
			// todo: assert more details -- compare address/history/phone etc.
			Person currP = (Person) retrieved.getCurrentVersion();
			Person prevP = (Person) retrieved.getPreviousVersion();
			
			verifyRetrievedHistoryData(currP);
			verifyRetrievedHistoryData(prevP);
			
			log("\n============ Retrieved current history objects: ============");
			printHistoryData(currP);
			log("\n============ Retrieved previous history objects: ============");
			printHistoryData(prevP);
			
		} catch (Exception e) {
			fail("Can not execute DAO", e);
		}

	}
	
	protected HistoricalInfo executeDAOGetHistoryByChangeTime(ChangeEvent ce) throws DAOException {
		return getHistoryDAO().getHistoryByChangeTime(ce);
	}
	
	abstract protected void printHistoryData(Person p);
	
	abstract protected void verifyRetrievedHistoryData(Person p);

	protected void getHistoryChangeTimes() {
		try {
			retrievedChangeEvents = executeDAOGetHistoryChangeTimes(personKey);
			assertNotNull("No dates returned", retrievedChangeEvents);
			assertTrue("No dates returned", !(retrievedChangeEvents.isEmpty()));
			log("Retrieved History timestamps: ");
			printChangeEvents(retrievedChangeEvents);
		} catch (Exception e) {
			fail("Can not execute DAO", e);
		}
	}

	protected Set executeDAOGetHistoryChangeTimes(EntityKey personKey) throws DAOException {
		return getHistoryDAO().getHistoryChangeTimes(personKey);
	}

	public void testGetHistory() {
		// first get the change events
		getHistoryChangeTimes();
		// then get the actual for a particular timestamp
		getHistoryByChangeTime();
	}

	protected void printChangeEvents(Set ceList) {
		Iterator iter = ceList.iterator();
		
		int i = 0;
		
		while (iter.hasNext()) {
			log("[#" + i++ + "] timestamp = " + ((ChangeEvent)iter.next()).getTimeStamp());
		}

	}
	
	protected void printSet(String name, Set set) {
		AbstractKeyedEntity entity = null;
		Iterator iter = set.iterator();
		log("------ "+ name + " ------");
		
		int i = 1;
		
		while (iter.hasNext()) {
			entity = (AbstractKeyedEntity)iter.next();
			log("[#" + i++ + "] " + toHistoryString(entity));
		}
	}

	
	protected void printSet(String name, Set set, String[] children) {
		AbstractKeyedEntity entity = null;
		Iterator iter = set.iterator();
		PropertyUtilsBean propUtil = new PropertyUtilsBean();
		Set childSet = null;
		int i = 1;
		
		while (iter.hasNext()) {
			entity = (AbstractKeyedEntity)iter.next();
			log("========= "+ name + " =========");
			log("[#" + i++ + "] " + toHistoryString(entity));
			// print children:
			for (int j=0;j<children.length;j++) {
				try {
					childSet = (Set) propUtil.getProperty(entity, children[j]);
				} catch (NestedNullException ne) {
					// null value
				} catch (Exception e) {
					fail("Cannot access property " + children[j], e);					
				}
				printSet(children[j], childSet);
			}
		}
	}
	
	protected void printProperty(String name, AbstractKeyedEntity entity) {
		log("------ "+ name + " ------");
			log(toHistoryString(entity));
	}

	protected void printMap(String name, Map map) {
		AbstractKeyedEntity entity = null;
		Iterator iter = map.keySet().iterator();
		log("------ "+ name + " ------");
		
		int i = 1;
		
		while (iter.hasNext()) {
			entity = (AbstractKeyedEntity)map.get(iter.next());
			log("[#" + i++ + "] " + toHistoryString(entity));
		}
	}

	private String toHistoryString(AbstractKeyedEntity entity) {
		String histString = null;
		if (entity != null) {
			histString = "id = " + entity.getEntityKey().getKeyValueAsString() + "; " +
			"historyId = " + entity.getHistoryId() + "; " +	
			"modifiedOn = " + entity.getModifiedOn() + "; " +
			"isDeleted = " + entity.isDeleted();
		}
		return histString;
	}
	
	private HistoryDAO historyDao = null;

	private EntityKey personKey = null;

	private Set retrievedChangeEvents = null;

	protected Class getRootObjectClass() {
		return Person.class;
	}
	
	protected void loadHistoryDAO() {
		if (historyDao == null) {
			historyDao = (HistoryDAO) applicationContext.getBean(getHistoryDAOName());
		}	
	}
		
	protected HistoryDAO getHistoryDAO() {
		return historyDao;
	}

	protected EntityKey getPersonKey() {
		return personKey;
	}
	
	protected void log(String message) {
		// for now, just use out
		System.out.println(message);
	}
	
	abstract protected String getHistoryDAOName();
	abstract protected String getPersonId();

	protected Set getRetrievedChangeEvents() {
		return retrievedChangeEvents;
	}

	protected void setRetrievedChangeEvents(Set retrievedChangeEvents) {
		this.retrievedChangeEvents = retrievedChangeEvents;
	}
	
}