/**
 * QueryBuilder.java
 * Created May 19, 2008
 * Copyright (c) TANDBERG Television 2007-2008
 */
package com.tandbergtv.watchpoint.search;

import static com.tandbergtv.watchpoint.search.HQLQuery.AND;
import static com.tandbergtv.watchpoint.search.HQLQuery.FROM;
import static com.tandbergtv.watchpoint.search.HQLQuery.ORDERBY;
import static com.tandbergtv.watchpoint.search.HQLQuery.SELECT_DISTINCT;
import static com.tandbergtv.watchpoint.search.HQLQuery.WHERE;

import com.tandbergtv.workflow.driver.search.SearchParameterBase;
import com.tandbergtv.workflow.util.SearchCriteria;

/**
 * Builds an HQL query using a SearchCriteria
 * 
 * @author Sahil Verma
 */
public class QueryBuilder {
	
	/**
	 * Creates a QueryBuilder
	 */
	protected QueryBuilder() {
	}
	
	/**
	 * Returns a new instance of this builder
	 * 
	 * @return
	 */
	public static QueryBuilder newInstance() {
		return new QueryBuilder();
	}
	
	/**
	 * Builds the count query
	 * 
	 * @param criteria
	 * @return
	 */
	public String buildCountQuery(SearchCriteria criteria) {
		Entity e = null;
		for (SearchParameterBase s : criteria.getSearchList()) {
			if (s instanceof Entity) {
				e = (Entity) s;
				break;
			}
		}
		String select = "select count(distinct " + e.getCompleteAlias() + ")"; 
		
		return select + getFromClause(criteria) + getWhereClause(criteria);
	}
	
	/**
	 * Builds the search query
	 * 
	 * @param criteria
	 * @return
	 */
	public String buildQuery(SearchCriteria criteria) {
		if (criteria.getSearchList() == null || criteria.getSearchList().size() == 0)
			throw new IllegalArgumentException("Criteria must not be empty");
		/* UGLY - run the FROM clause first so that aliases are setup correctly */
		String from = getFromClause(criteria);
		String select = getSelectClause(criteria);
		String where = getWhereClause(criteria);
		String orderBy = getOrderByClause(criteria);
		
		return select + from + where + orderBy;
	}
	
	/**
	 * Creates the SELECT clause. Right now only the first entity is added to the clause.
	 * 
	 * @param parameter
	 * @return
	 */
	public String getSelectClause(SearchCriteria criteria) {
		/* FIXME Should DISTINCT be an option? */
		String clause = "";
		
		for (SearchParameterBase parameter : criteria.getSearchList()) {
			if (parameter instanceof Entity) {
				if (clause.length() > 0)
					clause += ", ";
				
				clause += Entity.class.cast(parameter).getSelectClause();
			}
		}
		
		return SELECT_DISTINCT + " " + clause + System.getProperty("line.separator");
	}
	
	/**
	 * Returns the complete FROM clause
	 * 
	 * @param criteria
	 * @return
	 */
	public String getFromClause(SearchCriteria criteria) {
		String clause = "";
		int count = 0;
		
		for (SearchParameterBase parameter : criteria.getSearchList()) {
			if (!(parameter instanceof Entity) && !(parameter instanceof Association))
				throw new RuntimeException("Criteria must contain only entities or associations");
			
			if (parameter instanceof Association)
				continue;
			
			Entity p = Entity.class.cast(parameter);
			
			if (clause.length() > 0)
				clause += ", ";
			
			clause += p.getPartialFromClause(count++);
		}
		
		clause = FROM + clause + System.getProperty("line.separator");
		
		return clause;
	}
	
	/**
	 * Returns the complete WHERE clause
	 * 
	 * @return
	 */
	public String getWhereClause(SearchCriteria criteria) {
		String clause = "";
		
		for (SearchParameterBase parameter : criteria.getSearchList()) {
			if (!(parameter instanceof Entity) && !(parameter instanceof Association))
				throw new RuntimeException("Criteria must contain only entities or associations");
			
			SearchParameter p = SearchParameter.class.cast(parameter);
			
			String c = p.getPartialWhereClause();
			if (c.trim().length() > 0) {
				if (clause.length() > 0) {
					clause += System.getProperty("line.separator") + "\t" + AND;
				}
				clause += c;
			}
		}
		
		if (clause.length() > 0)
			clause = WHERE + clause + System.getProperty("line.separator");
		
		return clause;
	}
	
	/**
	 * Returns the complete ORDER BY clause
	 * 
	 * @return
	 */
	public String getOrderByClause(SearchCriteria criteria) {
		String clause = "";
		
		for (SearchParameterBase parameter : criteria.getSearchList()) {
			if (!(parameter instanceof Entity))
				continue;
			
			Entity e = Entity.class.cast(parameter);
			
			String parameterClause = e.getPartialOrderByClause();
			if(!parameterClause.isEmpty() && !clause.isEmpty()) {
				clause += ",";
			}
			clause += parameterClause;
		}
		
		if (clause.length() > 0)
			clause = ORDERBY + clause;

		return clause;
	}
}
