/**
 * JobFormPopulator.java
 * Created on May 31, 2008
 * (C) Copyright TANDBERG Television Ltd.
 */
package com.tandbergtv.watchpoint.pmm.web.util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import org.apache.log4j.Logger;

import com.tandbergtv.watchpoint.pmm.entities.EntityType;
import com.tandbergtv.watchpoint.pmm.entities.RuleParameterDataType;
import com.tandbergtv.watchpoint.pmm.entities.RuleType;
import com.tandbergtv.watchpoint.pmm.job.IJobManager;
import com.tandbergtv.watchpoint.pmm.job.JobManager;
import com.tandbergtv.watchpoint.pmm.job.conf.ParameterReferenceFileParser;
import com.tandbergtv.watchpoint.pmm.job.conf.ParameterReferenceGroup;
import com.tandbergtv.watchpoint.pmm.job.conf.ParameterReferenceItem;
import com.tandbergtv.watchpoint.pmm.job.util.ParameterReferenceHelper;
import com.tandbergtv.watchpoint.pmm.web.formbeans.job.JobForm;
import com.tandbergtv.watchpoint.pmm.web.formbeans.job.JobParameterForm;
import com.tandbergtv.watchpoint.pmm.web.formbeans.job.OptionItemForm;
import com.tandbergtv.watchpoint.pmm.web.formbeans.job.OptionsGroupForm;
import com.tandbergtv.watchpoint.pmm.web.formbeans.job.RuleParameterForm;
import com.tandbergtv.workflow.core.TaskVariable;
import com.tandbergtv.workflow.core.WorkflowTemplate;
import com.tandbergtv.workflow.core.service.ServiceRegistry;
import com.tandbergtv.workflow.driver.service.ITemplateLoaderService;

/**
 * @author spuranik
 * 
 */
public class JobFormPopulator {

	private static Logger logger = Logger.getLogger(JobFormPopulator.class);
	private static String PARAMETER_COUNT_DELIMETER = ",";
	private static String PARAMETER_NAME_COUNT_DELIMETER = ";";
	private static int PARAMETER_NAME_INDEX = 0;
	private static int PARAMETER_COUNT_INDEX = 1;

	/**
	 * this method populates all the required items in the jobform so that it can be painted back.
	 * 
	 * @param jobform
	 */
	public static void setRequiredInfoInForm(JobForm jobform) {
		// set schedule rule with titles associated
		IJobManager jobMgr = JobManager.getInstance();
		List<RuleType> rules = jobMgr.getRuleTypes(Boolean.valueOf(jobform
				.getIsAssociatedWithTitles()));
		jobform.setScheduleRules(rules);
		logger.debug("Schedule rule list count: " + jobform.getScheduleRules().size());
		StringBuilder parameterCount = new StringBuilder();
		for (int i = 0; i < rules.size(); i++) {
			if (parameterCount.length() > 0)
				parameterCount.append(PARAMETER_COUNT_DELIMETER);
			parameterCount.append(rules.get(i).getName() + PARAMETER_NAME_COUNT_DELIMETER
					+ rules.get(i).getParams().size());
		}
		jobform.setRuleTypeParameterCount(parameterCount.toString());

		// set template name list
		jobform.setTemplateNames(getTemplateNames());
		logger.debug("Template list count: " + jobform.getTemplateNames().size());

		// set the rule and parameter menu options
		ParameterReferenceFileParser parser = ParameterReferenceFileParser.getInstance();
		EntityType type = EntityType.toType(jobform.getEntityType());
		List<ParameterReferenceGroup> parameterGroups = parser.getParameters(type, Boolean
				.valueOf(jobform.getIsAssociatedWithTitles()));
		List<ParameterReferenceGroup> ruleGroups = parser.getRules(type);
		List<OptionsGroupForm> parameterForms = prepareOptionForms(parameterGroups);
		List<OptionsGroupForm> ruleForms = prepareOptionForms(ruleGroups);
		jobform.setRuleOptions(ruleForms);
		jobform.setParameterOptions(parameterForms);

		// create job form params and return it back
		if (jobform.getSelectedTemplate() != null && jobform.getSelectedTemplate().length() > 0) {
			Collection<TaskVariable> variables = getTemplateParameters(jobform
					.getSelectedTemplate());
			List<JobParameterForm> jobParameters = prepareJobParameterForm(variables, jobform
					.getValue());
			jobform.setParams(jobParameters);
		}

		// for each of the rule type create the rule parameter placeholder upfront
		for (RuleType ruleType : rules) {
			if (ruleType.getName().equalsIgnoreCase(jobform.getSelectedRuleType())) {
				List<RuleParameterForm> ruleFormList = new ArrayList<RuleParameterForm>();
				String[] selectedRuleParams = getRuleParamsForSelectedRule(jobform);
				for (int i = 0; i < selectedRuleParams.length; i++) {
					RuleParameterForm ruleParameterForm = new RuleParameterForm();
					ruleParameterForm.setRuleParamValue(selectedRuleParams[i]);
					// order is 1 based.
					ruleParameterForm.setOrder(i + 1);
					ruleFormList.add(ruleParameterForm);
				}
				jobform.addRuleParam(jobform.getSelectedRuleType(), ruleFormList);
			} else {
				jobform.addRuleParam(ruleType.getName(), createDummyRuleParameters(ruleType));
			}
		}
	}

