package com.tandbergtv.watchpoint.pmm.title.search;

import static com.tandbergtv.watchpoint.pmm.title.search.TitleSearchKey.DEFAULT_SYSTEM_ID;
import static com.tandbergtv.watchpoint.pmm.title.search.TitleSearchKey.TITLE_PROVIDER_ID;
import static com.tandbergtv.workflow.driver.search.SearchType.STRING;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import com.tandbergtv.metadatamanager.search.AssetSearchKey;
import com.tandbergtv.metadatamanager.search.AssetSearchService;
import com.tandbergtv.metadatamanager.search.FieldInfo;
import com.tandbergtv.metadatamanager.search.ListFieldInfo;
import com.tandbergtv.metadatamanager.search.MetadataValueFieldInfo;
import com.tandbergtv.metadatamanager.search.RangeFieldInfo;
import com.tandbergtv.metadatamanager.search.SearchInfo;
import com.tandbergtv.metadatamanager.search.SortInfo;
import com.tandbergtv.watchpoint.pmm.dao.hibernate.ApplicationContextHelper;
import com.tandbergtv.watchpoint.pmm.entities.Title;
import com.tandbergtv.watchpoint.search.Association;
import com.tandbergtv.watchpoint.search.Entity;
import com.tandbergtv.workflow.driver.search.ListParameter;
import com.tandbergtv.workflow.driver.search.RangeParameter;
import com.tandbergtv.workflow.driver.search.SearchType;
import com.tandbergtv.workflow.driver.search.SortParameter;
import com.tandbergtv.workflow.driver.search.ValueParameter;
import com.tandbergtv.workflow.util.SearchCriteria;
import com.tandbergtv.workflow.util.SortingOrder;

/**
 * Class that helps building the search criteria for title.
 * 
 * @author spuranik
 * 
 */
public class TitleSearchCriteriaBuilder {
	private static String ROOT_ENTITY_ALIAS = "rootAsset";
	public static int LATEST_REVISION = -1;

	public static SearchCriteria getCriteria(boolean isInternalSearch,
			String externalSourceId, String searchProperty,
			Collection<SearchField> searchFields, int startIndex,
			int maxRecordCount, String sortProperty, SortingOrder order,
			String sortName) {
		SearchCriteria criteria = new SearchCriteria();

		/* prepare title criteria */
		Entity title = new Entity("title", Title.class, "t");

		/* distinguish between internal and external search */
		if (isInternalSearch) {
			criteria.addParameter(new ValueParameter(TITLE_PROVIDER_ID
					.toString(), STRING, DEFAULT_SYSTEM_ID));
		} else {
			criteria.addParameter(new ValueParameter(TITLE_PROVIDER_ID
					.toString(), STRING, getTitleProviderId(externalSourceId)));
		}

		/* get active titles only */
		title.addParameter(new ValueParameter(TitleSearchKey.TITLE_ACTIVE
				.toString(), SearchType.NUMERIC, 1));

		/* get search fields */
		SearchInfo searchInfo = null;
		if (searchProperty.equals(AssetSearchKey.ALL_DESCENDANT_FIELDS
				.toString())
				|| searchProperty.equals(AssetSearchKey.FIELDS.toString())) {
			searchInfo = getSearchInfo(searchFields, searchProperty);
		} else {
			// attach the search fields directly to the title entity
			title = addSearchFields(title, searchFields);
		}

		/* get sort info */
		// if its a metadata property create the sort info
		SortInfo sortInfo = null;
		if (sortProperty != null
				&& (sortProperty.equals(AssetSearchKey.ALL_DESCENDANT_FIELDS
						.toString()) || sortProperty
						.equals(AssetSearchKey.FIELDS.toString()))) {
			sortInfo = getSortInfo(order, sortName, sortProperty);
		} else {
			// attach it to the title
			title = addSortField(title, order, sortName);
		}

		/* put all paging info */
		criteria.setStartingRecordNumber(startIndex);
		criteria.setRecordsCount(maxRecordCount);

		/* add title entity to search criteria */
		criteria.addParameter(title);

		/*
		 * Get and add asset criteria only if metadata search/sort is involved,
		 * otherwise skip it.
		 */
		if (searchInfo != null || sortInfo != null) {
			/* get asset search criteria */
			AssetSearchService assetSearch = ApplicationContextHelper
					.getInstance().getAssetSearchService();
			SearchCriteria assetCriteria = assetSearch.getCriteria(
					ROOT_ENTITY_ALIAS, searchInfo, sortInfo, LATEST_REVISION);
			Entity asset = (Entity) assetCriteria
					.getParameter(ROOT_ENTITY_ALIAS);

			/* create association between title and asset entities */
			Association a = new Association("titleAssetAssociation", title,
					"asset.id", asset, "id");
			criteria.addParameter(a);

			/* add asset to the criteria */
			criteria.addParameter(asset);
		}

		return criteria;
	}

