/********************************************************************
 * Copyright © 2004 VHA. All rights reserved
 ********************************************************************/
// Package
package gov.va.med.fw.report.data.hibernate;

// Java classes
import java.lang.reflect.InvocationTargetException;
import java.util.List;

// Library classes
import org.apache.commons.lang.Validate;
import org.hibernate.Query;

// Framework classes
import gov.va.med.fw.report.data.ReportDataException;
import gov.va.med.fw.report.ReportConfiguration;
import gov.va.med.fw.report.data.QueryCriteria;
import gov.va.med.fw.util.Reflector;
import gov.va.med.fw.util.ReflectionException;

// ESR classes

/**
 * Uses reflection and dependency injection to allow for a logical
 * grouping of many methods into one derived DAO class.
 *
 * Project: Framework</br>
 * Created on: 11:42:43 AM </br>
 *
 * @author DNS   LEV
 */
public abstract class ConfigurableReportDataDAO extends HibernateReportDataDAO {

	/**
	 * An instance of serialVersionUID
	 */
	private static final long serialVersionUID = -8745384804766807093L;

	/**
	 * An instance of methodName
	 */
	private String methodName = null;
	
	/**
	 * An instance of subReportMethodName
	 */
	private String subReportMethodName = null;

	/**
	 * An instance of postDataRetrievalMethodName
	 */
	private String postDataRetrievalMethodName = null;
	
	/**
	 * An instance of subReportPostDataRetrievalMethodName
	 */
	private String subReportPostDataRetrievalMethodName = null;
	
	/**
	 * A name a method to be executed before retrieving data
	 */
	private String preDataRetrievalMethodName = null;
	
	/**
	 * A default constructor
	 */
	protected ConfigurableReportDataDAO() {
		super();
	}

	/**
	 * @param methodName The methodName to set.
	 */
	public void setMethodName(String methodName) {
		this.methodName = methodName;
	}

	/**
	 * @param postDataRetrievalMethodName The postDataRetrievalMethodName to set.
	 */
	public void setPostDataRetrievalMethodName(String postDataRetrievalMethodName) {
		this.postDataRetrievalMethodName = postDataRetrievalMethodName;
	}

	/**
	 * @param preDataRetrievalMethodName The preDataRetrievalMethodName to set
	 */
	public void setPreDataRetrievalMethodName(String preDataRetrievalMethodName) {
		this.preDataRetrievalMethodName = preDataRetrievalMethodName;
	}
	/**
	 * @param subReportMethodName The subReportMethodName to set.
	 */
	public void setSubReportMethodName(String subReportMethodName) {
		this.subReportMethodName = subReportMethodName;
	}

	/**
	 * @param subReportPostDataRetrievalMethodName The subReportPostDataRetrievalMethodName to set.
	 */
	public void setSubReportPostDataRetrievalMethodName( String subReportPostDataRetrievalMethodName ) {
		this.subReportPostDataRetrievalMethodName = subReportPostDataRetrievalMethodName;
	}

	/**
	 * @see gov.va.med.fw.report.data.hibernate.HibernateReportDataDAO#postDataRetrieval(java.util.List)
	 */
	public List postDataRetrieval( final List results, final ReportConfiguration config ) throws ReportDataException {
		
		String methodName = this.isSubReportPresent( config ) ? this.subReportPostDataRetrievalMethodName : this.postDataRetrievalMethodName;
		if( methodName != null ) {
			Object result = this.invokeMethod( methodName, new Object[]{results, config}	);
			return result instanceof List ? (List)result : null;
		}
		return super.postDataRetrieval(results,config);
	}
	
	/**
	 * @see gov.va.med.fw.report.data.ReportDataDAO#preDataRetrieval(gov.va.med.fw.report.ReportConfiguration)
	 */
	public void preDataRetrieval( final ReportConfiguration config )  throws ReportDataException {
		if( this.preDataRetrievalMethodName != null ) {
			this.invokeMethod( preDataRetrievalMethodName, new Object[]{config} );
		}
	}

	/**
	 * @see org.springframework.dao.support.DaoSupport#initDao()
	 */
	protected void initDao() throws Exception {
		super.initDao();
		Validate.notNull( this.methodName, "A name of a method to be invoked must be configured" );
	}
	
	protected boolean isSubReportPresent( ReportConfiguration config ) {
		boolean exist = false;
		QueryCriteria criteria = config.getQueryCriteria();
		QueryCriteria subCriteria = criteria != null ? criteria.getSubReportCriteria() : null;
		exist = subCriteria != null && (this.subReportMethodName != null || this.subReportPostDataRetrievalMethodName != null);
		return exist;
	}
	
	/**
	 * @see gov.va.med.fw.report.data.hibernate.HibernateReportDataDAO#buildQuery(gov.va.med.fw.report.ReportConfiguration)
	 */
	protected Query buildQuery( ReportConfiguration config ) throws ReportDataException {
		String derivedMethodName = this.isSubReportPresent( config ) ? this.subReportMethodName : this.methodName;
		Object result = this.invokeMethod( derivedMethodName, new Object[]{config} );
		return result instanceof Query ? (Query)result : null;
	}
	
	/**
	 * @see gov.va.med.fw.report.data.hibernate.HibernateReportDataDAO#buildCriteria(gov.va.med.fw.report.data.QueryCriteria, org.hibernate.Session)
	 */
	protected Object invokeMethod( String name, Object[] params ) throws ReportDataException {
		
		Object result = null;
		try {
			result = Reflector.invoke( this, name, params );
		}
		catch( InvocationTargetException e ) {
			throw new ReportDataException( "Failed to invoke method: " + this.methodName + " to build a query criteria" );
		}
		catch( NoSuchMethodException e ) {
			throw new ReportDataException( "Failed to find method: " + this.methodName + " to build a query criteria" );
		}
		catch( ReflectionException e ) {
			throw new ReportDataException( "Exception thrown in method: " + this.methodName + " to build a query criteria" );
		}
		return result;
	}
}