package eu.dnetlib.enabling.tools;

import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Required;

import com.google.common.collect.Lists;

import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpDocumentNotFoundException;
import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpException;
import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpService;
import eu.dnetlib.enabling.tools.ServiceLocator;
import eu.dnetlib.miscutils.collections.PositionalStringMapGenerator;

/**
 * Utility class which interacts with a lookup, performs a query and fills a java domain object splitting the result
 * into constructor arguments.
 * 
 * @author marko
 * 
 */
public class SplittedQueryExecutor {
	/**
	 * lookup locator.
	 */
	private ServiceLocator<ISLookUpService> lookupLocator;

	/**
	 * default constructor. lookupLocator has to be injected.
	 */
	public SplittedQueryExecutor() {
		// empty
	}

	/**
	 * Commodity constructor for constructor injection.
	 * 
	 * @param lookupLocator
	 *            lookup locator
	 */
	public SplittedQueryExecutor(ServiceLocator<ISLookUpService> lookupLocator) {
		super();
		this.lookupLocator = lookupLocator;
	}

	/**
	 * Performs the query, splits the result at ":-:".
	 * 
	 * @param <X>
	 *            domain class type
	 * @param clazz
	 *            domain class
	 * @param query
	 *            xquery
	 * @return iterable of domain class instances
	 */
	public <X> Iterable<X> query(final Class<X> clazz, final String query) {
		return query(clazz, query, ":-:");
	}

	/**
	 * Performs the query, splits the result at separator
	 * 
	 * @param <X>
	 *            domain class type
	 * @param clazz
	 *            domain class
	 * @param query
	 *            xquery
	 * @param separator
	 *            split separator
	 * @return iterable of domain class instances
	 */
	public <X> Iterable<X> query(final Class<X> clazz, final String query, final String separator) {
		return new PositionalStringMapGenerator<String>().split(clazz, performQuery(query), separator);
	}

	/**
	 * Return a list of maps of splitted query results
	 * 
	 * @param query
	 *            xquery
	 * @param keys
	 *            list of keys
	 * @return collection of key/value pairs
	 */
	public Iterable<Map<String, String>> query(final String query, final String... keys) {
		return new PositionalStringMapGenerator<String>(keys).split(performQuery(query), ":-:");
	}

	/**
	 * Like query(String, String..) but returns a container that whose values can be modified.
	 * 
	 * @param query
	 *            xquery
	 * @param keys
	 *            list of keys
	 * @return mutable collection of key/value pairs
	 */
	public Iterable<Map<String, String>> mutableQuery(final String query, final String... keys) {
		return Lists.newArrayList(query(query, keys));
	}

	/**
	 * Like query(Class, String), but returns a container which can be modified.
	 * 
	 * @param <X>
	 *            some domain class
	 * @param clazz
	 *            domain class
	 * @param query
	 *            xquery
	 * @return mutable collection of X
	 */
	public <X> Iterable<X> mutableQuery(final Class<X> clazz, final String query) {
		return Lists.newArrayList(query(clazz, query));
	}

	/**
	 * Like query(Class, String, String), but returns a container which can be modified.
	 * 
	 * @param <X>
	 *            some domain class
	 * @param clazz
	 *            domain class
	 * @param query
	 *            xquery
	 * @param separator
	 *            separator
	 * @return mutable collection of X
	 */
	public <X> Iterable<X> mutableQuery(final Class<X> clazz, final String query, final String separator) {
		return Lists.newArrayList(query(clazz, query, separator));
	}

	public List<String> performQuery(final String query) {
		try {
			return lookupLocator.getService().quickSearchProfile(query);
		} catch (final ISLookUpException e) {
			throw new IllegalStateException(e);
		}
	}

	/**
	 * Fetch only one string result.
	 * 
	 * @param query
	 * @return null if no such result
	 */
	public String queryFirst(final String query) {
		try {
			return lookupLocator.getService().getResourceProfileByQuery(query);
		} catch (ISLookUpDocumentNotFoundException e) {
			return null;
		} catch (final ISLookUpException e) {
			throw new IllegalStateException(e);
		}

	}

	public ServiceLocator<ISLookUpService> getLookupLocator() {
		return lookupLocator;
	}

	@Required
	public void setLookupLocator(ServiceLocator<ISLookUpService> lookupLocator) {
		this.lookupLocator = lookupLocator;
	}
}
