/**
 * TitleSearchCriteriaBuilder.java
 * Created Jun 6, 2008
 * Copyright (c) TANDBERG Television 2007-2008
 */
package com.tandbergtv.watchpoint.pmm.web.title.search;

import static com.tandbergtv.watchpoint.pmm.entities.TitleListType.PITCH;
import static com.tandbergtv.watchpoint.pmm.entities.TitleListType.PLANNER;
import static com.tandbergtv.watchpoint.pmm.title.search.EntityName.METADATA;
import static com.tandbergtv.watchpoint.pmm.title.search.EntityName.PROGRESS;
import static com.tandbergtv.watchpoint.pmm.title.search.EntityName.TITLE;
import static com.tandbergtv.watchpoint.pmm.title.search.TitleSearchKey.COMPLETE_METADATA;
import static com.tandbergtv.watchpoint.pmm.title.search.TitleSearchKey.LIST_TYPE;
import static com.tandbergtv.watchpoint.pmm.title.search.TitleSearchKey.METADATA_NAME;
import static com.tandbergtv.watchpoint.pmm.title.search.TitleSearchKey.METADATA_VALUE;
import static com.tandbergtv.watchpoint.pmm.title.search.TitleSearchKey.PROGRESS_ITEMS;
import static com.tandbergtv.watchpoint.pmm.title.search.TitleSearchKey.TITLE_EXTERNAL_LOCATION;
import static com.tandbergtv.watchpoint.pmm.title.search.TitleSearchKey.TITLE_LISTS;
import static com.tandbergtv.watchpoint.pmm.title.search.TitleSearchKey.TITLE_PARENT;
import static com.tandbergtv.watchpoint.pmm.title.search.TitleSearchKey.TITLE_PROVIDER_ID;
import static com.tandbergtv.watchpoint.pmm.title.search.TitleSearchKey.TITLE_SEARCH_TYPE;
import static com.tandbergtv.watchpoint.pmm.title.search.TitleSearchKey.TITLE_SPEC;
import static com.tandbergtv.workflow.driver.search.SearchType.NUMERIC;
import static com.tandbergtv.workflow.driver.search.SearchType.STRING;

import javax.servlet.http.HttpServletRequest;

import org.apache.log4j.Logger;
import org.apache.struts.action.ActionForm;

import com.tandbergtv.watchpoint.pmm.entities.Title;
import com.tandbergtv.watchpoint.pmm.title.search.EntityName;
import com.tandbergtv.watchpoint.pmm.title.search.TitleSearchKey;
import com.tandbergtv.workflow.web.page.Field;
import com.tandbergtv.workflow.web.page.Page;
import com.tandbergtv.watchpoint.pmm.web.search.AbstractSearchCriteriaBuilder;
import com.tandbergtv.workflow.web.table.Column;
import com.tandbergtv.workflow.web.table.Table;
import com.tandbergtv.watchpoint.pmm.web.title.PagingForm;
import com.tandbergtv.watchpoint.pmm.web.title.TitleSearchType;
import com.tandbergtv.watchpoint.search.Entity;
import com.tandbergtv.watchpoint.search.Join;
import com.tandbergtv.workflow.driver.search.SearchParameterBase;
import com.tandbergtv.workflow.driver.search.SearchType;
import com.tandbergtv.workflow.driver.search.SortParameter;
import com.tandbergtv.workflow.driver.search.ValueParameter;
import com.tandbergtv.workflow.util.SearchCriteria;
import com.tandbergtv.workflow.util.SortingOrder;

/**
 * Builds the search criteria from the HTTP request
 * 
 * @author Sahil Verma
 */
public class TitleSearchCriteriaBuilder extends AbstractSearchCriteriaBuilder {
	
	private HttpServletRequest request;
	
	private ActionForm form;
	
	private static final Logger logger = Logger.getLogger(TitleSearchCriteriaBuilder.class);
	
	private TitleSearchCriteriaBuilder(HttpServletRequest request, ActionForm form) {
		this.request = request;
		this.form = form;
	}
	
	/**
	 * @return
	 */
	public static TitleSearchCriteriaBuilder newInstance(HttpServletRequest request, ActionForm form) {
		return new TitleSearchCriteriaBuilder(request, form);
	}
	
