/**
 * ScheduleReferenceEvaluator.java
 * Created on Jun 4, 2008
 * (C) Copyright TANDBERG Television Ltd.
 */
package com.tandbergtv.watchpoint.pmm.job.referenceEvaluator;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import com.tandbergtv.watchpoint.pmm.entities.DistributionSchedule;
import com.tandbergtv.watchpoint.pmm.entities.IContainer;
import com.tandbergtv.watchpoint.pmm.entities.JobParameter;
import com.tandbergtv.watchpoint.pmm.entities.Planner;
import com.tandbergtv.watchpoint.pmm.entities.Schedule;
import com.tandbergtv.watchpoint.pmm.job.callback.CallbackHelper;
import com.tandbergtv.watchpoint.pmm.job.util.JobScheduleInfoConstants;
import com.tandbergtv.watchpoint.pmm.job.util.ParameterReferenceHelper;

/**
 * This class resolves all parameter references for a schedule and planner. Currently only Id and
 * PitchDate are available as parameter references. Note: Currently the same class is used to
 * evaluate planner and schedule properties since they are really the same other than the section
 * name that is used for comparison. If in the future any of the sections have additional or many
 * different proeprties, split the planner and schedule evaluator.
 * 
 * @author spuranik
 */
public class ScheduleReferenceEvaluator extends ReferenceEvaluator {

	private Logger logger = Logger.getLogger(ScheduleReferenceEvaluator.class);
	private static String SCHEDULE_PARAMETER_REFERENCE_GROUP = "Schedule";
	private static String PLANNER_PARAMETER_REFERENCE_GROUP = "Planner";
	private static String SCHEDULE_ID = "Id";
	private static String SCHEDULE_PITCHDATE = "Date";
	private static String PLANNER_DATE = "Date";
	private static String SCHEDULE_PITCHDATE_FIELD = "pitchDate";
	private static String PLANNER_DATE_FIELD = "plannerDate";
	private static String SCHEDULE_DATE_FORMAT = "yyyy-MM-dd";

	/*
	 * (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 Schedule parameter references");

		// check if the recvd entity is a schedule (could be Distribution or Planner)
		if (entity != null
				&& (entity.getClass().equals(DistributionSchedule.class) || entity.getClass()
						.equals(Planner.class))) {
			Schedule schedule = (Schedule) entity;

			List<JobParameter> paramters = (List<JobParameter>) info
					.get(JobScheduleInfoConstants.JOB_PARAMETERS);
			for (JobParameter parameter : paramters) {
				try {
					String parameterReference = parameter.getValue();
					if (ParameterReferenceHelper.isParameterReference(parameterReference)) {
						String groupName = ParameterReferenceHelper
								.getGroupName(parameterReference);
						String propertyName = ParameterReferenceHelper
								.getPropertyName(parameterReference);
						// the group name could be a combination of 2 level groups, hence cannot
						// check for schedule directly
						if (groupName.startsWith(SCHEDULE_PARAMETER_REFERENCE_GROUP)) {
							// ensure that the property requested is for a pitch schedule only
							if (schedule.getClass() == DistributionSchedule.class) {
								substituteScheduleValues((DistributionSchedule) schedule,
										parameter, propertyName);
							} else {
								logger
										.warn("Property: "
												+ parameterReference
												+ " can be evaluated only for (Pitch) Schedules, not for Planners.");
							}
						} else if (groupName.startsWith(PLANNER_PARAMETER_REFERENCE_GROUP)) {
							// ensure that the property requested is for a planner only
							if (schedule.getClass() == Planner.class) {
								substitutePlannerValues((Planner) schedule, parameter, propertyName);
							} else {
								logger
										.warn("Property: "
												+ parameterReference
												+ " can be evaluated only for Planners, not for (Pitch) Schedules.");
							}
						}
					}
				} catch (RuntimeException e) {
					// if there was a problem resolving one of the parameters, log it and
					// continue with the rest.
					IContainer container = CallbackHelper.getContainer(info);
					String errorMsg = "Job execution failed for job: "
							+ info.get(JobScheduleInfoConstants.JOB_NAME) + " ["
							+ container.getContainerType() + ":" + container.getContainerName()
							+ "]" + " Error while trying to evaluate schedule reference";
					logger.warn(errorMsg, e);
				}
			}
		}
		// always call the next evaluator even if there was an error in this evaluator.
		if (successor != null) {
			successor.evaluate(entity, info);
		}
	}

	/**
	 * Substitutes the value of the property using the schedule object provided.
	 * 
	 * @param schedule
	 * @param parameterReference
	 */
	private void substituteScheduleValues(DistributionSchedule schedule, JobParameter parameter,
			String propertyName) {
		// id and date properties are substituted for pitch schedules
		if (propertyName.equalsIgnoreCase(SCHEDULE_ID)) {
			parameter.setValue(Long.valueOf(schedule.getId()).toString());
		} else if (propertyName.equalsIgnoreCase(SCHEDULE_PITCHDATE)) {
			String pitchDate = formatDate(schedule.getDate(), SCHEDULE_DATE_FORMAT);
			parameter.setValue(pitchDate);
		} else {
			throw new RuntimeException("Property:" + propertyName
					+ " not avaiable for substitution.");
		}
	}

