/*
 * Created on Jul 3, 2008 (C) Copyright TANDBERG Television Ltd.
 */

package com.tandbergtv.watchpoint.pmm.communication.handlers;

import static com.tandbergtv.watchpoint.pmm.schedule.search.ScheduleSearchKey.ACTIVE;
import static com.tandbergtv.watchpoint.pmm.schedule.search.ScheduleSearchKey.CONTEXT;
import static com.tandbergtv.watchpoint.pmm.schedule.search.ScheduleSearchKey.PITCH_DATE;
import static com.tandbergtv.watchpoint.pmm.schedule.search.ScheduleSearchKey.TITLE_LIST_TYPE;
import static com.tandbergtv.workflow.driver.search.SearchType.DATE;
import static com.tandbergtv.workflow.driver.search.SearchType.NUMERIC;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;

import com.tandbergtv.watchpoint.pmm.communication.HandlerErrorCode;
import com.tandbergtv.watchpoint.pmm.communication.MessageHandler;
import com.tandbergtv.watchpoint.pmm.communication.MessageHandlerException;
import com.tandbergtv.watchpoint.pmm.entities.Schedule;
import com.tandbergtv.watchpoint.pmm.entities.ScheduleProxy;
import com.tandbergtv.watchpoint.pmm.entities.ScheduleStatus;
import com.tandbergtv.watchpoint.pmm.entities.TitleListType;
import com.tandbergtv.watchpoint.pmm.schedule.search.IScheduleSearchService;
import com.tandbergtv.watchpoint.search.Entity;
import com.tandbergtv.workflow.core.service.ServiceRegistry;
import com.tandbergtv.workflow.driver.search.RangeParameter;
import com.tandbergtv.workflow.driver.search.ValueParameter;
import com.tandbergtv.workflow.message.IMessageKey;
import com.tandbergtv.workflow.message.IMessageUID;
import com.tandbergtv.workflow.message.WorkflowMessage;
import com.tandbergtv.workflow.message.WorkflowMessage.MessageType;
import com.tandbergtv.workflow.util.SearchCriteria;

/**
 * Gets the list of Schedule Ids for Schedules that are not in 'approved' status Generates the Pitch
 * Schedules for the given Partner / Service using the provided date range.
 * 
 * @author Vijay Silva
 */
public class GetUnapprovedSchedulesMessageHandler implements MessageHandler {

	/* The Context Id in the input message */
	private static final String CONTEXT_ID_PARAM = "contextId";

	/* The Starting Date for Pitch Schedules the input message */
	private static final String START_DATE_PARAM = "startDate";

	/* The number of Days after the Starting Date for Pitch Schedules the input message */
	private static final String DAYS_AFTER_PARAM = "daysAfter";

	/* The CSV list of schedule IDs with status that is not approved in the output message */
	private static final String SCHEDULE_IDS_PARAM = "scheduleIds";

	/* The Date Format for the date present in the message */
	private static final String DATE_FORMAT = "yyyy-MM-dd";

	/* The Date Format for the date to use in the Search Criteria */
	private static final String SEARCH_DATE_FORMAT = "yyyy-MM-dd";

	/**
	 * Gets the list of Pitch Schedules for the given Context Id and date range. Writes the
	 * schedules to the specified output file path.
	 * 
	 * @see com.tandbergtv.watchpoint.pmm.communication.MessageHandler#handleMessage(com.tandbergtv.workflow.message.WorkflowMessage)
	 */
	public WorkflowMessage handleMessage(WorkflowMessage message) throws MessageHandlerException {
		long contextId = this.getLongParameter(message, CONTEXT_ID_PARAM);
		Date startDate = this.getDateParameter(message, START_DATE_PARAM);
		int daysAfter = (int) this.getLongParameter(message, DAYS_AFTER_PARAM);

		/* Ensure that Days After is >= 0 */
		if (daysAfter < 0) {
			String msg = "The " + DAYS_AFTER_PARAM + " message parameter must have "
					+ "a number value greater than or equal to 0, got value: " + daysAfter;
			throw new MessageHandlerException(msg);
		}

		/* Calculate the End Date in the Date Range */
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(startDate);
		calendar.add(Calendar.DAY_OF_MONTH, daysAfter);
		Date endDate = calendar.getTime();

		/* Get the Service required and get the schedules */
		ServiceRegistry registry = ServiceRegistry.getDefault();
		IScheduleSearchService service = registry.lookup(IScheduleSearchService.class);
		SearchCriteria criteria = this.buildSearchCriteria(contextId, startDate, endDate);
		Collection<? extends Schedule> schedules = service.search(criteria);

		/* Get the CSV list of schedule Ids that are not approved. */
		String scheduleIds = getUnapprovedScheduleIds(schedules);

		/* Build the Workflow Message Response */
		IMessageUID uid = message.getMessageUID();
		IMessageKey key = message.getKey();
		WorkflowMessage response = new WorkflowMessage(uid, key, MessageType.ack);
		response.putValue(SCHEDULE_IDS_PARAM, scheduleIds);

		return response;
	}