	/* (non-Javadoc)
	 * @see com.tandbergtv.watchpoint.pmm.web.search.AbstractSearchCriteriaBuilder#getRequest()
	 */
	protected HttpServletRequest getRequest() {
		return this.request;
	}
	
	/* (non-Javadoc)
	 * @see com.tandbergtv.watchpoint.pmm.web.search.AbstractSearchCriteriaBuilder#getForm()
	 */
	protected ActionForm getForm() {
		return this.form;
	}
	
	/* (non-Javadoc)
	 * @see com.tandbergtv.watchpoint.pmm.web.search.AbstractSearchCriteriaBuilder#getPage()
	 */
	protected Page getPage() {
		Page page = null;
		
		try {
			page = buildSearchPage(getRequest());
		} catch (Exception e) {
			throw new RuntimeException("Failed to build search page", e);
		}
		
		return page;
	}
	
	/* (non-Javadoc)
	 * @see com.tandbergtv.watchpoint.pmm.web.search.AbstractSearchCriteriaBuilder#getEntity(com.tandbergtv.workflow.util.SearchCriteria)
	 */
	protected Entity getEntity(SearchCriteria criteria) {
		return criteria.getParameter(TITLE.toString(), Entity.class);
	}

	/* (non-Javadoc)
	 * @see com.tandbergtv.watchpoint.pmm.web.search.AbstractSearchCriteriaBuilder#getSearchCriteria()
	 */
	public SearchCriteria getSearchCriteria() {
		SearchCriteria criteria = getDefaultSearchCriteria();
		
		/* Determine the title provider id */
		String providerId = this.getTitleProviderId(request); 
		criteria.addParameter(new ValueParameter(TITLE_PROVIDER_ID.toString(), STRING, providerId));

		super.addSearchCriteria(criteria);
		
		addSortParameter(criteria);
		
		return criteria;
	}
	
	/**
	 * Adds sorting parameters to the criteria
	 * 
	 * @param criteria
	 */
	protected void addSortParameter(SearchCriteria criteria) {
		PagingForm pagingForm = PagingForm.class.cast(form);
		String columnName = pagingForm.getSortingColumnName();
		
		/* Nothing to do... */
		if (columnName == null || columnName.length() == 0)
			return;
		
		String order = pagingForm.getSortingOrder();
		Table table = pagingForm.getTable();
		Column column = table.getColumnBySortName(columnName);
		
		logger.debug("Sort by " + columnName + " order " + order);
		
		if (column.getParameter() != null && getSearchType() == TitleSearchType.INTERNAL) {
			/* 
			 * Most columns display metadata fields. Sorting gets slightly tricky because we have to 
			 * explicitly select the field we're sorting by to make the SQL query work for
			 * the (default) relational title provider. Moreover, we might need to tweak the left join path
			 * in order to prevent dupes in the case where the same metadata field exists at multiple 
			 * levels in the title heirarchy.
			 */
			Entity title = getEntity(criteria);
			String sortProperty = COMPLETE_METADATA.toString();
			
			if (column.getSortingProperty() != null)
				sortProperty = column.getSortingProperty();
				
			Entity e = new Entity(column.getParameter(), sortProperty, "m");
			
			e.setJoin(Join.LEFT_OUTER);
			e.setFetch(true);
			e.addParameter(new ValueParameter(METADATA_NAME.toString(), STRING, column.getParameter()));
			e.addParameter(new SortParameter(METADATA_VALUE.toString(), SortingOrder.valueOf(order)));
			
			title.addParameter(e);
			
			logger.debug("Adding sort parameter " + column.getParameter());
		} else {
			Entity title = getEntity(criteria);
		
			title.addParameter(new SortParameter(columnName, SortingOrder.valueOf(order)));
		}
	}

