/**
 * PartnerReferenceEvaluator.java
 * Created on Jun 3, 2008
 * (C) Copyright TANDBERG Television Ltd.
 */
package com.tandbergtv.watchpoint.pmm.job.referenceEvaluator;

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.Context;
import com.tandbergtv.watchpoint.pmm.entities.IContainer;
import com.tandbergtv.watchpoint.pmm.entities.JobParameter;
import com.tandbergtv.watchpoint.pmm.entities.Partner;
import com.tandbergtv.watchpoint.pmm.job.callback.CallbackHelper;
import com.tandbergtv.watchpoint.pmm.job.util.JobScheduleInfoConstants;
import com.tandbergtv.watchpoint.pmm.job.util.ParameterReferenceHelper;
import com.tandbergtv.watchpoint.pmm.util.ContextManager;
import com.tandbergtv.workflow.core.service.ServiceRegistry;
import com.tandbergtv.workflow.core.service.cache.ICacheService;

/**
 * This class evaluates all partner parameter references
 * 
 * @author spuranik
 */
public class PartnerReferenceEvaluator extends ReferenceEvaluator {

	private Logger logger = Logger.getLogger(PartnerReferenceEvaluator.class);
	private static String PARTNER_PARAMETER_REFERENCE_GROUP = "Partner";
	private static String CONTAINER_CACHE_SERVICE_NAME = "Container Cache";

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.tandbergtv.watchpoint.pmm.job.referenceEvaluator.ReferenceEvaluator#evaluate(java.lang.Object,
	 *      java.util.Map)
	 */
	@Override
	@SuppressWarnings("unchecked")
	public void evaluate(Object entity, Map<String, Object> info) {
		logger.debug("Evaluating Partner parameter references");

		List<JobParameter> jobParameters = (List<JobParameter>) info
				.get(JobScheduleInfoConstants.JOB_PARAMETERS);

		// If the job params have Partner references then only try to resolve it.
		if (parametersHavePartnerReference(jobParameters)) {
			// the info object contains the contextId. Get the container from
			// the cache.
			Long contextId = ((Long) info.get(JobScheduleInfoConstants.CONTEXTID));
			ICacheService<IContainer> containerCache = (ICacheService<IContainer>) ServiceRegistry
					.getDefault().lookup(CONTAINER_CACHE_SERVICE_NAME);
			IContainer container = (IContainer) containerCache.get(Long.valueOf(contextId));
			
			// If the cache was not yet populated, the container will not be found. 
			// So get it directly from db. Does not really justify the cache's existence!
			if (container == null) {
				Context ctxt = ContextManager.getInstance().getContext(contextId);
				if (ctxt != null) {
					container = ctxt.getContainer();
				}
			}					

			// if the container is still not found, throw an exception
			if (container == null) {
				logger.warn("No container found for contextId: " + contextId);
			}
			else {
				// container must be a partner!
				if (container.getContainerType().equals(ContainerType.PARTNER)) {
					substituteValues(info, jobParameters, (Partner) container);
				} else {
					// Don't throw an exception. The next evaluator should be
					// called.
					logger
							.error("Context: "
									+ contextId
									+ " points to a service and parameter(s) have a reference to a partner parameter reference");
				}
			}
		}

		// no matter what call the next evaluator to resolve other parameter refs.
		if (successor != null) {
			successor.evaluate(entity, info);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.tandbergtv.watchpoint.pmm.job.referenceEvaluator.ReferenceEvaluator#getPath(java.lang.String)
	 */
	@Override
	public ParameterReferencePath getPath(String property) {

		// partner does not expect any rule parameters for which the path needs to be evaluated
		// hence just forwarding the request to the next one if any else returns null.
		if (this.successor != null) {
			return successor.getPath(property);
		}

		return null;
	}

	/**
	 * Checks if even one of the job parameters is a partner reference parameter. This is done to
	 * save the context load (which could be an expensive operation) if there are no partner
	 * parameter reference
	 * 
	 * @param jobParameters
	 *            list of params to inspect for partner parameter reference
	 * @return true/false based on whether a partner parameter reference was found
	 */
	private boolean parametersHavePartnerReference(List<JobParameter> jobParameters) {
		for (JobParameter parameter : jobParameters) {
			String propertyValue = parameter.getValue();
			if (ParameterReferenceHelper.isParameterReference(propertyValue)) {
				String groupName = ParameterReferenceHelper.getGroupName(propertyValue);
				if (groupName.equalsIgnoreCase(PARTNER_PARAMETER_REFERENCE_GROUP))
					return true;
			}
		}
		return false;
	}

	/**
	 * this method goes thru the job parameter list and looks for parameter references for the
	 * partner and substitutes them with the appropriate values from the partner object passed along
	 * 
	 * @param jobParameters
	 *            list of job params to be evaluated
	 * @param container
	 *            the partner whose values need to be substituted in the list
	 */
	private void substituteValues(Map<String, Object> info, List<JobParameter> jobParameters,
			Partner partner) {

		for (JobParameter parameter : jobParameters) {
			try {
				String parameterReference = parameter.getValue();
				if (ParameterReferenceHelper.isParameterReference(parameterReference)) {
					String groupName = ParameterReferenceHelper.getGroupName(parameterReference);
					if (groupName.equalsIgnoreCase(PARTNER_PARAMETER_REFERENCE_GROUP)) {
						// break up the string to get the property name
						String property = ParameterReferenceHelper
								.getPropertyName(parameterReference);
						parameter.setValue(partner.getValue(property));
					}
				}
			} catch (RuntimeException e) {
				IContainer container = CallbackHelper.getContainer(info);
				String errorMsg = "Error while subsituting value for: " + parameter.getValue()
						+ " for job: " + info.get(JobScheduleInfoConstants.JOB_NAME) + " ["
						+ container.getContainerType() + ":" + container.getContainerName() + "]";
				logger.warn(errorMsg, e);
			}
		}
	}
}
