/**
 * AbstractTitleAssociatedHandler.java
 * Created on Jun 6, 2008
 * (C) Copyright TANDBERG Television Ltd.
 */
package com.tandbergtv.watchpoint.pmm.job.handler;

import java.util.Calendar;
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.IContainer;
import com.tandbergtv.watchpoint.pmm.entities.RuleParameter;
import com.tandbergtv.watchpoint.pmm.entities.RuleParameterDataType;
import com.tandbergtv.watchpoint.pmm.entities.RuleType;
import com.tandbergtv.watchpoint.pmm.entities.RuleTypeParameter;
import com.tandbergtv.watchpoint.pmm.job.IJobManager;
import com.tandbergtv.watchpoint.pmm.job.JobManager;
import com.tandbergtv.watchpoint.pmm.job.callback.CallbackHelper;
import com.tandbergtv.watchpoint.pmm.job.referenceEvaluator.JobData;
import com.tandbergtv.watchpoint.pmm.job.referenceEvaluator.ParameterReferencePath;
import com.tandbergtv.watchpoint.pmm.job.referenceEvaluator.ReferenceEvaluatorChain;
import com.tandbergtv.watchpoint.pmm.job.util.JobScheduleInfoConstants;

/**
 * This is the base class for title and schedule based handlers This class gets all the required
 * info from the callback info, searches for titles matching the criteria given in the rule
 * parameter, evaluates the job parameters and creates a work order for each of the titles.
 * 
 * @author spuranik
 */
public abstract class AbstractTitleAssociatedHandler implements IJobHandler {

	private Logger logger = Logger.getLogger(this.getClass());

	private static String NDAYS_FROM_METADATA = "NDaysFromMetadata";
	private static String BEFORE = "before";
	private static String AFTER = "after";
	private static int INVALID_INDEX = -1;
	private static int NUMBER_DAYS_INDEX = 0;
	private static int TIMEFRAME_INDEX = 1;

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.tandbergtv.watchpoint.pmm.job.handler.IJobHandler#executeJob(java.util.Map)
	 */
	@SuppressWarnings("unchecked")
	public void executeJob(Map<String, Object> callbackInfo, Date jobExecutionDate) {

		String ruleName = (String) callbackInfo.get(JobScheduleInfoConstants.JOB_RULE_NAME);
		List<RuleParameter> ruleParameters = (List<RuleParameter>) callbackInfo
				.get(JobScheduleInfoConstants.JOB_RULE_PARAMETERS);

		// if there is a parameter reference in the rule, then only try to
		// get the search date. Only dates can be used in the rule parameter reference
		// Also the value for this date in the db should be of the form: 'yyyy-MM-dd'
		int index = getParameterReferenceFieldIndex(ruleName);
		if (index != INVALID_INDEX) {
			// get the path for the parameter reference field.
			// build the search query and pass it on to the search service
			RuleParameter parameterReference = ruleParameters.get(index);
			ReferenceEvaluatorChain evaluatorChain = ReferenceEvaluatorChain.getInstance();
			// evaluate any 'General' items upfront.
			JobData jobData = new JobData(jobExecutionDate);
			evaluatorChain.evaluate(jobData, callbackInfo);

			// get the property info which will be used search schedules/titles.
			ParameterReferencePath path = evaluatorChain.getPath(parameterReference.getValue());
			if (path != null) {
				Collection<?> items = search(path, callbackInfo, jobExecutionDate);
				if (items != null) {
					processItems(items, callbackInfo);
				}
			} else {
				IContainer container = CallbackHelper.getContainer(callbackInfo);
				logger.warn("Property: " + parameterReference.getValue() + " in job: "
						+ callbackInfo.get(JobScheduleInfoConstants.JOB_NAME) + " for "
						+ container.getContainerType() + " : " + container.getContainerName()
						+ " could not be resolved by any evaluator.");
			}
		}
	}

	/**
	 * Searches for titles given the criteria
	 * 
	 * @param criteria
	 *            Criteria to match when searching for titles
	 * @return list of titles that matched the criteria. These are not necessarily root titles.
	 */
	protected Collection<?> search(ParameterReferencePath path, Map<String, Object> callbackInfo,
			Date jobExecutionDate) {
		return null;
	}

	/**
	 * This method processes each of the items in the list
	 * 
	 * @param items
	 *            list of items (title/schedule) that need to be processed
	 * @param callbackInfo
	 *            the callback info recvd for executing the job
	 */
	protected void processItems(Collection<?> items, Map<String, Object> callbackInfo) {
	}

	/**
	 * Returns the date based on the rule parameters
	 * 
	 * @param ruleParameters
	 *            list of rule parameters supplied at the time of job creation
	 * @return date
	 */
	protected Date getSearchDate(String ruleTypeName, List<RuleParameter> ruleParameters,
			Date jobExecutionDate) {
		if (ruleTypeName.equalsIgnoreCase(NDAYS_FROM_METADATA)) {
			Calendar calendar = Calendar.getInstance();
			calendar.setTime(jobExecutionDate);

			// the first parameter is the number of days
			int numberOfDays = Integer.parseInt(ruleParameters.get(NUMBER_DAYS_INDEX).getValue());

			// second parameter is whether to add or subtract from current date
			String timeframe = ruleParameters.get(TIMEFRAME_INDEX).getValue();
			if (timeframe.equalsIgnoreCase(AFTER)) {
				calendar.add(Calendar.DATE, (-numberOfDays));
			} else if (timeframe.equalsIgnoreCase(BEFORE)) {
				calendar.add(Calendar.DATE, numberOfDays);
			}
			return calendar.getTime();
		}
		return null;
	}

	/**
	 * Given the rule type name, goes thru the list of parameters and returns the index of the first
	 * occurance of a parameter reference type of parameter.
	 * 
	 * @param ruleName
	 *            the ruletype name whose params need to be inspected.
	 * @return order of the first parameter reference type of parameter in the list of ruletype
	 *         params. If not found, returns -1
	 */
	private int getParameterReferenceFieldIndex(String ruleName) {
		IJobManager jobMgr = JobManager.getInstance();
		RuleType ruleType = jobMgr.getRuleType(ruleName);
		List<RuleTypeParameter> ruleTypeParams = ruleType.getParams();
		for (RuleTypeParameter ruleTypeParam : ruleTypeParams) {
			if (ruleTypeParam.getType() == RuleParameterDataType.PARAMETERREF) {
				int order = ruleTypeParam.getOrder();
				return order > 0 ? order - 1 : order;
			}
		}
		return INVALID_INDEX;
	}
}
