/*
 * Created on Jul 28, 2008 (C) Copyright TANDBERG Television Ltd.
 */

package com.tandbergtv.watchpoint.pmm.web.title.search;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import com.tandbergtv.watchpoint.pmm.title.conf.ISpecificationManager;
import com.tandbergtv.watchpoint.pmm.title.conf.Specification;
import com.tandbergtv.watchpoint.pmm.title.conf.Variable;
import com.tandbergtv.watchpoint.pmm.title.provider.ITitleProvider;
import com.tandbergtv.watchpoint.pmm.title.provider.ITitleProviderInstance;
import com.tandbergtv.watchpoint.pmm.title.provider.ITitleProviderRegistry;
import com.tandbergtv.watchpoint.pmm.title.search.TitleSearchKey;
import com.tandbergtv.workflow.web.page.Field;
import com.tandbergtv.workflow.web.page.Page;
import com.tandbergtv.workflow.web.page.PageConfig;
import com.tandbergtv.watchpoint.pmm.web.title.TitleSearchType;
import com.tandbergtv.workflow.core.Datatype;
import com.tandbergtv.workflow.core.service.ServiceRegistry;

/**
 * Builds the Search Page for a Title Search.
 * 
 * @author Vijay Silva
 */
public class TitleSearchPageBuilder {

	/* The Internal Title Search Page */
	private static final String INTERNAL_SEARCH_PAGE = "titlesearch";

	/* The External Title Search Page */
	private static final String EXTERNAL_SEARCH_PAGE = "titlesearch.external";

	/* Internal Constructor */
	private TitleSearchPageBuilder() {
	}
	
	/**
	 * Create an instance of the Title Search Page Builder
	 * 
	 * @return A Title Search Page builder
	 */
	public static TitleSearchPageBuilder newInstance() {
		return new TitleSearchPageBuilder();
	}

	/**
	 * Builds a Title Search Page given the specification name and the search type.
	 * 
	 * @param searchType The Title Search Type (internal/external)
	 * @param specificationName The Specification Name
	 * @return The Search Page for the given specification and the search type
	 * @throws Exception Exception building the search page
	 */
	public Page buildSearchPage(TitleSearchType searchType, String specificationName)
			throws Exception {
		/* Build the basic search page */
		Page searchPage = this.buildDefaultSearchPage(searchType);

		/* Check if specification with name exists */
		Specification specification = this.getSpecification(specificationName);
		if (specification == null)
			return searchPage;

		/* Get the specification 'variables' that are involved in the search */
		List<Variable> searchVariables = null;
		if (searchType == TitleSearchType.EXTERNAL) {
			searchVariables = new ArrayList<Variable>();
			this.updateExternalTitleProviderInstances(searchPage, specification);
		} else {
			searchVariables = new ArrayList<Variable>();
		}

		if (searchVariables != null) {
			for (Variable searchVariable : searchVariables) {
				Field searchField = this.buildSearchField(searchType, searchVariable);
				searchPage.getFields().add(searchField);
			}
		}

		return searchPage;
	}

	/**
	 * Builds the default search page based on the search type. This page does not contain any
	 * fields that relate to a particular specification.
	 * 
	 * @param searchType The title search type
	 * @return The search page based on the search type.
	 * @throws Exception Exception building the search page
	 */
	public Page buildDefaultSearchPage(TitleSearchType searchType) throws Exception {
		boolean isExternal = (searchType == TitleSearchType.EXTERNAL);
		String searchPageName = (isExternal) ? EXTERNAL_SEARCH_PAGE : INTERNAL_SEARCH_PAGE;
//		return PageConfig.getInstance().getPage(searchPageName);
		return null;
	}

	/* Get the specification given the name of the specification */
	private Specification getSpecification(String name) {
		ServiceRegistry registry = ServiceRegistry.getDefault();
		ISpecificationManager specManager = registry.lookup(ISpecificationManager.class);
		return specManager.getSpecificationByName(name);
	}

	/*
	 * Builds a single search field given the search variable in the specification, and a flag
	 * indicating if the Field is for an internal or external title search
	 */
	private Field buildSearchField(TitleSearchType searchType, Variable searchVariable) {
		Field searchField = new Field();
		Map<String, String> fieldAttributes = searchField.getAttributesMap();

		/* Set the field properties with defaults */
		String column = (searchType == TitleSearchType.EXTERNAL) ? "1" : "2";
		String dataType = "STRING";
		String type = "textbox";
		String paramType = "value";
		String operator = "LIKE";

		/* If Date field, then use a date range search */
		if (Datatype.DATE.toString().equals("")) {
			type = "dateRange";
			paramType = "range";
			operator = null;
		}

		fieldAttributes.put("name", searchVariable.getName());
		fieldAttributes.put("label", searchVariable.getDisplayName());
		fieldAttributes.put("dataType", dataType);
		fieldAttributes.put("type", type);
		fieldAttributes.put("paramType", paramType);
		fieldAttributes.put("column", column);
		fieldAttributes.put("operator", operator);
		fieldAttributes.put("entity", "metadata");

		return searchField;
	}

	/*
	 * Update the title 'External Location' field to contain the list of title provider instances
	 * based on the specification, sorted by the title provider instance name.
	 */
	private void updateExternalTitleProviderInstances(Page searchPage, Specification specification) {
		String fieldName = TitleSearchKey.TITLE_EXTERNAL_LOCATION.toString();
		Field field = searchPage.getFieldsMap().get(fieldName);

		/* Check if the field exists */
		if (field == null)
			return;

		/* Use a ordered map for the title provider instances */
		Map<String, String> instanceNames = new LinkedHashMap<String, String>();
		field.setValuesMap(instanceNames);

		/* Get the list of Title Provider instances for the given specification */
		List<ITitleProviderInstance> allInstances = new ArrayList<ITitleProviderInstance>();
		ServiceRegistry serviceRegistry = ServiceRegistry.getDefault();
		ITitleProviderRegistry registry = serviceRegistry.lookup(ITitleProviderRegistry.class);
		Collection<ITitleProvider> providers = registry.getProviders(specification.getName());
		for (ITitleProvider provider : providers) {
			Collection<ITitleProviderInstance> instances = provider.getProviderInstances();
			if (instances != null)
				allInstances.addAll(instances);
		}

		/* Sort the Title Provider Instances by name */
		Collections.sort(allInstances, new Comparator<ITitleProviderInstance>() {
			public int compare(ITitleProviderInstance o1, ITitleProviderInstance o2) {
				return o1.getName().compareTo(o2.getName());
			}
		});

		/* Add the sorted values in order to the linked hash map */
		for (ITitleProviderInstance instance : allInstances) {
			instanceNames.put(instance.getKey(), instance.getName());
		}
	}
}