	private static Entity addSortField(Entity title, SortingOrder order,
			String sortName) {
		if (sortName != null) {
			title.addParameter(new SortParameter(sortName, order));
		}
		return title;
	}

	private static Entity addSearchFields(Entity title,
			Collection<SearchField> searchFields) {
		for (SearchField field : searchFields) {
			if (field.isListParameter()) {
				ListParameter listParam = new ListParameter(field.getName(),
						field.getSearchType(), field.getSearchOperator());
				listParam.setValues(field.getValues());
			} else if (field.isRangeParameter()) {
				RangeParameter rangeParam = new RangeParameter(field.getName(),
						field.getSearchType());
				rangeParam.setFrom(field.getValues().get(0));
				rangeParam.setTo(field.getValues().get(1));
				title.addParameter(rangeParam);
			} else if (field.isValueParameter()) {
				title.addParameter(new ValueParameter(field.getName(), field
						.getSearchType(), field.getValues().get(0), field
						.getSearchOperator()));
			}
		}
		return title;
	}

	private static SortInfo getSortInfo(SortingOrder order, String sortName,
			String sortKeyProperty) {
		// if there is no sort field name then that means there is no sorting
		if (sortName == null) {
			return null;
		}

		SortInfo info = new SortInfo();
		info.setOrder(order);
		info.setSortItemName(sortName);
		if (sortKeyProperty != null) {
			info.setProperty(sortKeyProperty.toString());
		}
		return info;
	}

	private static SearchInfo getSearchInfo(
			Collection<SearchField> searchFields, String fieldProperty) {
		/* if there are no fields to search by, return null */
		if (searchFields.size() == 0) {
			return null;
		}

		SearchInfo info = new SearchInfo();
		info.setProperty(fieldProperty);
		List<FieldInfo> fields = getFields(searchFields);
		info.setFields(fields);
		return info;
	}

	private static List<FieldInfo> getFields(
			Collection<SearchField> searchFields) {
		List<FieldInfo> fields = new ArrayList<FieldInfo>();
		for (SearchField field : searchFields) {
			FieldInfo finfo = getFieldInfo(field);
			
			if (field.getSectionName() != null) {
				finfo.setAssetType(field.getSectionName());
			}
			fields.add(finfo);
		}
		return fields;
	}

	private static FieldInfo getFieldInfo(SearchField field) {
		if (field.isListParameter()) {
			return new ListFieldInfo(field.getName(), field.getValues());
		} else if (field.isValueParameter()) {
			return new MetadataValueFieldInfo(field.getName(), field
					.getSearchOperator(), field.getValues().get(0));
		} else if (field.isRangeParameter()) {
			return new RangeFieldInfo(field.getName(),
					field.getValues().get(0), field.getValues().get(1));
		}
		return null;
	}

	/* Get the provider Id from the title external source Id */
	private static String getTitleProviderId(String externalSourceId) {
		String providerId = null;

		if (externalSourceId != null) {
			String[] values = externalSourceId.split("-");
			providerId = (values.length > 0) ? values[0] : "";
		}

		return providerId;
	}
}