	/**
	 * this method picks out the rule parameters for the selcted rule from ruleparamvalues.
	 * ruleparamvalues contains values of not selected rules as well. hence got to filter out.
	 * 
	 * @param jobform
	 * @return
	 */
	public static String[] getRuleParamsForSelectedRule(JobForm jobform) {
		String selectedRuleType = jobform.getSelectedRuleType();
		String[] rules = jobform.getRuleTypeParameterCount().split(PARAMETER_COUNT_DELIMETER);
		int offset = 0;
		int numberOfParams = 0;
		for (int i = 0; i < rules.length; i++) {
			String[] nameCounts = rules[i].split(PARAMETER_NAME_COUNT_DELIMETER);
			if (!nameCounts[PARAMETER_NAME_INDEX].equalsIgnoreCase(selectedRuleType))
				offset += Integer.parseInt(nameCounts[PARAMETER_COUNT_INDEX]);
			else {
				numberOfParams = Integer.parseInt(nameCounts[PARAMETER_COUNT_INDEX]);
				break;
			}
		}
		String[] selectedRuleParams = new String[numberOfParams];
		for (int i = offset, j = 0; i < offset + numberOfParams; i++, j++) {
			selectedRuleParams[j] = jobform.getRuleParamValue()[i];
		}
		return selectedRuleParams;
	}

	/**
	 * @return list of latest template names (without the version)
	 */
	private static List<String> getTemplateNames() {
		List<String> templateNames = new ArrayList<String>();

		try {
			ServiceRegistry registry = ServiceRegistry.getDefault();
			ITemplateLoaderService templateLoader = registry.lookup(ITemplateLoaderService.class);
			List<WorkflowTemplate> templates = templateLoader.getLatestTemplates();
			for (WorkflowTemplate t : templates) {
				templateNames.add(t.getName());
			}
		} catch (Exception e) {
			logger.error("Error while getting template names: " + e.getMessage());
		}

		return templateNames;
	}

	/**
	 * @param options
	 *            options that need to be converted into forms for ui
	 * @return list of groups representing
	 */
	public static List<OptionsGroupForm> prepareOptionForms(List<ParameterReferenceGroup> options) {
		List<OptionsGroupForm> groups = new ArrayList<OptionsGroupForm>();
		for (ParameterReferenceGroup option : options) {
			List<OptionsGroupForm> groupForm = createGroupForm(option, null);
			groups.addAll(groupForm);
		}
		return groups;
	}

	/**
	 * @param templateName
	 *            template name (without the version) for which parameters are requested
	 * @return sorted list of parameters for a given template. All required variables appear before
	 *         the optional ones.
	 */
	public static Collection<TaskVariable> getTemplateParameters(String templateName) {
		Collection<TaskVariable> params = new ArrayList<TaskVariable>();

		ServiceRegistry registry = ServiceRegistry.getDefault();
		ITemplateLoaderService templateLoader = registry.lookup(ITemplateLoaderService.class);
		List<WorkflowTemplate> templates = templateLoader.getLatestTemplates();
		for (WorkflowTemplate t : templates) {
			if (t.getName().equals(templateName)) {
				params = t.getStartTaskVariables();
				params = sort(params);
				break;
			}
		}
		return params;
	}

	/**
	 * @param params
	 *            list of task variables that will be sorted
	 * @return sorted list of params. All required fields appear before the optional ones.
	 */
	private static Collection<TaskVariable> sort(Collection<TaskVariable> params) {
		List<TaskVariable> sortedParams = new ArrayList<TaskVariable>();
		List<TaskVariable> optionalParams = new ArrayList<TaskVariable>();

		for (TaskVariable tv : params) {
			if (tv.getAccess().isRequired()) {
				sortedParams.add(tv);
			} else {
				optionalParams.add(tv);
			}
		}
		sortedParams.addAll(optionalParams);
		return sortedParams;
	}

