/**
 * ScheduleAssociatedHandler.java
 * Created on Jun 6, 2008
 * (C) Copyright TANDBERG Television Ltd.
 */
package com.tandbergtv.watchpoint.pmm.job.handler;

import static com.tandbergtv.watchpoint.pmm.entities.ScheduleStatus.APPROVED;
import static com.tandbergtv.watchpoint.pmm.job.util.JobScheduleInfoConstants.CONTEXTID;
import static com.tandbergtv.watchpoint.pmm.job.util.JobScheduleInfoConstants.JOB_NAME;
import static com.tandbergtv.watchpoint.pmm.job.util.JobScheduleInfoConstants.JOB_RULE_NAME;
import static com.tandbergtv.watchpoint.pmm.job.util.JobScheduleInfoConstants.JOB_RULE_PARAMETERS;
import static com.tandbergtv.watchpoint.pmm.job.util.JobScheduleInfoConstants.TITLES;

import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import com.tandbergtv.watchpoint.pmm.entities.ContainerType;
import com.tandbergtv.watchpoint.pmm.entities.IContainer;
import com.tandbergtv.watchpoint.pmm.entities.RuleParameter;
import com.tandbergtv.watchpoint.pmm.entities.Schedule;
import com.tandbergtv.watchpoint.pmm.job.callback.CallbackHelper;
import com.tandbergtv.watchpoint.pmm.job.callback.IJobCallback;
import com.tandbergtv.watchpoint.pmm.job.referenceEvaluator.ParameterReferencePath;
import com.tandbergtv.watchpoint.pmm.job.referenceEvaluator.ReferenceEvaluatorChain;
import com.tandbergtv.watchpoint.pmm.job.util.JobScheduleInfoConstants;
import com.tandbergtv.watchpoint.pmm.schedule.search.IScheduleSearchService;
import com.tandbergtv.workflow.core.service.ServiceRegistry;
import com.tandbergtv.workflow.core.service.cache.ICacheService;

/**
 * This class is responsible for getting all schedules based on the rule parameter filled in at the
 * time of job creation. For each of the schedules that match the condition, get the titles and
 * create a work order for each one of them.
 * 
 * @author spuranik
 */
public class ScheduleAssociatedHandler extends AbstractTitleAssociatedHandler {

	private Logger logger = Logger.getLogger(ScheduleAssociatedHandler.class);
	private static String SCHEDULE_PITCHDATE_FIELD = "pitchDate";
	private static String PLANNER_DATE_FIELD = "plannerDate";
	private static String CONTAINER_CACHE_SERVICE_NAME = "Container Cache";

	/**
	 * @param next
	 */
	public ScheduleAssociatedHandler(IJobCallback next) {
		super(next);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.tandbergtv.watchpoint.pmm.job.handler.AbstractTitleAssociatedHandler#searchTitles(com.tandbergtv.workflow.util.SearchCriteria)
	 */
	@Override
	@SuppressWarnings("unchecked")
	public Collection<?> search(ParameterReferencePath path, Map<String, Object> searchInfo,
			Date jobExecutionDate) {
		Collection<?> schedules = null;
		String ruleName = (String) searchInfo.get(JOB_RULE_NAME);
		List<RuleParameter> ruleParameters = (List<RuleParameter>) searchInfo.get(JOB_RULE_PARAMETERS);
		long contextId = ((Long) searchInfo.get(CONTEXTID)).longValue();
		Date searchDate = getSearchDate(ruleName, ruleParameters, jobExecutionDate);

		IScheduleSearchService searchService = ServiceRegistry.getDefault().lookup(
				IScheduleSearchService.class);

		// search for pitch schedules if the field is 'pitchDate'
		if (path.getPropertyField().equalsIgnoreCase(SCHEDULE_PITCHDATE_FIELD)) {
			schedules = searchService.getPitchSchedulesByPitchDate(contextId, searchDate, APPROVED);
			logger.info("Search resulted in " + schedules.size() + " pitch schedules.");
		} else {
			// else search for planners if the field is 'plannerDate'
			if (path.getPropertyField().equalsIgnoreCase(PLANNER_DATE_FIELD)) {
				// get the partnerId for this context
				ICacheService<IContainer> containerCache = (ICacheService<IContainer>) ServiceRegistry
						.getDefault().lookup(CONTAINER_CACHE_SERVICE_NAME);
				// The container has to be a PARTNER. SERVICE only has pitch schedules, no
				// planners.
				IContainer container = containerCache.get(contextId);
				if (container == null) {
					throw new RuntimeException("No container found with contextId: " + contextId);
				}
				if (container.getContainerType() == ContainerType.SERVICE) {
					String errorMessage = "Job execution failed for job: "
							+ searchInfo.get(JobScheduleInfoConstants.JOB_NAME) + " ["
							+ container.getContainerType() + ":" + container.getContainerName()
							+ "]" + " Planners cannot be associated with services.";
					throw new RuntimeException(errorMessage);
				}
				schedules = searchService.getPlannersByDate(container.getContainerId(), searchDate);
				logger.info("Search resulted in " + schedules.size() + " planners.");
			}
		}
		return schedules;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.tandbergtv.watchpoint.pmm.job.handler.AbstractTitleAssociatedHandler#createWorkOrder(java.util.Collection,
	 *      java.util.Map)
	 */
	@Override
	@SuppressWarnings("unchecked")
	protected void processItems(Collection<?> items, Map<String, Object> callbackInfo) {
		// iterate thru all schedules
		// evaluate the parameter reference for this schedule
		// for all titles within this schedule, invoke the next callback in the chain
		ReferenceEvaluatorChain evaluatorChain = ReferenceEvaluatorChain.getInstance();

		/* FIXME Ideally the list of schedules should get sent along in the map to the next callback */
		for (Schedule schedule : (Collection<Schedule>) items) {
			logger.debug("Found schedule " + schedule);
			
			try {
				Map<String, Object> clone = CallbackHelper.clone(callbackInfo);
				
				evaluatorChain.evaluate(schedule, clone);
				
				/* Add titles belonging to this schedule *after* we've cloned the data */
				clone.put(TITLES, schedule.getTitles());
				
				this.next.executeJob(clone, null);
			} catch (RuntimeException e) {
				IContainer container = CallbackHelper.getContainer(callbackInfo);
				logger.error("Job execution failed for: "
						+ callbackInfo.get(JOB_NAME) + " ["
						+ container.getContainerType() + ":" + container.getContainerName() + "]"
						+ " Error while evaluating parameters or executing job for schedule "
						+ schedule.getDate(), e);
			}
		}
	}
}
