/*
 * Created on Jul 28, 2005
 *
 * To change the template for this generated file go to
 * Window>Preferences>Java>Code Generation>Code and Comments
 */
package jreport.uds.javabean;

import java.beans.IntrospectionException;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.Vector;

import jreport.uds.javabean.exception.JavaBeanDataProviderException;
import jreport.uds.javabean.exception.RptDataInitializerException;
import jreport.uds.javabean.api.JavaBeanDataProvider;
import jreport.uds.javabean.api.RptDataInitializer;


/**
 * @author mdaly
 * GBeanProvider_BeanClsName,
 * GBeanProvider_UseFakeData,
 * GBeanProvider_NumOfFakeData
 * GBeanProvider_FakeDateSubCollectionInfo=<propname1,clsname1$propsname2,clsname2>
 * GBeanProvider_RptDataInitializer
 * GBeanProvider_ListOfDetailProps=<propname1,sharecount1,timeout$propname2,sharecount2, timeout>
 * To change the template for this generated type comment go to
 * Window>Preferences>Java>Code Generation>Code and Comments
 */
public class GenericBeanDataProvider implements JavaBeanDataProvider {
	protected final String UDS_PARAM_BeanClsName 			= "GBeanProvider_BeanClsName";
	protected final String UDS_PARAM_UseFakeData			= "GBeanProvider_UseFakeData";
	protected final String UDS_PARAM_NumOfFakeData			= "GBeanProvider_NumOfFakeData";
	protected final String UDS_PARAM_FakeDateSubCollectionInfo= "GBeanProvider_FakeDateSubCollectionInfo";
	protected final String UDS_PARAM_RptDataInitializer		= "GBeanProvider_RptDataInitializer";
	protected final String UDS_PARAM_ListOfDetailProps		= "GBeanProvider_ListOfDetailProps";

	protected String beanRecordClsName= null;
	protected Object dataCollection = null;
	protected boolean useFakedata = true;
	protected int	numOfFakeData = 10;
	protected String clsRptDataInitializer= null;

	private Hashtable declaredSubCollectionProps=null;
	private Hashtable subCollectionPropsClsForFakeData = null;
		
	private Iterator dataIterator=null;
	private int currentRecord=-1;	
	private RptDataInitializer dataInitializer = null;
	
	/* (non-Javadoc)
	 * @see jreport.uds.javabean.JavaBeanDataProvider#init(java.lang.String, java.util.Properties)
	 */
	public void init(String dataID, Properties initprops)throws JavaBeanDataProviderException {
		
		initProperties( initprops);
				
		if( useFakedata){ // value is set and it is true.
			try {
				populateData();
			} catch (Exception e) {
				UDSForJavaBeanLogger.debug(e);
				throw new JavaBeanDataProviderException(null,"Class "+beanRecordClsName+ " cannot be loaded or the Class cannot be populated. Please check your classpath.");
			}
		}else{
			
			Class cls;
			try {
				cls = Class.forName(clsRptDataInitializer);
			} catch (ClassNotFoundException e) {
				
				UDSForJavaBeanLogger.debug(e);
				throw new JavaBeanDataProviderException(e, "Cannot load the specified Data Initilizer Class:"+clsRptDataInitializer+". Please check CLASSPATH set up.");
			}
			try {
				this.dataInitializer = (RptDataInitializer)cls.newInstance();
			} catch (InstantiationException e1) {
				UDSForJavaBeanLogger.debug(e1);
				throw new JavaBeanDataProviderException(e1, 
						"Failed to create instance of Data Initilizer Class:"+clsRptDataInitializer+". Please check details in log file.");
			} catch (IllegalAccessException e1) {
				UDSForJavaBeanLogger.debug(e1);
				throw new JavaBeanDataProviderException(e1, 
						"Failed to create instance of Data Initilizer Class:"+clsRptDataInitializer+". Please check details in log file.");
			}
			try {
				dataCollection = dataInitializer.getDataCollection(initprops);
				UDSForJavaBeanLogger.debug("GenericBeanDataProvider-Get data collection:"+dataCollection);
			} catch (RptDataInitializerException e2) {
				UDSForJavaBeanLogger.debug(e2);
				throw new JavaBeanDataProviderException(e2, 
						"Failed get data collection. Please check details in log file.");
			}
		}
	}
	
	/* (non-Javadoc)
	 * @see jreport.uds.javabean.JavaBeanDataProvider#getMetadataJavaBean()
	 */
	public Class getMetadataJavaBean() throws ClassNotFoundException {
		return Class.forName(beanRecordClsName);
	}

