/**
 * SearchService.java
 * Created Jan 10, 2007
 * Copyright (C) Tandberg Television 2007
 */
package com.tandbergtv.workflow.driver.search;

import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;

import com.tandbergtv.workflow.core.ProcessStatus;
import com.tandbergtv.workflow.core.WorkflowProcess;
import com.tandbergtv.workflow.core.WorkflowTemplate;
import com.tandbergtv.workflow.core.service.ServiceRegistry;
import com.tandbergtv.workflow.core.service.cache.ICacheService;
import com.tandbergtv.workflow.driver.service.IProcessSearchService;
import com.tandbergtv.workflow.util.SearchCriteria;

/**
 * Default search service implementation
 * 
 * @author Sahil Verma
 */
public class SearchService implements IProcessSearchService {
	
	private SessionFactory factory;

	public static final String SERVICE_NAME = "Process Search Service";
	
	/**
	 * Creates a SearchService
	 */
	public SearchService(SessionFactory factory) {
		super();
		this.factory = factory;
	}
	
	/* (non-Javadoc)
	 * @see com.tandbergtv.workflow.driver.service.IProcessSearchService#count(com.tandbergtv.workflow.util.SearchCriteria)
	 */
	public int count(SearchCriteria criteria) {
		return getSearchHelper().count(criteria);
	}
	
	/* (non-Javadoc)
	 * @see com.tandbergtv.workflow.driver.service.IProcessSearchService#count(com.tandbergtv.workflow.core.ProcessStatus)
	 */
	@SuppressWarnings("serial")
	public int count(final ProcessStatus status) {
		return count(
			new SearchCriteria() {{ 
				addParameter(new ValueParameter(SearchKeyConstants.STATUS, SearchType.PROCESSSTATUS, status));
			}}
		);
	}

	/* (non-Javadoc)
	 * @see com.tandbergtv.workflow.driver.service.IProcessSearchService#count(com.tandbergtv.workflow.core.ProcessStatus, java.util.Date)
	 */
	public int count(ProcessStatus status, Date cutOffDate) {
		SearchCriteria criteria = new SearchCriteria();
		ValueParameter parameter = new ValueParameter(SearchKeyConstants.STATUS, SearchType.PROCESSSTATUS, status);
		String date = new SimpleDateFormat(SearchParameterBase.DATE_FORMAT).format(cutOffDate);
		RangeParameter rangeParam = new RangeParameter(SearchKeyConstants.WO_START_DATE, SearchType.DATE, false);
		rangeParam.setFrom(date);
		
		criteria.addParameter(parameter);
		criteria.addParameter(rangeParam);
		
		return count(criteria);
	}
	
	/* (non-Javadoc)
	 * @see com.tandbergtv.workflow.driver.service.IProcessSearchService#count(java.util.List)
	 */
	@SuppressWarnings("serial")
	public int count(List<ProcessStatus> statuslist) {
		final ListParameter parameter = new ListParameter(SearchKeyConstants.STATUS, SearchType.PROCESSSTATUS);
		
		for (ProcessStatus status : statuslist)
			parameter.addValue(status);
		
		return count(new SearchCriteria() {{ addParameter(parameter); }} );
	}
	
	/* (non-Javadoc)
	 * @see com.tandbergtv.workflow.driver.service.IProcessSearchService#count(com.tandbergtv.workflow.core.WorkflowTemplate, com.tandbergtv.workflow.core.ProcessStatus[])
	 */
	public int count(WorkflowTemplate template, ProcessStatus... status) {
		Session session = null;
		int count = 0;
		
		try {
			session = this.factory.openSession();

			Criteria criteria = session.createCriteria(WorkflowProcess.class)
				.add(Restrictions.in("status", status))
				.add(Restrictions.eq("active", true))
				.add(Restrictions.eq("processDefinition.id", template.getId()))
				.setProjection(Projections.rowCount())
				.createCriteria("processDefinition")
				.add(Restrictions.sqlRestriction("processDefinitionTypeId=2"));

			count = (Integer) criteria.uniqueResult();
		} finally {
			if (session != null)
				session.close();
		}
		
		return count;
	}

	/* (non-Javadoc)
	 * @see com.tandbergtv.workflow.driver.service.IProcessSearchService#findAllByStatus(com.tandbergtv.workflow.core.ProcessStatus)
	 */
	@SuppressWarnings("serial")
	public List<WorkflowProcess> findAllByStatus(final ProcessStatus status) {
		return findAllByStatus(new ArrayList<ProcessStatus>() {{ add(status); }});
	}

	/* (non-Javadoc)
	 * @see com.tandbergtv.workflow.driver.service.IProcessSearchService#findAllByStatus(java.util.List)
	 */
	public List<WorkflowProcess> findAllByStatus(List<ProcessStatus> statuslist) {
		SearchCriteria criteria = new SearchCriteria();
		final ListParameter parameter = new ListParameter(SearchKeyConstants.STATUS, SearchType.PROCESSSTATUS);
		
		for (ProcessStatus status : statuslist)
			parameter.addValue(status);
		
		criteria.addParameter(parameter);
		
		List<WorkflowProcess> list = search(criteria);
		
		return list;
	}

	/* (non-Javadoc)
	 * @see com.tandbergtv.workflow.driver.service.IProcessSearchService#findAllByStatus(com.tandbergtv.workflow.core.ProcessStatus[])
	 */
	public List<WorkflowProcess> findAllByStatus(ProcessStatus... statuslist) {
		List<ProcessStatus> list = new ArrayList<ProcessStatus>();
		
		for (ProcessStatus status : statuslist)
			list.add(status);
		
		return findAllByStatus(list);
	}

	/* (non-Javadoc)
	 * @see com.tandbergtv.workflow.driver.service.IProcessSearchService#search(com.tandbergtv.workflow.util.SearchCriteria)
	 */
	@SuppressWarnings("unchecked")
	public List<WorkflowProcess> search(SearchCriteria searchCriteria) {
		ICacheService<WorkflowProcess> cache = (ICacheService<WorkflowProcess>)
			ServiceRegistry.getDefault().lookup("Process Cache");
		List<WorkflowProcess> processes = new ArrayList<WorkflowProcess>();
		
		for (WorkflowProcess process : getSearchHelper().search(searchCriteria)) {
			/* Remember to load the process from the cache if it's already there */
			Serializable key = process.getId();
			WorkflowProcess cached = cache.get(key);
			
			if (cached != null)
				processes.add(cached);
			else
				processes.add(process);
		}
		
		return processes;
	}

	/* (non-Javadoc)
	 * @see com.tandbergtv.workflow.core.service.Service#getServiceName()
	 */
	public String getServiceName() {
		return SERVICE_NAME;
	}

	/* (non-Javadoc)
	 * @see com.tandbergtv.workflow.core.service.ServiceLifecycle#start()
	 */
	public void start() {
	}

	/* (non-Javadoc)
	 * @see com.tandbergtv.workflow.core.service.ServiceLifecycle#stop()
	 */
	public void stop() {
	}

	private ISearchHelper getSearchHelper() {
		return new WorkflowProcessSearchHelper(this.factory);
	}
}
