/**
 * ReferenceEvaluatorChain.java
 * Created on Jun 3, 2008
 * (C) Copyright TANDBERG Television Ltd.
 */
package com.tandbergtv.watchpoint.pmm.job.referenceEvaluator;

import java.util.Map;

import org.apache.log4j.Logger;

import com.tandbergtv.watchpoint.pmm.title.ITitleService;
import com.tandbergtv.watchpoint.pmm.title.conf.ISpecificationManager;
import com.tandbergtv.workflow.core.service.ServiceRegistry;

/**
 * This class is used to resolve parameter references in both the rule and job parameters
 * when a job is executed.
 * 
 * @author spuranik 
 */
public class ReferenceEvaluatorChain {

	private Logger logger = Logger.getLogger(PartnerReferenceEvaluator.class);
	public static ReferenceEvaluatorChain instance;

	// points to the start of the evaluator chain
	private ReferenceEvaluator chainRoot;

	private ReferenceEvaluatorChain() {
		buildEvaluatorChain();
	}

	public static synchronized ReferenceEvaluatorChain getInstance() {
		if (instance == null) {
			instance = new ReferenceEvaluatorChain();
		}
		return instance;
	}

	/**
	 * @return the chainRoot
	 */
	public ReferenceEvaluator getChainRoot() {
		return chainRoot;
	}

	/**
	 * @param chainRoot the chainRoot to set
	 */
	public void setChainRoot(ReferenceEvaluator chainRoot) {
		this.chainRoot = chainRoot;
	}

	/**
	 * This method constructs the chain of evaluators.
	 * currently which evaluators will be added to the chain
	 * are fixed in this method.
	 */
	private void buildEvaluatorChain() {
		// build the complete chain and set the root
		ReferenceEvaluator partnerEvaluator = new PartnerReferenceEvaluator();

		ITitleService titleService = ServiceRegistry.getDefault().lookup(ITitleService.class);		
		ISpecificationManager specManager = ServiceRegistry.getDefault().lookup(ISpecificationManager.class);
		ReferenceEvaluator titleEvaluator = new TitleReferenceEvaluator(titleService, specManager);
		partnerEvaluator.setSuccessor(titleEvaluator);

		ReferenceEvaluator scheduleEvaluator = new ScheduleReferenceEvaluator();
		titleEvaluator.setSuccessor(scheduleEvaluator);
		
		ServiceReferenceEvaluator serviceEvaluator = new ServiceReferenceEvaluator();
		scheduleEvaluator.setSuccessor(serviceEvaluator);

		GeneralReferenceEvaluator generalEvaluator = new GeneralReferenceEvaluator();
		serviceEvaluator.setSuccessor(generalEvaluator);

		setChainRoot(partnerEvaluator);
	}

	/**
	 * This method is called to build the search criteria based
	 * on the parameter reference property
	 * 
	 * @param property
	 * 	the property for which the path needs to be evaluated. 
	 * @return
	 * 	the path in the object corresponding to this property.
	 */
	public ParameterReferencePath getPath(String property) {

		return chainRoot.getPath(property);
	}

	/**
	 * This method evaluates all property references using the given object
	 * by passing it along to all the evaluators in the chain.
	 * Each evaluator substitutes the values it is responsible for
	 * and passes the entity and info to the next one in the chain for evaluation.
	 *  
	 * @param entity
	 * 	Entity which will be used to substitute values for 
	 *  the parameter references. Currently schedule and title
	 *  entities are used.
	 *   
	 * @param info
	 * 	contains the list which has the references and any other
	 * 	info that might be required to get the values.
	 */
	public void evaluate(Object entity, Map<String, Object> info) {
		
		// start the evaluation process
		try {
			chainRoot.evaluate(entity, info);
		} catch (RuntimeException e) {
			// if one evaluator throws an exception, at least continue with the rest. 
			logger.error("Error while evaluating parameter reference.", e);
		}

		logger.debug("Done evaluating parameter references");
	}
}
