package com.apropobenefits.wicket.hibernate;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;

import wicket.contrib.data.model.ISelectCountAndListAction;
import wicket.contrib.data.model.hibernate.IHibernateSessionDelegate;

/**
 * An abstract class that builds on a DetatchedCriteria query provided
 * by a base class.
 * 
 * @author Phil Kulak
 */
public abstract class CriteriaCountAndListAction implements ISelectCountAndListAction {
	private IHibernateSessionDelegate sessionDelegate;
	private List orderCols = new ArrayList();
	
	public CriteriaCountAndListAction(IHibernateSessionDelegate sessionDelegate) {
		this.sessionDelegate = sessionDelegate;
	}

	/**
	 * @see wicket.contrib.data.model.ISelectObjectAction#execute(java.lang.Object)
	 */
	public Object execute(Object queryObject) {
		return getBaseCriteria(queryObject, sessionDelegate.getSession())
			.setProjection(Projections.rowCount())
			.uniqueResult();
	}

	/**
	 * @see wicket.contrib.data.model.ISelectListAction#execute(java.lang.Object, int, int)
	 */
	public List execute(Object queryObject, int startFromRow, int numberOfRows) {
		return getOrderedCriteria(queryObject, sessionDelegate.getSession())
			.setFirstResult(startFromRow)
			.setMaxResults(numberOfRows)
			.list();
	}
	
	/**
	 * Adds an ordering to the query. If the column already exists, it's
	 * ordering is flipped.
	 */
	public void addOrdering(String field) {
		boolean ascending = true;
		WicketOrder order = new WicketOrder(field);
		int i = orderCols.indexOf(order);
		if (i != -1) {
			WicketOrder current = (WicketOrder) orderCols.remove(i);
			ascending = !current.ascending;
		}
		order.ascending = ascending;
		orderCols.add(0, order);
	}
	
	/**
	 * Removes the ordering on the field.
	 */
	public void removeOrdering(String field) {
		orderCols.remove(new WicketOrder(field));
	}
	
	/**
	 * Override this to return a criteria query with no agregation or ordering
	 * and all parameters set.
	 * 
	 * param queryObject I don't know what this is, but it's probably null
	 */
	protected abstract Criteria getBaseCriteria(Object queryObject,
			Session session);
	
	/**
	 * Adds the orderings to the base query.
	 */
	private Criteria getOrderedCriteria(Object queryObject, Session session) {
		Criteria ret = getBaseCriteria(queryObject, session);
		
		for (Iterator i = orderCols.iterator(); i.hasNext();) {
			WicketOrder order = (WicketOrder) i.next();
			ret.addOrder(order.getHibernateOrder());
		}
		return ret;
	}
	
	/**
	 * Adds a couple methods to Hibernate's Order class.
	 * 
	 * @author Phil Kulak
	 */
	protected static class WicketOrder {
		String field;
		boolean ascending = true;
		
		public WicketOrder(String propertyName) {
			this.field = propertyName;
		}
		
		public Order getHibernateOrder() {
			return ascending ? Order.asc(field) : Order.desc(field);
		}
		
		@Override
		public boolean equals(Object rhs) {
			if (!(rhs instanceof WicketOrder)) {
				return false;
			}
			return field.equals(((WicketOrder)rhs).field);
		}
	}
}