	/* (non-Javadoc)
	 * @see com.tandbergtv.watchpoint.pmm.web.search.AbstractSearchCriteriaBuilder#addSearchCriteria(com.tandbergtv.workflow.util.SearchCriteria, com.tandbergtv.workflow.web.page.Field, java.lang.String[])
	 */
	protected void addSearchCriteria(SearchCriteria criteria, Field field, String... values) {
		Entity title = getEntity(criteria);
		
		if (METADATA.toString().equals(field.getEntity())) {
			Entity metadata = new Entity(field.getName(), COMPLETE_METADATA.toString(), "m");
			SearchParameterBase parameter = getCriterion(field, METADATA_VALUE.toString(), values);
			
			metadata.addParameter(new ValueParameter(METADATA_NAME.toString(), STRING, field.getName()));
			metadata.addParameter(parameter);
			
			title.addParameter(metadata);
		} else if (EntityName.PLANNER.toString().equals(field.getEntity())) {
			Entity planner = new Entity(field.getName(), TITLE_LISTS.toString(), "planner");
			SearchParameterBase parameter = getCriterion(field, field.getProperty(), values);
			
			planner.addParameter(parameter);
			planner.addParameter(new ValueParameter(LIST_TYPE.toString(), NUMERIC, PLANNER.ordinal()));
			
			title.addParameter(planner);
		} else if (EntityName.PITCH.toString().equals(field.getEntity())) {
			Entity pitch = new Entity(field.getName(), TITLE_LISTS.toString(), "pitch");
			SearchParameterBase parameter = getCriterion(field, field.getProperty(), values);
			
			pitch.addParameter(parameter);
			pitch.addParameter(new ValueParameter(LIST_TYPE.toString(), NUMERIC, PITCH.ordinal()));
			
			title.addParameter(pitch);
		} else if (PROGRESS.toString().equals(field.getEntity())) {
			Entity progress = new Entity(field.getName(), PROGRESS_ITEMS.toString(), "progress");
			SearchParameterBase parameter = getCriterion(field, field.getProperty(), values);
			
			progress.addParameter(parameter);
			
			title.addParameter(progress);
		} else {
			super.addSearchCriteria(criteria, field, values);
		}
	}
	
	/**
	 * Returns the default search critera for titles - basically just look for root titles
	 * 
	 * @return
	 */
	protected SearchCriteria getDefaultSearchCriteria() {
		SearchCriteria criteria = new SearchCriteria();
		
		Entity title = new Entity(TITLE.toString(), Title.class, "t");

		title.addParameter(new ValueParameter(TITLE_PARENT.toString(), STRING, (Object)null));
		title.addParameter(new ValueParameter("isActive", SearchType.NUMERIC, 1));
		
		criteria.addParameter(title);
		
		return criteria;
	}
	
	/**
	 * Returns the search page - either the internal repository or an external one
	 * 
	 * @return
	 */
	private TitleSearchType getSearchType() {
		String searchTypeValue = getRequest().getParameter(TITLE_SEARCH_TYPE.toString());
		TitleSearchType searchType = TitleSearchType.valueOf(searchTypeValue);

		return searchType;
	}

	/* Build the search page for the request */
	private Page buildSearchPage(HttpServletRequest request) throws Exception {
		/* Determine the specification and search type for building search page */
		String specification = request.getParameter(TITLE_SPEC.toString());
		TitleSearchType searchType = getSearchType();

		/* Build the page */
		TitleSearchPageBuilder pageBuilder = TitleSearchPageBuilder.newInstance();
		return pageBuilder.buildSearchPage(searchType, specification);
	}

	/* Get the Provider Id from the request */
	private String getTitleProviderId(HttpServletRequest request) {
		String searchTypeValue = request.getParameter(TITLE_SEARCH_TYPE.toString());
		TitleSearchType searchType = TitleSearchType.valueOf(searchTypeValue);
		
		String providerId = TitleSearchKey.DEFAULT_SYSTEM_ID.toString();
		if (searchType == TitleSearchType.EXTERNAL) {
			String instanceId = request.getParameter(TITLE_EXTERNAL_LOCATION.toString());
			providerId = this.getTitleProviderId(instanceId);
		}
		
		return providerId;
	}

	/* Get the provider Id from the title external source Id */
	private String getTitleProviderId(String externalSourceId) {
		String providerId = null;
		
		if (externalSourceId != null) {
			String[] values = externalSourceId.split("-");
			providerId = (values.length > 0) ? values[0] : "";
		}
		
		return providerId;
	}
}