	/**
	 * Substitutes the value of the property using the planner object provided.
	 * 
	 * @param planner
	 * @param parameter
	 * @param parameterReference
	 */
	private void substitutePlannerValues(Planner planner, JobParameter parameter,
			String propertyName) {
		// id and date properties are substituted for planners
		if (propertyName.equalsIgnoreCase(SCHEDULE_ID)) {
			parameter.setValue(Long.valueOf(planner.getId()).toString());
		} else if (propertyName.equalsIgnoreCase(PLANNER_DATE)) {
			String plannerDate = formatDate(planner.getDate(), SCHEDULE_DATE_FORMAT);
			parameter.setValue(plannerDate);
		} else {
			throw new RuntimeException("Property:" + propertyName
					+ " not avaiable for substitution.");
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.tandbergtv.watchpoint.pmm.job.referenceEvaluator.ReferenceEvaluator#getPath(java.lang.String)
	 */
	@Override
	public ParameterReferencePath getPath(String property) {

		// first make sure that the property is a parameter reference
		// then does it refer to a schedule parameter reference
		// if yes, get the path for this property
		// else forward the request to the next one in the chain if any or else return null
		try {
			if (ParameterReferenceHelper.isParameterReference(property)) {
				String groupName = ParameterReferenceHelper.getGroupName(property);
				String propertyName = ParameterReferenceHelper.getPropertyName(property);
				ParameterReferencePath path = new ParameterReferencePath();
				path.setParameterReference(property);
				// schedule does not have metadata properties
				path.setMetadata(false);
				// name is not required for schedules
				path.setName("");

				// set the right property field identifier for the schedule/planner handler
				if (groupName.startsWith(SCHEDULE_PARAMETER_REFERENCE_GROUP)) {
					if (propertyName.equalsIgnoreCase(SCHEDULE_PITCHDATE)) {
						path.setPropertyName(SCHEDULE_PITCHDATE_FIELD);
					}
					return path;
				}
				if (groupName.startsWith(PLANNER_PARAMETER_REFERENCE_GROUP)) {
					if (propertyName.equalsIgnoreCase(PLANNER_DATE)) {
						path.setPropertyName(PLANNER_DATE_FIELD);
					}
					return path;
				}
			}
		} catch (Exception e) {
			throw new RuntimeException("Error while getting path for property: " + property, e);
		}

		if (successor != null) {
			return successor.getPath(property);
		}
		return null;
	}

	/**
	 * converts the given date to the destination format
	 * 
	 * @param dateToFormat
	 *            date to be converted
	 * @return string representation of the converted date.
	 */
	private String formatDate(Date dateToFormat, String toFormat) {
		if (dateToFormat == null) {
			throw new RuntimeException("Date is null.");
		}

		SimpleDateFormat from = new SimpleDateFormat();
		Calendar c = Calendar.getInstance();
		c.setTime(dateToFormat);
		from.setCalendar(c);
		SimpleDateFormat to = new SimpleDateFormat(toFormat);
		return to.format(dateToFormat);
	}

}
