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

// Java classes
import java.io.Serializable;
import java.util.Map;

// Library classes
import org.apache.commons.beanutils.PropertyUtils;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRField;
import net.sf.jasperreports.engine.data.JRAbstractBeanDataSource;

// Framework classes
import gov.va.med.fw.report.ReportConfiguration;
import gov.va.med.fw.report.data.QueryCriteria;
import gov.va.med.fw.report.data.ReportDataIterator;
import gov.va.med.fw.report.data.ReportDataException;


/**
 * Project: Framework</br> Created on: 5:59:36 PM </br>
 * 
 * @author DNS   LEV
 */
public class JasperReportDataSource extends JRAbstractBeanDataSource implements Serializable {

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

	/**
	 * An instance of dataIterator
	 */
	private ReportDataIterator dataIterator = null;

	/**
	 * An instance of index
	 */
	private int index = 0;

	/**
	 * An instance of bean
	 */
	private Object bean = null;
	
	/**
	 * An instance of metaData
	 */
	private Map metaData = null;

	/**
	 * An instance of configuration
	 */
	private ReportConfiguration configuration = null;
	
	/**
	 * An instance of useFieldDescription
	 */
	private boolean useFieldDescription = false; 

	/**
	 * A default constructor
	 * @param iterator
	 * @param metaData
	 * @param config
	 */
	public JasperReportDataSource( ReportDataIterator iterator, Map metaData, ReportConfiguration config ) {
		this( iterator, metaData, config, false );
	}

	/**
	 * A default constructor
	 * @param dataIterator
	 * @param metaData
	 * @param config
	 * @param useFieldDescription
	 */
	public JasperReportDataSource( ReportDataIterator dataIterator, 
											 Map metaData, 
											 ReportConfiguration config, 
											 boolean useFieldDescription ) {
		super( useFieldDescription );
	
		this.metaData = metaData;
		this.dataIterator = dataIterator;
		this.useFieldDescription = useFieldDescription;
		this.configuration = config;
	}
	
	public JasperReportDataSource getSubDataSource( Object key, Object data ) {
		JasperReportDataSource subDataSource = null;
		QueryCriteria criteria = this.configuration != null ? this.configuration.getQueryCriteria() : null; 
		if( criteria != null ) {
			QueryCriteria subCriteria = new QueryCriteria();
			subCriteria.addCriterion( key, data );
			criteria.setSubReportCriteria( subCriteria );
			ReportDataIterator iterator = new ReportDataIterator( this.dataIterator.getReportDAO(), this.configuration );
			subDataSource = new JasperReportDataSource( iterator, this.metaData, this.configuration, this.useFieldDescription );
		}
		return subDataSource;
	}
	
	/**
	 * @see net.sf.jasperreports.engine.JRDataSource#next()
	 */
	public boolean next() throws JRException {
		try {
			bean = dataIterator.getRecord(index++);
			return (bean != null);
		}
		catch (ReportDataException e) {
			throw new JRException("Failed to check for a next record", e);
		}
	}

	/**
	 * @see net.sf.jasperreports.engine.JRRewindableDataSource#moveFirst()
	 */
	public void moveFirst() throws JRException {
		try {
			index = 0;
			bean = this.dataIterator.getRecord(index);
		}
		catch (ReportDataException e) {
			throw new JRException("Failed to set to a first record", e);
		}
	}

	public Object getFieldValue(JRField field) throws JRException {
		
		Object value = null;
		if( bean != null ) {
			
			String propertyName = this.getPropertyNameByField( field ); 
			try {
				// This is probably a result of Hibernate executing a sql statement
				if( bean instanceof Map ) {
					value = ((Map)bean).get( propertyName );
				}
				else if( bean instanceof Object[] ) {
					
					// A meta data map must be provided if a result collection is an array of Object
					if( this.metaData == null || this.metaData.isEmpty() ) {
						throw new JRException("Missing a meta data map to retrieve value for property: " + propertyName );
					}
					Object meta = this.metaData.get( propertyName );
					Integer index = meta instanceof Integer ? (Integer)meta : null; 
					if (index != null) {
						value = ((Object[])bean)[ index.intValue() ];
					}
				}
				else {
					value = PropertyUtils.getProperty(bean, propertyName);
				}
			}
			catch (java.lang.IllegalAccessException e)
			{
				throw new JRException("Error retrieving field value from bean : " + propertyName, e);
			}
			catch (java.lang.reflect.InvocationTargetException e)
			{
				throw new JRException("Error retrieving field value from bean : " + propertyName, e);
			}
			catch (java.lang.NoSuchMethodException e)
			{
				throw new JRException("Error retrieving field value from bean : " + propertyName, e);
			}
			catch (IllegalArgumentException e)
			{
				if (!e.getMessage().startsWith("Null property value for ")) 
				{
					throw e;
				}
			}
		}
		return value;
	}

	private String getPropertyNameByField( JRField field ) {
		return this.useFieldDescription ? field.getDescription() : field.getName();
	}
}