package com.tandbergtv.metadatamanager.specimpl;

import java.io.Serializable;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.log4j.Logger;
import org.springframework.transaction.annotation.Transactional;

import com.tandbergtv.metadatamanager.exception.SearchException;
import com.tandbergtv.metadatamanager.model.Asset;
import com.tandbergtv.metadatamanager.model.SearchCriteria;
import com.tandbergtv.metadatamanager.search.AssetSearchService;
import com.tandbergtv.metadatamanager.search.CriteriaBuilder;
import com.tandbergtv.metadatamanager.spec.IIdentifier;
import com.tandbergtv.metadatamanager.specimpl.ttv.TTVId;

/**
 * Base class for most identifiers.
 * 
 * @author spuranik
 * 
 */
public abstract class IdentifierBase implements IIdentifier, Serializable {
	private static final Logger logger = Logger.getLogger(IIdentifier.class);
	
	private static final long serialVersionUID = 2127389786395783352L;
	private AssetSearchService searchService;
	private URL mappingResourceUrl;

	/**
	 * @return the mappingResource
	 */
	public URL getMappingResourceUrl() {
		return mappingResourceUrl;
	}

	/**
	 * @param mappingResource
	 *            the mappingResource to set
	 */
	public void setMappingResourceUrl(URL mappingResourceUrl) {
		this.mappingResourceUrl = mappingResourceUrl;
	}

	@Transactional
	public Asset getAsset() throws SearchException {
		logger.debug("In Identfier.getAsset()");
		Collection<Asset> assets = new ArrayList<Asset>();
		
		SearchCriteria criteriaMap = buildSearchCriteriaMap(
				getSpecIdentifiers(), getTTVPaths());
		com.tandbergtv.workflow.util.SearchCriteria criteria = buildSearchCriteria(criteriaMap, false);
		
		if (criteria.getSearchList().size() > 0) {
			assets = searchService.search(criteria);
		}

		if (assets.size() == 0) {
			throw new SearchException("No asset found with id: "
					+ criteriaMap.toString());
		}
		if (assets.size() > 1) {
			throw new SearchException("Multiple assets found with id: "
					+ criteria.toString());
		}
		return assets.iterator().next();
	}

	@Transactional
	public TTVId getAssetTTVId() throws SearchException {
		logger.debug("In Identfier.getAssetTTVId()");
		Collection<Long> assetIDs = new ArrayList<Long>();
		
		SearchCriteria criteriaMap = buildSearchCriteriaMap(
				getSpecIdentifiers(), getTTVPaths());
		com.tandbergtv.workflow.util.SearchCriteria criteria = buildSearchCriteria(criteriaMap, true);
		
		if (criteria.getSearchList().size() > 0) {
			assetIDs = searchService.searchForAssetID(criteria);
		}

		if (assetIDs.size() == 0) {
			throw new SearchException("No asset found with id: "
					+ criteriaMap.toString());
		}
		if (assetIDs.size() > 1) {
			throw new SearchException("Multiple assets found with id: "
					+ criteria.toString());
		}
		return new TTVId(assetIDs.iterator().next());
	}

	protected com.tandbergtv.workflow.util.SearchCriteria buildSearchCriteria(
			SearchCriteria criteriaMap, boolean idOnly) {
		criteriaMap.put("rootlevel", "true");
		return CriteriaBuilder.buildFieldSearchCriteria(criteriaMap, idOnly);
	}

	/**
	 * Prepares a map (ttv xpath for field -> value of field) by iterating thru
	 * the fields map (spec field xpath -> value) and finding the match in
	 * ttvxpaths map (field xpath ->ttv xpath)
	 * 
	 * @param idFields
	 * @param ttvXpaths
	 * @return
	 */
	protected SearchCriteria buildSearchCriteriaMap(
			Map<String, String> idFields, Map<String, String> ttvXpaths) {

		SearchCriteria criteria = new SearchCriteria();
		Iterator<Entry<String, String>> iter = idFields.entrySet().iterator();
		while (iter.hasNext()) {
			Entry<String, String> entry = iter.next();
			// TODO: what happens if the xpath is not found?
			if (ttvXpaths.containsKey(entry.getKey())) {
				criteria.put(ttvXpaths.get(entry.getKey()), entry.getValue());
			}
		}
		return criteria;
	}

	public String toString() {
		StringBuilder sb = new StringBuilder();
		Map<String, String> ids = getSpecIdentifiers();
		Iterator<Entry<String, String>> iter = ids.entrySet().iterator();
		while (iter.hasNext()) {
			if (sb.length() > 0) {
				sb.append(", ");
			}
			Entry<String, String> entry = iter.next();
			sb.append(entry.getKey());
			sb.append("[");
			sb.append(entry.getValue());
			sb.append("]");
		}
		return sb.toString();
	}

	/**
	 * @return the searchService
	 */
	public AssetSearchService getSearchService() {
		return searchService;
	}

	/**
	 * @param searchService
	 *            the searchService to set
	 */
	public void setSearchService(AssetSearchService searchService) {
		this.searchService = searchService;
	}
}