	/* (non-Javadoc)
	 * @see jreport.uds.javabean.JavaBeanDataProvider#next()
	 */
	public Object next() throws JavaBeanDataProviderException {
		if( dataCollection== null)
			return null;
		
		if( dataCollection instanceof Collection){
			return getNextFromCollection();
		}else if( dataCollection instanceof Vector){
			return getNextFromVector();
		}else if(dataCollection.getClass().isArray()){
			return getNextFromArray();
		}else
			throw new JavaBeanDataProviderException(null, "Unknow collection type "+dataCollection.getClass());
	}
	
	protected Object getNextFromCollection(){
		if( dataIterator == null)
			dataIterator = ((Collection)dataCollection).iterator();
		
		if(dataIterator.hasNext()){
			return dataIterator.next();
		}else{
			return null;
		}
	}

	protected Object getNextFromVector(){
		Vector dataCollection = (Vector)this.dataCollection;
		if( currentRecord ==-1 && dataCollection != null && dataCollection.size()>0){
			currentRecord = 0;
			return ((Vector)dataCollection).elementAt(currentRecord);
		}else if(currentRecord != -1 && ( currentRecord < (dataCollection.size()-1))){
			currentRecord ++;
			return ((Vector)dataCollection).elementAt(currentRecord);
		}
		return null;
		
	}
	
	protected Object getNextFromArray(){
		
		if( currentRecord ==-1 && Array.getLength(dataCollection)>0){
			currentRecord = 0;
			return Array.get(dataCollection, currentRecord);
		}else if(currentRecord != -1 && ( currentRecord < Array.getLength(dataCollection)-1)){
			currentRecord ++;
			return Array.get(dataCollection, currentRecord) ;
		}
		return null;
	}

	/* (non-Javadoc)
	 * @see jreport.uds.javabean.JavaBeanDataProvider#exit()
	 */
	public void exit() throws JavaBeanDataProviderException {
		if(dataInitializer !=null)
			this.dataInitializer.close();
	}
	
	
	protected void initProperties(Properties props) throws JavaBeanDataProviderException{
		beanRecordClsName = (String)props.get(UDS_PARAM_BeanClsName);
		if( beanRecordClsName == null ||beanRecordClsName.trim().length()==0){
			throw new JavaBeanDataProviderException(null, UDS_PARAM_BeanClsName+" is not specified for the data provdider GenericBeanDataProvider");
		}
		
		String listOfDetailProps = (String)props.get(UDS_PARAM_ListOfDetailProps);
		if( listOfDetailProps != null && listOfDetailProps.trim().length()>0){
			declaredSubCollectionProps = new Hashtable();
			StringTokenizer tokenizer = new StringTokenizer(listOfDetailProps, "$");
			UDSForJavaBeanLogger.debug("Adding sub collection's property info>>>");
			
			String next =null;
			StringTokenizer tokenizer0;
			String propName;
			int iMaxShareTimes;
			
			while (tokenizer.hasMoreElements()){ // for each property
				next = (String)tokenizer.nextElement();
				UDSForJavaBeanLogger.debug(next);
				tokenizer0 = new StringTokenizer(next.trim(),",");
				
				if( tokenizer0.hasMoreElements()){
					propName = (String)tokenizer0.nextElement();
					if( tokenizer0.hasMoreElements()){
						iMaxShareTimes = Integer.parseInt((String)tokenizer0.nextElement());
						UDSForJavaBeanLogger.debug("PropName:"+propName+" MaxShareTimes:"+iMaxShareTimes);
						if( tokenizer0.hasMoreElements()){
							int iTimeout = Integer.parseInt((String)tokenizer0.nextElement());
							declaredSubCollectionProps.put(propName, new SubCollectionPropInfo(iMaxShareTimes, iTimeout));
						}else{
							declaredSubCollectionProps.put(propName, new SubCollectionPropInfo(iMaxShareTimes, 60000));
						}
					}else{
						throw new JavaBeanDataProviderException(null, 
								"Please make sure the sub collection property information contains property name, and max share times:"+
								next);
					}
				}
			}
			UDSForJavaBeanLogger.debug("End of adding sub collection's property name>>>");
		}
		
	
		String useFakeData =(String) props.get(UDS_PARAM_UseFakeData);
		if( useFakeData!=null && !useFakeData.trim().equals("") )// no value set to the parameter 
			this.useFakedata = new Boolean(useFakeData).booleanValue(); // value is set and it is true.
		
		if(useFakedata){
			
			// init the number of fake data
			String strNumOfFakeData =(String)props.get(UDS_PARAM_NumOfFakeData);
			if(strNumOfFakeData !=null && strNumOfFakeData.trim().length()>0){
				this.numOfFakeData = Integer.parseInt(strNumOfFakeData);
			}
			
			// init the sub collection class information for fake data
			String strList = (String)props.get(UDS_PARAM_FakeDateSubCollectionInfo);
			if( strList != null && 
					strList.trim().length()>0){
				
				subCollectionPropsClsForFakeData = new Hashtable();
				StringTokenizer tokenizer = new StringTokenizer(strList, "$");
				UDSForJavaBeanLogger.debug("Adding sub collection's cls info for fake data>>>");
				
				String next =null;
				StringTokenizer tokenizer0;
				String propName;
				String clsName;
				
				while (tokenizer.hasMoreElements()){ // for each property
					next = (String)tokenizer.nextElement();
					UDSForJavaBeanLogger.debug(next);
					tokenizer0 = new StringTokenizer(next,",");
					
					if( tokenizer0.hasMoreElements()){
						propName = (String)tokenizer0.nextElement();
						if( tokenizer0.hasMoreElements()){
							clsName = (String)tokenizer0.nextElement();
							subCollectionPropsClsForFakeData.put(propName, clsName);
						}else{
							throw new JavaBeanDataProviderException(null, 
									"Please make sure "+UDS_PARAM_FakeDateSubCollectionInfo+"  contains property name and class name:"+
									next);
						}
					}
				}
				UDSForJavaBeanLogger.debug("End of adding sub collection's property name>>>");
			}else if(declaredSubCollectionProps!=null &&declaredSubCollectionProps.size()>0){
				throw new JavaBeanDataProviderException(null, "Please make sure you specify "+UDS_PARAM_FakeDateSubCollectionInfo+" property to pupulate fake data");
			}
		}
		
		clsRptDataInitializer =(String) props.get(this.UDS_PARAM_RptDataInitializer);
		
	}
	
