/********************************************************************
 * Copyright © 2006 VHA. All rights reserved
 ********************************************************************/

package gov.va.med.fw.persistent.hibernate;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang.Validate;
import org.hibernate.Query;
import org.hibernate.ScrollMode;
import org.hibernate.ScrollableResults;
import org.hibernate.Session;

import gov.va.med.fw.persistent.QueryIncrementTracker;
import gov.va.med.fw.persistent.QueryInfo;
import gov.va.med.fw.persistent.QueryResults;
import gov.va.med.fw.persistent.ScrollableCallback;

/**
 * Allows for scrolling behavior such that the client does not have to be
 * concerned with Hibernate Session management.
 * 
 * Created Sep 18, 2006 5:17:40 PM
 * 
 * @author DNS   BOHMEG
 */
public class ScrollableDAOAction extends AbstractDAOAction {
	private ScrollableCallback handler;
	private QueryInfo query;
	private QueryIncrementTracker tracker;

	protected ScrollableDAOAction(ScrollableCallback handler, QueryInfo query, QueryIncrementTracker tracker) {
		this.handler = handler;		
		this.query = query;
		if(tracker != null) 
			this.tracker = tracker;
		else
			this.tracker = new QueryResults();
	}


	protected ScrollableDAOAction(ScrollableCallback handler, QueryInfo query) {
		this(handler, query, null);
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see gov.va.med.fw.persistent.hibernate.AbstractDAOAction#execute(org.hibernate.Session)
	 */
	protected Object execute(Session session) {
		Query queryImpl = query.isNamedQuery() ? session.getNamedQuery(query.getQuery()) :
			session.createQuery(query.getQuery());
		queryImpl.setFetchSize(query.getFetchSize());

		String[] paramNames = query.getParamNames();
		Object[] paramValues = query.getParamValues();
		if (paramNames != null && paramValues != null) {
			Validate.isTrue(paramNames.length == paramValues.length,
					"ParamNames and paramValues array must be of same size.");

			for (int i = 0; i < paramNames.length; i++) {
				queryImpl.setParameter(paramNames[i], paramValues[i]);
			}
		}

		ScrollableResults results = queryImpl.scroll(ScrollMode.FORWARD_ONLY);

		List data = new ArrayList();
		int total = 0;
		int scrollCheckSize = 5*query.getFetchSize();
		
		while (results.next()) {
			total++;
			data.add(results.get());

			// Write out in chunks of data equal to fetch size
			//if (data.size() == query.getFetchSize() || results.isLast() /* this bombs?! */) {
			if (data.size() == scrollCheckSize) {
				if(!handler.continueScrolling(tracker)) {
					break;
				}
				callback(data, total);
				data.clear();
			}
		}
		// handle last set of data
		if(!data.isEmpty()) {
			callback(data, total);			
		}
		return new Integer(total);
	}
	
	private void callback(List data, int count) {
		tracker.setIncrementalData(data);
		tracker.setCurrentRecord(count);
		handler.handleScrolledData(tracker);		
	}

	/**
	 * @return Returns the handler.
	 */
	public ScrollableCallback getHandler() {
		return handler;
	}

	/**
	 * @param handler
	 *            The handler to set.
	 */
	public void setHandler(ScrollableCallback handler) {
		this.handler = handler;
	}

	/**
	 * @return Returns the query.
	 */
	public QueryInfo getQuery() {
		return query;
	}

	/**
	 * @param query The query to set.
	 */
	public void setQuery(QueryInfo query) {
		this.query = query;
	}
}