	private SearchCriteria buildSearchCriteria(long contextId, Date startDate, Date endDate) {
		SearchCriteria criteria = new SearchCriteria();

		/* Perform a Search on the Schedule Proxy Object (to limit data fetched) */
		Entity e = new Entity("list", ScheduleProxy.class, "s");

		/* Add the 'active' flag and the 'contextId' parameters */
		e.addParameter(new ValueParameter(ACTIVE.toString(), NUMERIC, 1));
		e.addParameter(new ValueParameter(CONTEXT.toString(), NUMERIC, contextId));

		/* Add the Pitch Date range */
		DateFormat format = new SimpleDateFormat(SEARCH_DATE_FORMAT);
		RangeParameter range = new RangeParameter(PITCH_DATE.toString(), DATE);
		range.setFrom(format.format(startDate));
		/* Deducting a day from endDate for the limitation in RangeParameter::setTo method */
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(endDate);
		calendar.add(Calendar.DAY_OF_MONTH, -1);
		Date modifiedEndDate = calendar.getTime();
		range.setTo(format.format(modifiedEndDate));
		e.addParameter(range);

		/* Only Search for Pitch Schedules */
		int pitchType = TitleListType.PITCH.ordinal();
		e.addParameter(new ValueParameter(TITLE_LIST_TYPE.toString(), NUMERIC, pitchType));

		criteria.addParameter(e);

		return criteria;
	}

	/* Write the List of schedules in the collection to an XML file */
	private String getUnapprovedScheduleIds(Collection<? extends Schedule> schedules)
			throws MessageHandlerException {
		StringBuilder result = new StringBuilder();

		/* Make sure that the collection is not null */
		if (schedules == null) {
			schedules = new ArrayList<Schedule>();
		}

		/* Iterate through each Schedule and add to XML Document */
		for (Schedule schedule : schedules) {
			if (schedule.getStatus() != ScheduleStatus.APPROVED) {
				if (result.length() != 0)
					result.append(", ");
				result.append(schedule.getId());
			}
		}

		return result.toString();
	}

	/* Get a Long Parameter from the Workflow Message */
	private long getLongParameter(WorkflowMessage message, String name)
			throws MessageHandlerException {
		long result = 0;

		String value = (message.getPayload() != null) ? message.getValue(name) : null;
		if (value == null || value.trim().length() == 0) {
			throw new MessageHandlerException(HandlerErrorCode.INVALID_INPUT, "The " + name
					+ " message parameter is blank / missing when a number value was expected.");
		}

		try {
			result = Long.parseLong(value);
		} catch (Exception e) {
			throw new MessageHandlerException(HandlerErrorCode.INVALID_INPUT, "The " + name
					+ " message parameter does not have a valid number value, received value: "
					+ value, e);
		}

		return result;
	}

	/* Get a Date Parameter from the Workflow Message */
	private Date getDateParameter(WorkflowMessage message, String name)
			throws MessageHandlerException {
		Date result = null;

		String value = (message.getPayload() != null) ? message.getValue(name) : null;
		if (value == null || value.trim().length() == 0) {
			throw new MessageHandlerException(HandlerErrorCode.INVALID_INPUT, "The " + name
					+ " message parameter is blank / missing when a date value was expected.");
		}

		try {
			DateFormat format = new SimpleDateFormat(DATE_FORMAT);
			result = format.parse(value);
		} catch (Exception e) {
			throw new MessageHandlerException(HandlerErrorCode.INVALID_INPUT, "The " + name
					+ " message parameter does not have a valid date value. Expected format: "
					+ DATE_FORMAT + ", received value: " + value, e);
		}

		return result;
	}
}