	protected  void populateData() throws ClassNotFoundException,IllegalArgumentException, InvocationTargetException, InstantiationException, IllegalAccessException, IntrospectionException{
		dataCollection = new ArrayList();
		
		for( int i=0; i < this.numOfFakeData; i++){
			Class cls = Class.forName(beanRecordClsName);
			Object data1;
			data1 = cls.newInstance();
			FakeDataGeneratorForJavaBean.populate(data1, subCollectionPropsClsForFakeData,numOfFakeData);
			((ArrayList)dataCollection).add(data1);
		}
		
	}

	/**
	 * check to see the the property is a collection property needs to return to the report 
	 * with ID to run sub-report
	 */
	public boolean requireDetails(String collectionPropName) {
		UDSForJavaBeanLogger.debug("Checking if it is required details on the name:"+collectionPropName);
		UDSForJavaBeanLogger.debug("declaredSubCollectionProps="+declaredSubCollectionProps+ " size:"+
				(declaredSubCollectionProps==null?0:declaredSubCollectionProps.size()));
		
		boolean ret = declaredSubCollectionProps==null?false:declaredSubCollectionProps.containsKey(collectionPropName);
		UDSForJavaBeanLogger.debug("contains?"+ret);
		return ret;
	}

	/* (non-Javadoc)
	 * @see jreport.uds.javabean.JavaBeanDataProvider#getMaxShare(java.lang.String)
	 */
	public int getMaxShareTimes(String collectionPropName) {
		int i =1;
		
		if( collectionPropName!=null && declaredSubCollectionProps !=null){
			SubCollectionPropInfo propInfo = (SubCollectionPropInfo)declaredSubCollectionProps.get(collectionPropName);
			if( propInfo != null)
				i = propInfo.getMaxShareTimes();
		}
		
		UDSForJavaBeanLogger.debug("The max share times for collection prop "+collectionPropName+" is:"+i);
		return i;

	}

	/* (non-Javadoc)
	 * @see jreport.uds.javabean.JavaBeanDataProvider#getTimeoutForSubCollection(java.lang.String)
	 */
	public int getTimeoutForSubCollection(String collectionPropName) {
		int i =-1;
		
		if( collectionPropName!=null && declaredSubCollectionProps !=null){
			SubCollectionPropInfo propInfo = (SubCollectionPropInfo)declaredSubCollectionProps.get(collectionPropName);
			if( propInfo != null)
				i = propInfo.getTimeout();
		}
		
		UDSForJavaBeanLogger.debug("The max share times for collection prop "+collectionPropName+" is:"+i);
		return i;
	}
}