	/**
	 * @param variables
	 *            list of all variables for the selected template
	 * @return a list of forms corresponding to each variable in the list provided
	 */
	public static List<JobParameterForm> prepareJobParameterForm(
			Collection<TaskVariable> variables, String[] values) {
		List<JobParameterForm> formList = new ArrayList<JobParameterForm>();
		TaskVariable taskVariables[] = new TaskVariable[variables.size()];
		taskVariables = variables.toArray(taskVariables);
		for (int i = 0; i < taskVariables.length; i++) {
			TaskVariable tv = taskVariables[i];
			JobParameterForm jobParameterForm = new JobParameterForm();
			jobParameterForm.setName(tv.getMappedName());
			jobParameterForm.setRequired(tv.getAccess().isRequired());
			jobParameterForm.setType(tv.getDatatype().toString());

			// set the value back in the list for the jsp to render it
			if (values != null) {
				jobParameterForm.setValue(values[i]);
			}

			formList.add(jobParameterForm);
		}
		return formList;
	}

	/**
	 * @param ruleType
	 *            rule type for which rule params are created
	 * @return list of ruleparam forms
	 */
	private static ArrayList<RuleParameterForm> createDummyRuleParameters(RuleType ruleType) {
		ArrayList<RuleParameterForm> ruleParams = new ArrayList<RuleParameterForm>();
		for (int i = 0; i < ruleType.getParams().size(); i++) {
			RuleParameterForm form = new RuleParameterForm();
			// default value for time parameter
			if (ruleType.getParams().get(i).getType() == RuleParameterDataType.TIME)
				form.setRuleParamValue("12:00 am");
			ruleParams.add(form);
		}
		return ruleParams;
	}

	/**
	 * @param group
	 *            group which needs to be converted to forms
	 * @return list of group forms representing the given group. Each item which is a group is
	 *         treated as a different group. The name for this child is prefixed with the parent's
	 *         name.
	 */
	private static List<OptionsGroupForm> createGroupForm(ParameterReferenceGroup group,
			OptionsGroupForm parent) {
		List<OptionsGroupForm> groupFormList = new ArrayList<OptionsGroupForm>();

		// set the name, parent and group indication flag for this group
		OptionsGroupForm groupForm = new OptionsGroupForm();
		String groupName = "";
		if (parent != null) {
			groupName = ParameterReferenceHelper.combineGroupNames(parent.getName(), group
					.getName());
		} else {
			groupName = group.getName();
		}

		groupForm.setName(groupName);
		groupForm.setParent(parent);
		groupForm.setIsItemGroup(true);

		List<ParameterReferenceItem> items = group.getItems();
		JobParameterComparator comparator = new JobParameterComparator();
		Collections.sort(items, comparator);
		for (ParameterReferenceItem item : items) {
			// if the child item is a group build a group and add it separately to the list
			if (item.getClass().equals(ParameterReferenceGroup.class)) {
				groupFormList.addAll(createGroupForm((ParameterReferenceGroup) item, groupForm));
			} else {
				// build a single item
				groupForm.addItem(createItemForm(item, groupForm));
			}
		}
		// if the form created has any items add it to the list of forms to be returned
		// this is done because the items could be groups which end up as separate
		// groups leaving the parent group empty which should not be added.
		if (groupForm.getItems() != null && groupForm.getItems().size() > 0)
			groupFormList.add(groupForm);

		return groupFormList;
	}

	/**
	 * @param item
	 *            item to be converted to a form
	 * @param parentForm
	 *            the parent form for this itemform
	 * @return item form corresponding to this item
	 */
	private static OptionItemForm createItemForm(ParameterReferenceItem item,
			OptionsGroupForm parentForm) {
		OptionItemForm itemForm = new OptionItemForm();
		itemForm.setName(item.getName());
		// itemForm.setValue(item.getValue());
		// once the item is selected it will show up as $[parent name].[item name]
		String refString = ParameterReferenceHelper.buildParameterReferenceProperty(parentForm
				.getName(), item.getName());
		itemForm.setValue(refString);
		itemForm.setParent(parentForm);
		itemForm.setIsItemGroup(false);

		return itemForm;
	}

}
