package org.gcube.portlets.user.speciesdiscovery.server;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;

import net.sf.csv4j.CSVWriter;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.gcube.application.framework.core.session.ASLSession;
import org.gcube.common.homelibrary.home.workspace.Workspace;
import org.gcube.common.homelibrary.home.workspace.WorkspaceFolder;
import org.gcube.common.homelibrary.util.WorkspaceUtil;
import org.gcube.data.spd.model.products.OccurrencePoint;
import org.gcube.data.spd.model.products.ResultElement;
import org.gcube.data.spd.model.products.TaxonomyItem;
import org.gcube.data.spd.stubs.types.Status;
import org.gcube.portlets.user.speciesdiscovery.client.ConstantsSpeciesDiscovery;
import org.gcube.portlets.user.speciesdiscovery.client.model.ClassificationModel;
import org.gcube.portlets.user.speciesdiscovery.client.rpc.TaxonomySearchService;
import org.gcube.portlets.user.speciesdiscovery.client.util.GridField;
import org.gcube.portlets.user.speciesdiscovery.server.asl.SessionUtil;
import org.gcube.portlets.user.speciesdiscovery.server.job.OccurrenceJobUtil;
import org.gcube.portlets.user.speciesdiscovery.server.job.OccurrenceKeys;
import org.gcube.portlets.user.speciesdiscovery.server.job.TaxonomyJobUtil;
import org.gcube.portlets.user.speciesdiscovery.server.persistence.DaoSession;
import org.gcube.portlets.user.speciesdiscovery.server.persistence.dao.OccurrenceJobPersistence;
import org.gcube.portlets.user.speciesdiscovery.server.persistence.dao.OccurrenceRowPersistence;
import org.gcube.portlets.user.speciesdiscovery.server.persistence.dao.ResultRowPersistence;
import org.gcube.portlets.user.speciesdiscovery.server.persistence.dao.TaxonRowPersistence;
import org.gcube.portlets.user.speciesdiscovery.server.persistence.dao.TaxonomyJobPersistence;
import org.gcube.portlets.user.speciesdiscovery.server.persistence.dao.TaxonomyRowPersistence;
import org.gcube.portlets.user.speciesdiscovery.server.service.IteratorChainBuilder;
import org.gcube.portlets.user.speciesdiscovery.server.service.SpeciesService;
import org.gcube.portlets.user.speciesdiscovery.server.service.TaxonomyItemConverter;
import org.gcube.portlets.user.speciesdiscovery.server.session.FetchingSession;
import org.gcube.portlets.user.speciesdiscovery.server.session.FetchingSessionUtil;
import org.gcube.portlets.user.speciesdiscovery.server.session.FilterableFetchingBuffer;
import org.gcube.portlets.user.speciesdiscovery.server.session.SelectableFetchingBuffer;
import org.gcube.portlets.user.speciesdiscovery.server.stream.CSVGenerator;
import org.gcube.portlets.user.speciesdiscovery.server.stream.CloseableIterator;
import org.gcube.portlets.user.speciesdiscovery.server.stream.IteratorPointInfo;
import org.gcube.portlets.user.speciesdiscovery.server.stream.OccurenceCSVConverter;
import org.gcube.portlets.user.speciesdiscovery.server.stream.OccurenceCSVConverterOpenModeller;
import org.gcube.portlets.user.speciesdiscovery.server.stream.StreamExtend;
import org.gcube.portlets.user.speciesdiscovery.server.stream.aggregation.FieldAggregator;
import org.gcube.portlets.user.speciesdiscovery.server.stream.aggregation.TaxonomyClassificationAggregator;
import org.gcube.portlets.user.speciesdiscovery.server.util.DateUtil;
import org.gcube.portlets.user.speciesdiscovery.server.util.GetWorkspaceUtil;
import org.gcube.portlets.user.speciesdiscovery.server.util.QueryUtil;
import org.gcube.portlets.user.speciesdiscovery.shared.CommonName;
import org.gcube.portlets.user.speciesdiscovery.shared.DataSource;
import org.gcube.portlets.user.speciesdiscovery.shared.DataSourceModel;
import org.gcube.portlets.user.speciesdiscovery.shared.DatabaseServiceException;
import org.gcube.portlets.user.speciesdiscovery.shared.DownloadState;
import org.gcube.portlets.user.speciesdiscovery.shared.FetchingElement;
import org.gcube.portlets.user.speciesdiscovery.shared.ItemParameter;
import org.gcube.portlets.user.speciesdiscovery.shared.JobOccurrencesModel;
import org.gcube.portlets.user.speciesdiscovery.shared.JobTaxonomyModel;
import org.gcube.portlets.user.speciesdiscovery.shared.LightTaxonomyRow;
import org.gcube.portlets.user.speciesdiscovery.shared.MainTaxonomicRankEnum;
import org.gcube.portlets.user.speciesdiscovery.shared.Occurrence;
import org.gcube.portlets.user.speciesdiscovery.shared.OccurrenceBatch;
import org.gcube.portlets.user.speciesdiscovery.shared.OccurrencesJob;
import org.gcube.portlets.user.speciesdiscovery.shared.OccurrencesSaveEnum;
import org.gcube.portlets.user.speciesdiscovery.shared.OccurrencesStatus;
import org.gcube.portlets.user.speciesdiscovery.shared.ResultRow;
import org.gcube.portlets.user.speciesdiscovery.shared.SaveFileFormat;
import org.gcube.portlets.user.speciesdiscovery.shared.SearchByQueryParameter;
import org.gcube.portlets.user.speciesdiscovery.shared.SearchFilters;
import org.gcube.portlets.user.speciesdiscovery.shared.SearchResult;
import org.gcube.portlets.user.speciesdiscovery.shared.SearchResultType;
import org.gcube.portlets.user.speciesdiscovery.shared.SearchServiceException;
import org.gcube.portlets.user.speciesdiscovery.shared.SearchStatus;
import org.gcube.portlets.user.speciesdiscovery.shared.SearchType;
import org.gcube.portlets.user.speciesdiscovery.shared.Taxon;
import org.gcube.portlets.user.speciesdiscovery.shared.TaxonomyJob;
import org.gcube.portlets.user.speciesdiscovery.shared.TaxonomyRow;
import org.gcube.portlets.user.speciesdiscovery.shared.cluster.ClusterCommonNameDataSourceForResultRow;
import org.gcube.portlets.user.speciesdiscovery.shared.cluster.ClusterCommonNameDataSourceForTaxonomyRow;
import org.gcube.portlets.user.speciesdiscovery.shared.cluster.ClusterStructuresForResultRow;
import org.gcube.portlets.user.speciesdiscovery.shared.cluster.ClusterStructuresForTaxonomyRow;
import org.gcube.portlets.user.speciesdiscovery.shared.cluster.ManagerClusterCommonNameDataSourceForResultRow;
import org.gcube.portlets.user.speciesdiscovery.shared.cluster.ManagerClusterCommonNameDataSourceForTaxonomyRow;
import org.gcube.portlets.user.speciesdiscovery.shared.filter.ResultFilter;
import org.gcube.portlets.user.speciesdiscovery.shared.util.NormalizeString;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;

/**
 * The server side implementation of the RPC service.
 * @author "Federico De Faveri defaveri@isti.cnr.it" - 
 * @author "Francesco Mangiacrapa francesco.mangiacrapa@isti.cnr.it"
 */
public class TaxonomySearchServiceImpl extends RemoteServiceServlet implements TaxonomySearchService {

	protected static final String SAVE_CHILDREN_OF = "Save children of ";
	protected static final String RESUBMIT = "Resubmit";
	
	private static final long serialVersionUID = -287193068445844326L;

	protected static final long MAX_BUFFERING_ELEMENTS = 1000;
	protected static final long BUFFER_LIMIT = 10;

	protected Logger logger = Logger.getLogger(TaxonomySearchService.class);

	public static final String TAXONOMYUNKNOWN = "Unknown";
	public static final String BASETAXONOMY = "Kingdom";
	public static final String UNK = "Unk";

	static {
		Logger root = Logger.getLogger("org.gcube.portlets.user.speciesdiscovery");
		root.setLevel(Level.ALL);
	}

	protected ASLSession getASLSession()
	{
		return SessionUtil.getAslSession(this.getThreadLocalRequest().getSession());
	}

	protected SpeciesService getSpeciesService() throws SearchServiceException
	{
		try {
			ASLSession session = getASLSession();
			return SessionUtil.getService(session);
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("An error occurred when contacting the species service", e);
			//			System.out.println("An error occurred retrieving the service" +e);
			throw new SearchServiceException("contacting the species service.");
		}
	}

	protected FetchingSession<? extends FetchingElement> getSearchSession() throws SearchServiceException
	{
		ASLSession session = getASLSession();
		FetchingSession<? extends FetchingElement> searchSession = SessionUtil.getCurrentSearchSession(session);

		if (searchSession == null) {
			logger.error("No search session found for user "+session.getUsername());
			throw new SearchServiceException("No search session found for user "+session.getUsername());
		}

		return searchSession;
	}



	protected FetchingSession<Occurrence> getOccurrenceSession() throws SearchServiceException
	{
		ASLSession session = getASLSession();
		FetchingSession<Occurrence> occurrenceSession = SessionUtil.getCurrentOccurrenceSession(session);

		if (occurrenceSession == null) {
			logger.error("No occurrence session found for user "+session.getUsername());
			throw new SearchServiceException("No occurrence session found for user "+session.getUsername());
		}

		return occurrenceSession;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void searchByScientificName(String searchTerm, SearchFilters searchFilters) throws SearchServiceException {
		logger.trace("searchByScientificName searchTerm: "+searchTerm+" searchFilters: "+searchFilters);

//		System.out.println("searchByScientificName searchTerm: "+searchTerm+" searchFilters: "+searchFilters);
		
		stopSearch();
		search(searchTerm, SearchType.BY_SCIENTIFIC_NAME, searchFilters);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void searchByCommonName(String searchTerm, SearchFilters searchFilters) throws SearchServiceException {
		logger.trace("searchByCommonName searchTerm: "+searchTerm+" searchFilters: "+searchFilters);

		stopSearch();
		search(searchTerm, SearchType.BY_COMMON_NAME, searchFilters);
	}

	protected SearchResultType search(String searchTerm, SearchType searchType, SearchFilters searchFilters) throws SearchServiceException
	{
		
		ASLSession aslSession = getASLSession();
		try {
			deleteAllRowIntoDaoTable(); //RESET TABLE

			//			logger.trace("In search session id: " + aslSession.getExternalSessionID() + " sessione username: " +aslSession.getUsername() + " session scope: "+aslSession.getScopeName());

			SpeciesService taxonomyService = getSpeciesService();
			CloseableIterator<ResultElement> input = taxonomyService.searchByFilters(searchTerm, searchType, searchFilters);
			//DEBUG
			
			SessionUtil.setCurrentQuery(aslSession, taxonomyService.getLastQuery());
//			System.out.println("returned input stream by service...");
			logger.trace("returned input stream by service...");

			SearchResultType resultType = QueryUtil.getResultType(searchFilters);
			CloseableIterator<FetchingElement> output = IteratorChainBuilder.buildChain(input, resultType, aslSession);
			FetchingSessionUtil.createFetchingSession(output, resultType, aslSession);
			return resultType;
		} catch (Exception e) {
			logger.error("Error starting search "+searchType+" for term \""+searchTerm+"\" with filters "+searchFilters, e);
			e.printStackTrace();
			SessionUtil.setCurrentQuery(aslSession, "invalid query");
			throw new SearchServiceException(e.getMessage());
			
		}
	}


	@Override
	public SearchByQueryParameter searchByQuery(String query) throws SearchServiceException {
		logger.trace("searchByQuery - query: "+query);

		stopSearch();
		ASLSession aslSession = getASLSession();
		
		try {
			deleteAllRowIntoDaoTable(); //RESET TABLE

			//			logger.trace("In searchByQuery session id: " + aslSession.getExternalSessionID() + " sessione username: " +aslSession.getUsername() + " session scope: "+aslSession.getScopeName());

			SpeciesService taxonomyService = getSpeciesService();
			CloseableIterator<ResultElement> input = taxonomyService.searchByQuery(query);

			SessionUtil.setCurrentQuery(aslSession, query);
			
			//DEBUG
//			System.out.println("service return iterator searched...");
			logger.trace("service return iterator searched...");
			
			SearchByQueryParameter queryParameters = QueryUtil.getQueryResultType(query);
			CloseableIterator<FetchingElement> output = IteratorChainBuilder.buildChain(input, queryParameters.getSearchResultType(), aslSession);
			FetchingSessionUtil.createFetchingSession(output, queryParameters.getSearchResultType(), aslSession);
			logger.trace("creating fetching session completed!");
			return queryParameters;

		} catch (Exception e) {
			logger.error("Error starting search by query", e);
			SessionUtil.setCurrentQuery(aslSession, "invalid query");
			throw new SearchServiceException(e.getMessage());
		}
	}


	private void deleteAllRowIntoDaoTable() throws Exception{

//		BaseDaoImpl<ResultRow, Integer> daoResultRow = null;
		ResultRowPersistence daoResultRow = null;
		TaxonRowPersistence daoTaxon = null;
//		BaseDaoImpl<CommonName, Integer> daoCommonName = null;
		TaxonomyRowPersistence daoTaxonomyRow = null;
		ASLSession session = getASLSession();

		try {
			daoResultRow = DaoSession.getResultRowDAO(session);
			daoTaxon = DaoSession.getTaxonDAO(session);
			daoTaxonomyRow = DaoSession.getTaxonomyDAO(session);

		} catch (Exception e) {
			logger.error("Error in delete all row -  getDao's " +e.getMessage(), e);
			throw new Exception("Error in delete all row-  getDao's " + e.getMessage(), e);
		}


		try {
			if(daoResultRow!=null){
				daoResultRow.removeAll();
			}

			if(daoTaxon!=null){
				daoTaxon.removeAll();
			}
			
			if(daoTaxonomyRow!=null)
				daoTaxonomyRow.removeAll();

			logger.trace("delete all row from Dao's - completed");
		} catch (Exception e) {
			logger.error("Error in delete all row");
			throw new Exception("Error in delete all row" + e.getCause(), e);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	@Override
	public SearchResult<ResultRow> getSearchResultRows(int start, int limit, ResultFilter activeFiltersObject, boolean onlySelected) throws SearchServiceException {
		logger.trace("getSearchResultRows start: "+start+" limit: "+limit+" onlySelected: "+onlySelected);
		Long startTime =  System.currentTimeMillis();

		FetchingSession<ResultRow> searchSession = (FetchingSession<ResultRow>) getSearchSession();

		ArrayList<ResultRow> chunk = new ArrayList<ResultRow>();

		
		try {
			List<ResultRow> data = new ArrayList<ResultRow>();
			if (onlySelected) {
				SelectableFetchingBuffer<ResultRow> buffer = (SelectableFetchingBuffer<ResultRow>) searchSession.getBuffer();
				data = buffer.getSelected();
				
				int end = Math.min(start+limit, data.size());
				start = Math.min(start, end);

				logger.trace("chunk selected data bounds [start: "+start+" end: " + end+"]");
				
				data = data.subList(start, end);
				
			} else if (activeFiltersObject == null || (!activeFiltersObject.isActiveFilters())) {
				if(limit>0){
					data = searchSession.getBuffer().getList(start,limit);
				}
			} else {
				FilterableFetchingBuffer<ResultRow> buffer = (FilterableFetchingBuffer<ResultRow>) searchSession.getBuffer();
				data = buffer.getFilteredList(activeFiltersObject);
				
				int end = Math.min(start+limit, data.size());
				start = Math.min(start, end);

				logger.trace("chunk filtered data bounds [start: "+start+" end: " + end+"]");
				
				data = data.subList(start, end);
			}

			logger.trace("Fetching data from search session buffer, size: "+data.size());

//			int end = Math.min(start+limit, data.size());
//			start = Math.min(start, end);

			for (ResultRow resultRow : data) {

				//return common names?
				if(activeFiltersObject == null || !activeFiltersObject.isLoadCommonName() || !resultRow.existsCommonName()){
					resultRow.setCommonNames(null);
				}
				
				//return properties?
				if(activeFiltersObject == null || !resultRow.existsProperties() || !activeFiltersObject.isLoadAllProperties()){
					resultRow.setProperties(null);
				}

				//FOR DEBUG
//				System.out.println("################### ResultRow parents: "+resultRow.getParents());

				chunk.add(resultRow);

				logger.trace("getSearchResultRows return on client result item with id: " +resultRow.getId() + " service id: "+resultRow.getServiceId());
				
//				DEBUG
//				logger.trace("getSearchResultRows return on client result row: " +resultRow);
				//System.out.println("getSearchResultRows return on client result item with id: " +resultRow.getId() + " service id: "+resultRow.getServiceId());
			}

			Long endTime = System.currentTimeMillis() - startTime;
			String time = String.format("%d msc %d sec", endTime, TimeUnit.MILLISECONDS.toSeconds(endTime));
			logger.trace("returning "+chunk.size()+" elements in " + time);
			//			result = new SearchResult(chunk);

		} catch (Exception e) {
			logger.error("Error in getSearchResultRows " + e.getMessage());
			e.printStackTrace();
			//			System.out.println("Error in getSearchResultRows " +e);
			//			System.out.println("Error in getSearchResultRows " +e.getMessage());
			throw new SearchServiceException(e.getMessage());
		}
		
//		return new SearchResult<ResultRow>(new ArrayList<ResultRow>());

		return new SearchResult<ResultRow>(chunk);
	}
	
	private void printProperties(List<ItemParameter> properties){
		
		for (ItemParameter itemParameter : properties) {
			System.out.println("Property "+itemParameter);
		}
		
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public SearchResult<TaxonomyRow> getSearchTaxonomyRow(int start, int limit, ResultFilter activeFiltersObject, boolean onlySelected) throws SearchServiceException {

		logger.trace("getSearchResultRows start: "+start+" limit: "+limit+" onlySelected: "+onlySelected);
		Long startTime =  System.currentTimeMillis();

		FetchingSession<TaxonomyRow> searchSession = (FetchingSession<TaxonomyRow>) getSearchSession();

		ArrayList<TaxonomyRow> chunk = new ArrayList<TaxonomyRow>();

		try {

			logger.trace("current buffer size "+searchSession.getBuffer().size());
			List<TaxonomyRow> data = new ArrayList<TaxonomyRow>();

			if (onlySelected) {
				logger.trace("getting only selected data");
				SelectableFetchingBuffer<TaxonomyRow> buffer = (SelectableFetchingBuffer<TaxonomyRow>) searchSession.getBuffer();
				data = buffer.getSelected();
				
				int end = Math.min(start+limit, data.size());
				start = Math.min(start, end);

				logger.trace("chunk selected data bounds [start: "+start+" end: " + end+"]");
				
				data = data.subList(start, end);
				
			} else if (activeFiltersObject == null || (!activeFiltersObject.isActiveFilters())) {
				logger.trace("getting all available data");
				
				if(limit>0){
					Map<String, String> filterAndMap = new HashMap<String, String>();
					filterAndMap.put(TaxonomyRow.IS_PARENT, "false");
					data = searchSession.getBuffer().getList(filterAndMap, start,limit);
				}
			} else {
				logger.trace("getting filtered data");
				FilterableFetchingBuffer<TaxonomyRow> buffer = (FilterableFetchingBuffer<TaxonomyRow>) searchSession.getBuffer();
				data = buffer.getFilteredList(activeFiltersObject);
				
				int end = Math.min(start+limit, data.size());
				start = Math.min(start, end);

				logger.trace("chunk filtered data bounds [start: "+start+" end: " + end+"]");
				
				data = data.subList(start, end);
			}

			logger.trace("Fetching data from search session buffer, size: "+data.size());

			for (TaxonomyRow taxonomyRow : data) {
				//ADD common names

				//return common names?
				if(activeFiltersObject == null || !activeFiltersObject.isLoadCommonName() || !taxonomyRow.existsCommonName()){
					taxonomyRow.setCommonNames(null);
				}

				//return properties?
				if(activeFiltersObject == null || !taxonomyRow.existsProperties() || !activeFiltersObject.isLoadAllProperties()){
					taxonomyRow.setProperties(null);
				}
		
				//				logger.trace("getSearchTaxonomyRow return on client taxonomy row: " +taxonomyRow.getId());

				logger.trace("getSearchTaxonomyRow return on client taxonomy item with id: " +taxonomyRow.getId() + " service id: "+taxonomyRow.getServiceId());
				//				System.out.println("getSearchTaxonomyRow return on client taxonomy item with id: " +taxonomyRow.getId() + " service id: "+taxonomyRow.getServiceId());

				chunk.add(taxonomyRow);

			}

			Long endTime = System.currentTimeMillis() - startTime;
			String time = String.format("%d msc %d sec", endTime, TimeUnit.MILLISECONDS.toSeconds(endTime));
			logger.trace("returning "+chunk.size()+" elements in " + time);

		} catch (Exception e) {
			logger.error("Error in getSearchTaxonomyRow " + e.getMessage(), e);
			e.printStackTrace();
			//			System.out.println("Error in getSearchTaxonomyRow " +e);
			//			System.out.println("Error in getSearchTaxonomyRow " +e.getMessage());
			throw new SearchServiceException(e.getMessage());
		}

		return new SearchResult<TaxonomyRow>(chunk);
	}

	public TaxonomyRow loadTaxonomyParentByParentId(String parentID) throws Exception {

		TaxonomyRow taxonomyRow = null;
		logger.trace("loadTaxonomyParentByParentId: "+ parentID);
		
		try {
			
			TaxonomyRowPersistence dao = DaoSession.getTaxonomyDAO(getASLSession());
			
			CriteriaBuilder queryBuilder = dao.getCriteriaBuilder();
			CriteriaQuery<Object> cq = queryBuilder.createQuery();
			Predicate pr1 =  queryBuilder.equal(dao.rootFrom(cq).get(TaxonomyRow.PARENT_ID), parentID);
			cq.where(pr1);

			Iterator<TaxonomyRow> iterator = dao.executeCriteriaQuery(cq).iterator();

			if(iterator!=null && iterator.hasNext()){
				taxonomyRow = iterator.next();
			}
			
		} catch (Exception e) {
			logger.error("Error in loadTaxonomyParentsByRowId", e);
			throw new Exception(e);
		}

		return taxonomyRow;
	}

//	public void loadParentsListOfTaxonomy(TaxonomyRow taxonomy) throws Exception {
//
//		taxonomy.setParent(setParentListOfTaxonomy(taxonomy.getParent()));
//	}
//
//	private TaxonomyRow setParentListOfTaxonomy(TaxonomyRow taxonomy) throws Exception{
//
//		if (taxonomy == null) return null;
//		//		DaoSession.getTaxonomyDAO(getASLSession()).refresh(taxonomy.getParent());
//		taxonomy.setParent(setParentListOfTaxonomy(taxonomy.getParent()));
//		return taxonomy;	
//	}



	/**
	 * {@inheritDoc}
	 */
	@Override
	public HashMap<String, Integer> getFilterCounterById(GridField field) throws Exception {
		logger.trace("Filter Counter for: "+ field);
		try {
			FetchingSession<? extends FetchingElement> searchSession = (FetchingSession<? extends FetchingElement>) getSearchSession();
			FieldAggregator<?,?> aggregator = (FieldAggregator<?,?>) searchSession.getAggregator(FieldAggregator.getFieldAggregatorName(field));
			if (aggregator!=null) return aggregator.getAggregation();
			else return new HashMap<String, Integer>();
		} catch(Exception e)
		{
			logger.error("Error in getFilterCounterById "+ field.getId()+" "+field.getName(), e);
			throw new SearchServiceException(e.getMessage());
		}
	}


	@Override
	public HashMap<String, ClassificationModel> getFilterCounterForClassification(String rankLabel) throws Exception {
		logger.trace("Counter for classification: "+ rankLabel);
		try {
			MainTaxonomicRankEnum rank = MainTaxonomicRankEnum.valueOfLabel(rankLabel);

			if (rank!=null) {
				FetchingSession<? extends FetchingElement> searchSession = (FetchingSession<? extends FetchingElement>) getSearchSession();
				TaxonomyClassificationAggregator<?> classificationAggregator = (TaxonomyClassificationAggregator<?>) searchSession.getAggregator(TaxonomyClassificationAggregator.NAME);
				return classificationAggregator.getAggregation().get(rank);
			} else return new HashMap<String, ClassificationModel>();
		} catch(Exception e)
		{
			logger.error("Error in getFilterCounterForClassification "+ rankLabel, e);
			throw new SearchServiceException(e.getMessage());
		}
	}


	/**
	 * {@inheritDoc}
	 */
	@Override
	public SearchStatus getSearchStatus(boolean onlySelected, boolean isActiveFilterOnResult) throws SearchServiceException {
		logger.trace("getSearchStatus onlySelected: "+onlySelected);

		FetchingSession<? extends FetchingElement> searchSession = getSearchSession();

		SearchStatus status = new SearchStatus();
		int bufferSize = 0;

		try {
			bufferSize = isActiveFilterOnResult?((FilterableFetchingBuffer<? extends FetchingElement>) searchSession.getBuffer()).getFilteredListSize():searchSession.getBufferSize();
		} catch (Exception e) {
			logger.trace("getSearchStatus bufferSize error : "+e.getMessage(), e);
			throw new SearchServiceException(e.getMessage());
		}

		logger.trace("getSearchStatus bufferSize " + bufferSize);
		//if buffer size is >= the MAXIMUM ELEMENTS. Maximun is reached and the search is stopped
		if(bufferSize>=MAX_BUFFERING_ELEMENTS){
			logger.trace("getSearchStatus MAX_BUFFERING_ELEMENTS is reached -  stop search");
			stopSearchWithoutRemove();
			
			
			//CALCULATE NEW BUFFER SIZE AFTER FETCHING IS CLOSED
			try {
//				int sleepingTime = 500;
//				logger.trace("sleeping "+sleepingTime+" ms for translate last objets arrived into buffer");
//				Thread.sleep(sleepingTime); //SLEEPING 0,5 sec, for translating objects that are inserting in buffer and calculate new size of buffer
//				logger.trace("sleep termined - search status alive");
				bufferSize = isActiveFilterOnResult?((FilterableFetchingBuffer<? extends FetchingElement>) searchSession.getBuffer()).getFilteredListSize():searchSession.getBufferSize();
			
			} catch (SQLException e) {
				logger.trace("getSearchStatus bufferSize error : "+e.getMessage(), e);
				throw new SearchServiceException("An error occured on server in searching status, please retry");
			} catch (Exception e) {
				logger.trace("getSearchStatus bufferSize error : "+e.getMessage(), e);
				throw new SearchServiceException("An error occured on server in searching status, please retry");
			}
			
			status.setResultEOF(true);
			status.setSize(bufferSize);
			status.setIsMaxSize(true);
			return status;
		}

		//		logger.trace("Control !onlySelected && !isActiveFilterOnResult");
		if (!onlySelected && !isActiveFilterOnResult) {
			status.setResultEOF(searchSession.isComplete());
			status.setSize(bufferSize);
			//			logger.trace("Control isActiveFilterOnResult "+ isActiveFilterOnResult);
		} else if(isActiveFilterOnResult){
			status.setResultEOF(true);

			try {
				status.setSize(((FilterableFetchingBuffer<? extends FetchingElement>) searchSession.getBuffer()).getFilteredListSize());
			} catch (Exception e) {
				logger.error("isActiveFilterOnResult - An error occured in getSearchStatus " +e.getMessage(), e);
				throw new SearchServiceException(e.getMessage());
			}
		}
		else{

			status.setResultEOF(true);

			try {
				status.setSize(((SelectableFetchingBuffer<? extends FetchingElement>) searchSession.getBuffer()).getSelected().size());
			} catch (Exception e) {
				logger.error("An error occured in getSearchStatus " +e.getMessage(), e);
				throw new SearchServiceException(e.getMessage());
			}
		}

		logger.trace("getSearchStatus return status size: "+status.getSize()  +" EOF: " + status.isResultEOF());
		return status;
	}


	/**
	 * {@inheritDoc}
	 */
	public void stopSearchWithoutRemove() throws SearchServiceException {
		logger.trace("stopSearch without Remove");

		ASLSession session = getASLSession();
		//we safely get the session if exists
		FetchingSession<? extends FetchingElement> searchSession = SessionUtil.getCurrentSearchSession(session);

		if (searchSession != null) {
			try {
				searchSession.close();
			} catch (IOException e) {
				throw new SearchServiceException(e.getMessage());
			}
		} else logger.warn("Search session not found");
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void stopSearch() throws SearchServiceException {
		logger.trace("stopSearch");

		ASLSession session = getASLSession();
		FetchingSession<? extends FetchingElement> searchSession = SessionUtil.getCurrentSearchSession(session);

		if (searchSession != null) {
			try {
				searchSession.close();
			} catch (IOException e) {
				throw new SearchServiceException(e.getMessage());
			}
			SessionUtil.setCurrentSearchSession(session, null);
		} else logger.warn("Search session not found");
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void updateRowSelection(int rowId, boolean selection) throws SearchServiceException {
		logger.trace("updateRowSelection rowId: "+rowId+" selection: "+selection);

		try {
			ASLSession session = getASLSession();
			FetchingSession<? extends FetchingElement> searchSession = SessionUtil.getCurrentSearchSession(session);
			SelectableFetchingBuffer<? extends FetchingElement> buffer = (SelectableFetchingBuffer<? extends FetchingElement>) searchSession.getBuffer();
			buffer.updateSelection(rowId, selection);

		} catch(Exception e){
			logger.error("Error in updateRowSelection rowId: "+rowId+" selection: "+selection, e);
			throw new SearchServiceException(e.getMessage());
		}
	}


	/**
	 * {@inheritDoc}
	 */
	@Override
	public Integer updateRowSelections(boolean selection, ResultFilter activeFiltersObject) throws SearchServiceException {
		logger.trace("updateRowSelections selection: "+selection);
		List<? extends FetchingElement> data;
		FetchingSession<? extends FetchingElement> searchSession = getSearchSession();
		int size = 0;
		try {

			if (activeFiltersObject == null || (!activeFiltersObject.isActiveFilters())) {
				SelectableFetchingBuffer<? extends FetchingElement> buffer = (SelectableFetchingBuffer<? extends FetchingElement>) searchSession.getBuffer();
				buffer.updateAllSelection(selection);
				size = buffer.size();
			} else {
				@SuppressWarnings("unchecked")
				FilterableFetchingBuffer<ResultRow> buffer = (FilterableFetchingBuffer<ResultRow>) searchSession.getBuffer();
				data = buffer.getFilteredList(activeFiltersObject);
				
				if(data!=null){
					
					List<String> ids = new ArrayList<String>();
					for (FetchingElement fetchingElement : data){
						ids.add(fetchingElement.getId()+"");
					}
					SelectableFetchingBuffer<? extends FetchingElement> bufferCompleted = (SelectableFetchingBuffer<? extends FetchingElement>) searchSession.getBuffer();
					bufferCompleted.updateAllSelectionByIds(selection, ids);
					
					size = data.size();
					
					//OLD CODE
//					for (FetchingElement fetchingElement : data)
//						updateRowSelection(fetchingElement.getId(), selection);
//					
//					size = data.size();
				}
			}

		} catch (Exception e) {
			logger.error("An error occurred in updateRowSelections", e);
			throw new SearchServiceException(e.getMessage());
		}
		
		return Integer.valueOf(size);
	}


	/**
	 * {@inheritDoc}
	 * @throws SearchServiceException 
	 */
	@Override
	public int countOfSelectedRow() throws SearchServiceException{
		logger.trace("countOfSelectedRow()");

		FetchingSession<? extends FetchingElement> searchSession = getSearchSession();

		try {

			SelectableFetchingBuffer<? extends FetchingElement> buffer = (SelectableFetchingBuffer<? extends FetchingElement>) searchSession.getBuffer();
			return buffer.sizeSelected();

		} catch (Exception e) {
			logger.error("An error occurred in updateRowSelections", e);
			throw new SearchServiceException(e.getMessage());
		}
	}


	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	@Override
	public int retrieveOccurencesFromSelection() throws SearchServiceException {
		logger.trace("retrieveOccurencesFromSelection()");

		int count = 0;
		FetchingSession<ResultRow> searchSession = (FetchingSession<ResultRow>) getSearchSession();

		try {
			Collection<ResultRow> selectedRows = ((SelectableFetchingBuffer<ResultRow>) searchSession.getBuffer()).getSelected();
			logger.trace("found "+selectedRows.size()+" selected rows");

			List<String> keys = new ArrayList<String>(selectedRows.size());

			for (ResultRow row:selectedRows) {
				//ADD KEY ONLY IF IS NOT EQUAL NULL AND SIZE IS > 0
				if(row.getOccurencesKey()!=null && row.getOccurencesKey().length()>0){
					keys.add(row.getOccurencesKey());
					count += row.getOccurencesCount();
				}
			}

			logger.trace("found "+count+" occurrence points");

			//TODO remove
			if (logger.isTraceEnabled()) logger.trace("selected keys: "+keys);

			SpeciesService taxonomyService = getSpeciesService();

			CloseableIterator<OccurrencePoint> source = taxonomyService.getOccurrencesByKeys(keys);
			CloseableIterator<Occurrence> input = IteratorChainBuilder.buildOccurrenceConverter(source);

			//DELETE ALL ROW INTO DAO OCCURENCES
			OccurrenceRowPersistence occurrencesDao = DaoSession.getOccurrenceDAO(getASLSession());
			occurrencesDao.removeAll();
			FetchingSessionUtil.createOccurrenceFetchingSession(input, getASLSession());

		} catch (Exception e) {
			logger.error("An error occurred getting the number of occurrence points", e);
			throw new SearchServiceException(e.getMessage());
		}

		return count;
	}

	@Override
	public OccurrenceBatch getOccurrencesBatch(int start, int limit) throws SearchServiceException {
		logger.trace("getOccurrencesBatch: start: "+start+" limit: "+limit);

		FetchingSession<Occurrence> occurrenceSession = getOccurrenceSession();

		//		ASLSession session = getASLSession();
		//		FetchingSession<Occurrence> occurrenceSession = SessionUtil.getCurrentOccurrenceSession(session);

		List<Occurrence> buffer;
		OccurrenceBatch result = null;

		try {
			buffer = occurrenceSession.getBuffer().getList();

			logger.trace("Fetching data from occurrence session buffer, size: "+buffer.size());

			int end = Math.min(start+limit, buffer.size());

			logger.trace("chunk bounds[start: "+start+" end: " + end+"]");

			ArrayList<Occurrence> data = new ArrayList<Occurrence>(buffer.subList(start, end));
			logger.trace("returning "+data.size()+" elements");

			//DEBUG
//			for (Occurrence occurrence : data) {
//				logger.trace("citation :" + occurrence.getCitation()+
//						" catalog number: "+occurrence.getCatalogueNumber()+
//						" country: " +occurrence.getCountry()+
//						" family: "+occurrence.getFamily()+
//						" id: "+ occurrence.getId() +
//						" institute code: " +occurrence.getInstitutionCode() +
//						" kingdom: " + occurrence.getKingdom()+
//						" scientific name: "+ occurrence.getScientificName()+
//						" basis of record: "+occurrence.getBasisOfRecord());
//
//			}

			result = new OccurrenceBatch(data);
			result.setResultEOF(occurrenceSession.isComplete());

		} catch (Exception e) {
			logger.error("An error occurred getting the occurrence points", e);
			throw new SearchServiceException(e.getMessage());
		}

		return result;
	}

	@Override
	public OccurrencesStatus getCountOfOccurrencesBatch() throws SearchServiceException {
		logger.trace("get CountOf Occurrences Batch");

		FetchingSession<Occurrence> occurrenceSession = getOccurrenceSession();
		List<Occurrence> buffer;

		try {
			buffer = occurrenceSession.getBuffer().getList();

			logger.trace("Fetching data from occurrence session buffer, size: "+buffer.size());


			return new OccurrencesStatus(occurrenceSession.isComplete(), buffer.size());

		} catch (Exception e) {
			logger.error("An error occurred getting the occurrence points", e);
			throw new SearchServiceException(e.getMessage());
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void stopRetrievingOccurrences() throws SearchServiceException {
		logger.trace("stop Retrieving Occurrences ");

		ASLSession session = getASLSession();
		FetchingSession<Occurrence> occurrenceSearchSession = SessionUtil.getCurrentOccurrenceSession(session);

		if (occurrenceSearchSession != null) {
			try {
				occurrenceSearchSession.close();
				logger.trace("Occurrence session removed");
			} catch (IOException e) {
				throw new SearchServiceException(e.getMessage());
			}
			SessionUtil.setCurrentOccurrenceSession(session, null);
		} else logger.warn("Occurrence session not found");
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String generateMapFromSelectedOccurrencePoints() throws SearchServiceException {

		logger.trace("generate Map From Occurrence Points");

		Iterator<Occurrence> iteratorOccurrences = getIteratorSelectedOccurrenceIds();
		IteratorPointInfo streamKey = new IteratorPointInfo(iteratorOccurrences);

		try {
			SpeciesService taxonomyService = getSpeciesService();
			String groupName = taxonomyService.generateMapFromOccurrencePoints(streamKey);
			logger.trace("generated groupName: "+groupName);
			return groupName;
		} catch (Exception e) {
			logger.error("An error occurred creating the map", e);
			throw new SearchServiceException(e.getMessage());
		}
	}

	//USED FOR DEBUG
	protected void printId(List<String> listId){

		for (String id : listId) {
			System.out.println("Found id : " +id);
		}

	}

	protected List<String> getSelectedOccurrenceIds() throws SearchServiceException{

		FetchingSession<Occurrence> occurrenceSession = getOccurrenceSession();

		List<Occurrence> buffer;
		List<String> listId = new ArrayList<String>();

		try {

			buffer = occurrenceSession.getBuffer().getList();

			for (Occurrence occurrence : buffer) {
				listId.add(occurrence.getServiceId());
			}

			return  listId;
		} catch (Exception e) {

			logger.error("An error occurred on getSelectedOccurrenceIds", e);
			throw new SearchServiceException(e.getMessage());
		}

	}

	protected Iterator<Occurrence> getIteratorSelectedOccurrenceIds() throws SearchServiceException{

		FetchingSession<Occurrence> occurrenceSession = getOccurrenceSession();
		try {
			return occurrenceSession.getBuffer().getList().iterator();
		} catch (Exception e) {
			logger.error("An error occurred on getIteratorSelectedOccurrenceIds", e);
			throw new SearchServiceException(e.getMessage());
		}

	}

	@Override
	public void saveSelectedOccurrencePoints(String destinationFolderId, String fileName, SaveFileFormat fileFormat, OccurrencesSaveEnum typeCSV) throws SearchServiceException {
		logger.trace("saveSelectedOccurrencePoints destinationFolderId: "+destinationFolderId+" fileName: "+fileName+" fileFormat: "+fileFormat+" typeCSV: "+typeCSV);

		//TODO REMOVE - THIS IS OLD METHOD
	}

	@Override
	public void saveSelectedTaxonomyPoints(String destinationFolderId, String fileName, SaveFileFormat fileFormat) throws SearchServiceException {
		//TODO OLD CALL
	}

	@SuppressWarnings("unchecked")
	protected List<String> getSelectedOccurrenceKeys() throws SearchServiceException
	{
		FetchingSession<ResultRow> searchSession = (FetchingSession<ResultRow>) getSearchSession();

		Collection<ResultRow> selectedRows;

		List<String> keys = null;
		try {
			selectedRows = ((SelectableFetchingBuffer<ResultRow>) searchSession.getBuffer()).getSelected();

			logger.trace("found "+selectedRows.size()+" selected rows");

			keys = new ArrayList<String>(selectedRows.size());
			int count = 0;

			for (ResultRow row:selectedRows) {
				keys.add(row.getOccurencesKey());
				count += row.getOccurencesCount();
			}

			logger.trace("found "+count+" occurrence points and "+keys.size()+" keys");

			//TODO remove
			if (logger.isTraceEnabled()) logger.trace("selected keys: "+keys);

		} catch (Exception e) {
			logger.error("An error occured in getSelectedOccurrenceKeys" + e.getMessage());
			throw new SearchServiceException(e.getMessage());
		}

		return keys;
	}


	@SuppressWarnings("unchecked")
	protected List<String> getSelectedResultRowId() throws SearchServiceException
	{
		FetchingSession<ResultRow> searchSession = (FetchingSession<ResultRow>) getSearchSession();

		Collection<ResultRow> selectedRows;

		List<String> listId = null;
		try {
			selectedRows = ((SelectableFetchingBuffer<ResultRow>) searchSession.getBuffer()).getSelected();

			logger.trace("found "+selectedRows.size()+" selected rows");

			listId = new ArrayList<String>(selectedRows.size());

			for (ResultRow row:selectedRows)
				listId.add(row.getServiceId());

			logger.trace("found "+listId.size()+" ids");

			//TODO remove
			if (logger.isTraceEnabled()) logger.trace("selected ids: "+listId);

		} catch (Exception e) {
			logger.error("An error occured in getSelectedOccurrenceKeys" + e.getMessage());
			throw new SearchServiceException(e.getMessage());
		}

		return listId;
	}


//	@SuppressWarnings("unchecked")
//	protected List<String> getSelectedTaxonomyId() throws SearchServiceException
//	{
//		FetchingSession<TaxonomyRow> searchSession = (FetchingSession<TaxonomyRow>) getSearchSession();
//
//		Collection<TaxonomyRow> selectedRows;
//
//		List<String> listId = null;
//		try {
//			selectedRows = ((SelectableFetchingBuffer<TaxonomyRow>) searchSession.getBuffer()).getSelected();
//
//			logger.trace("found "+selectedRows.size()+" selected rows");
//
//			listId = new ArrayList<String>(selectedRows.size());
//
//			for (TaxonomyRow row:selectedRows){
//				listId.add(row.getServiceId());
//				logger.trace("add this id: "+row.getServiceId()+" to list");
//			}
//
//			logger.trace("found "+listId.size()+" id");
//
//			//TODO remove
//			if (logger.isTraceEnabled()) logger.trace("selected ids: "+listId);
//
//		} catch (Exception e) {
//			logger.error("An error occured in getSelectedOccurrenceKeys" + e.getMessage());
//			throw new SearchServiceException(e.getMessage());
//		}
//
//		return listId;
//	}
	
	@SuppressWarnings("unchecked")
	protected Map<String, String> getSelectedTaxonomyIdAndDataSource() throws SearchServiceException
	{
		FetchingSession<TaxonomyRow> searchSession = (FetchingSession<TaxonomyRow>) getSearchSession();

		HashMap<String, String> hashIdTaxonDataSource = null;
		
		Collection<TaxonomyRow> selectedRows;

//		List<String> listId = null;
		try {
			selectedRows = ((SelectableFetchingBuffer<TaxonomyRow>) searchSession.getBuffer()).getSelected();

			logger.trace("found "+selectedRows.size()+" selected rows");

			hashIdTaxonDataSource = new HashMap<String, String>(selectedRows.size());

			for (TaxonomyRow row:selectedRows){
				hashIdTaxonDataSource.put(row.getServiceId(), row.getDataProviderName());
				logger.trace("add this id: "+row.getServiceId()+" to list");
			}

			logger.trace("found "+hashIdTaxonDataSource.size()+" id");

			//TODO remove
			if (logger.isTraceEnabled()) logger.trace("selected ids: "+hashIdTaxonDataSource);

		} catch (Exception e) {
			logger.error("An error occured in getSelectedOccurrenceKeys" + e.getMessage());
			throw new SearchServiceException(e.getMessage());
		}

		return hashIdTaxonDataSource;
	}

	protected File generateCSVFile(List<String> ids, OccurrencesSaveEnum csvType) throws Exception
	{
		File csvFile = File.createTempFile("test", ".csv");
		logger.trace("outputfile "+csvFile.getAbsolutePath());

		FileWriter fileWriter = new FileWriter(csvFile);
		CSVWriter writer = new CSVWriter(fileWriter);

		SpeciesService taxonomyService = getSpeciesService();

		CloseableIterator<OccurrencePoint> source = taxonomyService.getOccurrencesByIds(ids);
		CloseableIterator<Occurrence> result = IteratorChainBuilder.buildOccurrenceConverter(source);

		CSVGenerator<Occurrence> csvGenerator = null;

		switch (csvType) {

			case OPENMODELLER: {

				OccurenceCSVConverterOpenModeller converterOpenModeller = new OccurenceCSVConverterOpenModeller();
				csvGenerator = new CSVGenerator<Occurrence>(result, converterOpenModeller, OccurenceCSVConverterOpenModeller.HEADER);

			}break;

			case STANDARD:{

				OccurenceCSVConverter converter = new OccurenceCSVConverter();
				csvGenerator = new CSVGenerator<Occurrence>(result, converter, OccurenceCSVConverter.HEADER);

			}break;
		}


		//		OccurenceCSVConverter converter = new OccurenceCSVConverter();
		//		ConversionIterator<Occurrence, List<String>> occurrencesConverter = new ConversionIterator<Occurrence, List<String>>(result, converter);

		while(csvGenerator.hasNext()) writer.writeLine(csvGenerator.next());

		fileWriter.close();

		return csvFile;
	}


	@Override
	public List<DataSourceModel> loadDataSourceList() throws SearchServiceException {
		logger.trace("loadDataSourceList... ");
//		System.out.println("loadDataSourceList loadDataSourceList loadDataSourceList loadDataSourceList loadDataSourceList ");
		List<DataSourceModel> listDS = null;

		try {
			DaoSession.initSessionDaoObjects(getASLSession()); //FIXME temporary?
			logger.trace("DAOs OK... ");
			System.out.println("DAOs OK");
			SpeciesService taxonomyService = getSpeciesService();
			System.out.println("Species Services OK");
			logger.trace("Species Services OK... ");
			listDS = taxonomyService.getPlugins();
			System.out.println("Plugins OK");
			logger.trace("Plugins OK");
			logger.trace("Return list plugin - size: "  +listDS.size());

		} catch (DatabaseServiceException e) {
			throw new SearchServiceException("Sorry, an error has occurred on the server while "+e.getMessage());
			
		} catch (Exception e) {
			throw new SearchServiceException("Sorry, an error has occurred on the server while "+e.getMessage());
		}

		return listDS;
	}


	@Override
	public ArrayList<CommonName> loadListCommonNameByRowId(String resultRowId) throws Exception {

		ArrayList<CommonName> listCommonName = new ArrayList<CommonName>();

		
		//TODO
//		try {
//			Dao<CommonName,Integer> commonDao = DaoSession.getCommonNameDAO(getASLSession());
//
//			QueryBuilder<CommonName, Integer> queryBuilder = commonDao.queryBuilder();
//
//			com.j256.ormlite.dao.CloseableIterator<CommonName> iterator = queryBuilder.where().eq(CommonName.REFERENCE_RESULTROW_ID, resultRowId).iterator();
//
//			while(iterator.hasNext()){
//				listCommonName.add(iterator.next());
//			}
//			
//			iterator.close();
//
//		} catch (Exception e) {
//			logger.error("Error on loadListCommonNameByRowId",e);
//			throw new Exception("Error on loadListCommonNameByRowId", e);
//		}

		return listCommonName;
	}

	@Override
	public List<Taxon> getParentsList(Taxon taxon) throws Exception {

//		ArrayList<Taxon> listParent = new ArrayList<Taxon>();
//
//		getParentListOfTaxon(taxon.getParent(), listParent);
//
//		return listParent;
		
		return null;
	}


	@Override
	public ArrayList<LightTaxonomyRow> loadListChildrenByParentId(String parentId) throws Exception {
		logger.trace("Load List Children By ParentId: " + parentId);
		//FOR DEBUG
//		System.out.println("Load List Children By ParentId: " + parentId);
		ArrayList<LightTaxonomyRow> listLightTaxonomyRow = new ArrayList<LightTaxonomyRow>();
		
		if(parentId==null || parentId.isEmpty()){
			logger.warn("parentId is null or empty ");
			return listLightTaxonomyRow;
		}
		
		try {

			SpeciesService taxonomyService = getSpeciesService();
			CloseableIterator<TaxonomyItem> streamIterator = taxonomyService.getTaxonChildrenByParentId(parentId);
			ASLSession session = getASLSession();
			TaxonomyItemConverter converter = new TaxonomyItemConverter(getASLSession());		
			
			Map<String, TaxonomyRow> mapChildren = SessionUtil.getHashMapChildrenTaxonomyCache(session);
			
			if(mapChildren==null){
				logger.trace("Cache taxa children doesn't exists into session, creating..");
				mapChildren = new HashMap<String, TaxonomyRow>();
			}
//			int i = 0;
			while (streamIterator.hasNext()) {
				TaxonomyItem tax = streamIterator.next();
				
				TaxonomyRow taxonomy = converter.convert(tax);
				
				if(mapChildren.get(taxonomy.getServiceId())==null){
					logger.trace("Taxonomy with service id: "+taxonomy.getServiceId()+" doesn't exists into Map Children, adding..");
					mapChildren.put(taxonomy.getServiceId(),taxonomy);
				}
				
				LightTaxonomyRow lightTaxRow  = ClusterStructuresForTaxonomyRow.convetTaxonomyRowToLigthTaxonomyRow(taxonomy);
				listLightTaxonomyRow.add(lightTaxRow);
			}

			SessionUtil.setHashMapChildrenTaxonomyCache(session, mapChildren);

			streamIterator.close();

		} catch (Exception e) {
			logger.error("Error on loadListChildByParentId ", e);
			throw new Exception("Error on loadListChildByParentId", e);
		}

		logger.trace("Return list children By ParentId "+parentId+"- with size: "+ listLightTaxonomyRow.size());

		return listLightTaxonomyRow;
	}

	@Override
	public List<JobTaxonomyModel> getListTaxonomyJobs() throws Exception {
		logger.trace("getListTaxonomyJobs... ");
//		System.out.println("getListSpeciesJobs...");
		List<JobTaxonomyModel> listJobs = new ArrayList<JobTaxonomyModel>();

		try{
			
			TaxonomyJobPersistence taxonomyJobDao = DaoSession.getTaxonomyJobDAO(getASLSession());
			Iterator<TaxonomyJob> iterator = taxonomyJobDao.getList().iterator();
		    
		    SpeciesService taxonomyService = getSpeciesService();
 
		    while(iterator!=null && iterator.hasNext()){
		    	TaxonomyJob job = iterator.next();
		    	
		     	logger.trace("get taxonomy job "+job.getId()+ " from service");
	//	     	System.out.println("get taxonomy job "+job.getId()+ " from service");
		     	
		    	try{
		    	
		    	Status statusResponse = taxonomyService.getTaxonomyJobById(job.getId());
		    	
		    	if(statusResponse!=null){
		    		logger.trace("statusResponse is not null..." + job.getId());
	//	    		System.out.println("statusResponse is not null..." + job.getId());
			    	JobTaxonomyModel jobSpeciesModel = TaxonomyJobUtil.convertJob(job, statusResponse, taxonomyJobDao);
			    	
			    	//FOR DEBUG
	//		    	System.out.println("getListTaxonomyJobs added list: "+jobSpeciesModel);
			    	logger.trace("added list jobTaxonomyId: "+job.getTaxonomyId() + " status "+job.getState());
			    	
			    	if(jobSpeciesModel!=null)
			    		listJobs.add(jobSpeciesModel);
		    	}
		    	else{
		    		logger.trace("TaxonomyJob statusResponse is null..." + job.getId());
	//	    		System.out.println("TaxonomyJob statusResponse is null..." + job.getId());
	//	    		System.out.println("statusResponse is null..." + job.getId());
		    		TaxonomyJobUtil.deleteTaxonomyJobById(job.getId(),taxonomyJobDao);
		    	}
		    	
		    	}catch (Exception e) {
					e.printStackTrace();
					logger.error("Error on getListSpeciesJobs ", e);
					throw new Exception("Error on getListSpeciesJobs", e);
				}
		    }
	    
		}catch (Exception e) {
			logger.error("Error on getListSpeciesJobs ", e);
			throw new Exception("Error on getListSpeciesJobs", e);
		}

	    return listJobs;

	}
	
	
	/**
	 * 
	 * @param taxonomyServiceId
	 * @param taxonomyName
	 * @param taxonomyRank
	 * @param dataSourceName
	 * @return
	 * @throws Exception
	 */
	@Override
	public JobTaxonomyModel createTaxonomyJobByChildren(String taxonomyServiceId, String taxonomyName, String taxonomyRank, String dataSourceName) throws Exception {
		//FIXED 20/05/2013
		logger.trace("Create job for taxonomy id: " + taxonomyServiceId);
//		System.out.println("Create job for taxonomy id: " + taxonomy.getServiceId());
		
		JobTaxonomyModel jobSpeciesModel = null;
		
		TaxonomyJobPersistence taxonomyJobDao = DaoSession.getTaxonomyJobDAO(getASLSession());
		
		SpeciesService taxonomyService = getSpeciesService();
		
		String speciesJobId = taxonomyService.createTaxonomyJobForDWCAByChildren(taxonomyServiceId);
		
		long submitTime = Calendar.getInstance().getTimeInMillis();

		String name = NormalizeString.lowerCaseUpFirstChar(taxonomyName) + " group";
		
		//STORE INTO DAO
		TaxonomyJob speciesJob = new TaxonomyJob(speciesJobId, DownloadState.PENDING.toString(), name, taxonomyName, dataSourceName, taxonomyRank, 0, submitTime, 0, taxonomyServiceId);
		taxonomyJobDao.insert(speciesJob);
		
		jobSpeciesModel = new JobTaxonomyModel(speciesJob.getId(), speciesJob.getDescriptiveName(), DownloadState.PENDING, null, taxonomyName, dataSourceName, taxonomyRank);
		
		Date submit = DateUtil.millisecondsToDate(speciesJob.getSubmitTime());
//		jobSpeciesModel.setStartTime(DateUtil.dateToDateFormatString(start));
		jobSpeciesModel.setSubmitTime(submit);
		jobSpeciesModel.setEndTime(null);
	
		return jobSpeciesModel;
	}
	

	@Override
	public JobTaxonomyModel createTaxonomyJobByIds(String search, List<DataSourceModel> dataSources) throws Exception {
		
		logger.trace("Create job ForDWCAByIds for: " + search);

//		List<String> listId = getSelectedTaxonomyId();
		
		Map<String, String> hashIdDs = getSelectedTaxonomyIdAndDataSource();
//		printId(listId);

//		System.out.println("Create job for taxonomy id: " + taxonomy.getServiceId());
		
		JobTaxonomyModel jobSpeciesModel = null;
		
		TaxonomyJobPersistence taxonomyJobDao = DaoSession.getTaxonomyJobDAO(getASLSession());
		
		SpeciesService taxonomyService = getSpeciesService();
		
		List<String> listId = new ArrayList<String>(hashIdDs.keySet());

		String speciesJobId = taxonomyService.createTaxonomyJobForDWCAByIds(listId);
		
		long submitTime = Calendar.getInstance().getTimeInMillis();

		
		String name = NormalizeString.lowerCaseUpFirstChar(search) + " - ";
		name += listId.size() + " ";
		name += listId.size()>1?"taxa":"taxon";
		
		String dataSourceName = "";
		
		for (String taxonId : listId) {
			if(!dataSourceName.contains(hashIdDs.get(taxonId))) //remove duplicate
				dataSourceName+=hashIdDs.get(taxonId) + ", ";
			
		}
		
//		String dataSourceName = "";
//		
//		for (DataSourceModel dataSource : dataSources) {
//			dataSourceName+=dataSource.getName() + ", ";
//		}
		
		if(dataSourceName.endsWith(", "))
			dataSourceName = dataSourceName.substring(0, dataSourceName.length()-2);
		
		/*String name = "Save DWCA with "+listId.size()+ " ";
		
		name += listId.size()>1?"items":"item";	
		name += " "+search;
		
		String dataSourceName = "";
		
		for (DataSourceModel dataSource : dataSources) {
			dataSourceName+=dataSource.getName() + ", ";
		}*/
		
		//STORE INTO DAO
		TaxonomyJob speciesJob = new TaxonomyJob(speciesJobId, DownloadState.PENDING.toString(), name, name, dataSourceName, "", 0, submitTime, 0, speciesJobId);
		taxonomyJobDao.insert(speciesJob);
		
		jobSpeciesModel = new JobTaxonomyModel(speciesJob.getId(), speciesJob.getDescriptiveName(), DownloadState.PENDING, null, name, dataSourceName, "");
		
		Date submit = DateUtil.millisecondsToDate(speciesJob.getSubmitTime());
//		jobSpeciesModel.setStartTime(DateUtil.dateToDateFormatString(start));
		jobSpeciesModel.setSubmitTime(submit);
		jobSpeciesModel.setEndTime(null);
	
		return jobSpeciesModel;
	}
	
	
	@Override
	public JobTaxonomyModel resubmitTaxonomyJob(String jobIdentifier) throws Exception {
		
		logger.trace("Resubmit taxonomy job for id: " + jobIdentifier);
		//FOR DEBUG
//		System.out.println("Resubmit taxonomy job for id: " + jobIdentifier);
		
		JobTaxonomyModel jobSpeciesModel = null;
		
		
		//Get Dao with list taxonomy jobs
		TaxonomyJobPersistence taxonomyJobDao = DaoSession.getTaxonomyJobDAO(getASLSession());
		
		CriteriaBuilder queryBuilder = taxonomyJobDao.getCriteriaBuilder();
		CriteriaQuery<Object> cq = queryBuilder.createQuery();
		Predicate pr1 =  queryBuilder.equal(taxonomyJobDao.rootFrom(cq).get(TaxonomyJob.ID_FIELD), jobIdentifier);
		cq.where(pr1);
		
		Iterator<TaxonomyJob> iterator = taxonomyJobDao.executeCriteriaQuery(cq).iterator();
		
		TaxonomyJob taxonomy;
		
		if(iterator.hasNext())
			 taxonomy = iterator.next();
		else
			return jobSpeciesModel;
		
		SpeciesService taxonomyService = getSpeciesService();
		
		//recover taxomyId
		String speciesJobId = taxonomyService.createTaxonomyJobForDWCAByChildren(taxonomy.getTaxonomyId());
		
		long submitTime = Calendar.getInstance().getTimeInMillis();

		String name = RESUBMIT + ": " +NormalizeString.lowerCaseUpFirstChar(taxonomy.getDescriptiveName());
		
		//STORE INTO DAO
		TaxonomyJob speciesJob = new TaxonomyJob(speciesJobId, DownloadState.PENDING.toString(), name, taxonomy.getDescriptiveName(), taxonomy.getDataSourceName(), taxonomy.getRank(), 0, submitTime, 0, taxonomy.getTaxonomyId());
		taxonomyJobDao.insert(speciesJob);
		
		jobSpeciesModel = new JobTaxonomyModel(speciesJob.getId(), speciesJob.getDescriptiveName(), DownloadState.PENDING, null, taxonomy.getDescriptiveName(), taxonomy.getDataSourceName(), taxonomy.getRank());
		
		Date submit = DateUtil.millisecondsToDate(speciesJob.getSubmitTime());
//		jobSpeciesModel.setStartTime(DateUtil.dateToDateFormatString(start));
		jobSpeciesModel.setSubmitTime(submit);
		jobSpeciesModel.setEndTime(null);
	
		return jobSpeciesModel;
	}

	@Override
	public boolean cancelTaxonomyJob(String jobIdentifier) throws Exception {
	
		try{
	
			SpeciesService taxonomyService = getSpeciesService();

			//REMOVE JOB ON THE SERVICE
			taxonomyService.cancelTaxonomyJobById(jobIdentifier);

			TaxonomyJobPersistence speciesJobDao = DaoSession.getTaxonomyJobDAO(getASLSession());

			int count = TaxonomyJobUtil.deleteTaxonomyJobById(jobIdentifier, speciesJobDao);
			
			if(count==1)
				return true;

		}catch (Exception e) {
			logger.error("Error on cancel taxonomy job ", e);
			throw new Exception("Error on cancel taxonomy job", e);
		}
		
		return false;
	}

	@Override
	public boolean saveTaxonomyJob(String jobIdentifier, String destinationFolderId, String fileName, String scientificName, String dataSourceName) throws Exception {
		
		logger.trace("saveSpeciesJob jobId: "+jobIdentifier+" destinationFolderId: "+destinationFolderId+" fileName: "+fileName);
//		System.out.println("saveSpeciesJob jobId: "+jobIdentifier+" destinationFolderId: "+destinationFolderId+" fileName: "+fileName);
		
		try {
			
			SpeciesService taxonomyService = getSpeciesService();
			String mimeType = "application/zip";
			
			InputStream inputStream = taxonomyService.getTaxonomyJobFileById(jobIdentifier);
			
			if(inputStream!=null){
				Workspace workspace = GetWorkspaceUtil.getWorskspace(getASLSession());
				logger.trace("input stream is not null");
//				System.out.println("input stream is not null");
	
				WorkspaceFolder folder = (WorkspaceFolder) workspace.getItem(destinationFolderId);
				fileName = WorkspaceUtil.getUniqueName(fileName, folder);
				folder.createExternalFileItem(fileName,"Taxonomy job generated files", mimeType, inputStream);
				logger.trace("Save file with taxonomy was completed");
			}
			else{
				
				logger.trace("input stream is null");
				return false;
			}
				

			return true;

		} catch (Exception e) {

			logger.error("An error occurred saving the generated file into the workspace",e);
			throw new SearchServiceException(e.getMessage());
		}
		
	}
	
	@Override
	public boolean saveTaxonomyJobError(String jobIdentifier, String destinationFolderId, String fileName, String scientificName, String dataSourceName) throws Exception {
		
		logger.trace("saveSpeciesJob error jobId: "+jobIdentifier+" destinationFolderId: "+destinationFolderId+" fileName: "+fileName);
//		System.out.println("saveSpeciesJob jobId: "+jobIdentifier+" destinationFolderId: "+destinationFolderId+" fileName: "+fileName);
		
		try {
			
			SpeciesService taxonomyService = getSpeciesService();
			String mimeType = "text/plain";
			
			InputStream inputStream = taxonomyService.getTaxonomyJobErrorFileById(jobIdentifier);
			
			if(inputStream!=null){
				Workspace workspace = GetWorkspaceUtil.getWorskspace(getASLSession());
				logger.trace("input stream is not null");
//				System.out.println("input stream is not null");
	
				WorkspaceFolder folder = (WorkspaceFolder) workspace.getItem(destinationFolderId);
				fileName = WorkspaceUtil.getUniqueName(fileName, folder);
				folder.createExternalFileItem(fileName,"Report errors on taxonomy job", mimeType, inputStream);
				logger.trace("Save report file with errors occurred was completed");
			}
			else{
				
				logger.trace("input stream is null");
				return false;
			}
				

			return true;

		} catch (Exception e) {

			logger.error("An error occurred saving the generated file into the workspace",e);
			throw new SearchServiceException(e.getMessage());
		}
		
	}
	
	@Override
	public List<LightTaxonomyRow> retrieveTaxonomyByIds(List<String> ids) throws Exception{
		logger.trace("retrieveTaxonomyByIds ids size: " + ids.size());
		
//		System.out.println("retrieveTaxonomyByIds ids size: " + ids.size() + " first id " + ids.get(0));
		
		List<LightTaxonomyRow> listLightTaxonomyRow = new ArrayList<LightTaxonomyRow>();
		try {
			
			SpeciesService taxonomyService = getSpeciesService();
			StreamExtend<String> convert = new StreamExtend<String>(ids.iterator());
			
			CloseableIterator<TaxonomyItem> streamIterator = taxonomyService.retrieveTaxonomyById(convert);
			
			ASLSession session = getASLSession();
			TaxonomyItemConverter taxonomyItemConverter = new TaxonomyItemConverter(session);		
			
			Map<String, TaxonomyRow> mapTaxonomyIds = SessionUtil.getHashMapTaxonomyByIdsCache(session);
			
			if(mapTaxonomyIds==null){
				logger.trace("Cache taxa ByIds doesn't exists into session, creating..");
				mapTaxonomyIds = new HashMap<String, TaxonomyRow>();
			}
			
			while (streamIterator.hasNext()) {
				TaxonomyItem tax = streamIterator.next();
				
				TaxonomyRow taxonomy = taxonomyItemConverter.convert(tax);
				
				if(mapTaxonomyIds.get(taxonomy.getServiceId())==null){
					logger.trace("Taxonomy with service id: "+taxonomy.getServiceId()+" doesn't exists into Map Taxonomy Ids, adding..");
					mapTaxonomyIds.put(taxonomy.getServiceId(),taxonomy);
				}
				
				LightTaxonomyRow lightTaxRow  = ClusterStructuresForTaxonomyRow.convetTaxonomyRowToLigthTaxonomyRow(taxonomy);
				listLightTaxonomyRow.add(lightTaxRow);
			}

			SessionUtil.setHashMapTaxonomyByIdsCache(session, mapTaxonomyIds);

			streamIterator.close();

		} catch (Exception e) {

			e.printStackTrace();
			logger.error("An error retrieve taxonomy by Id",e);
//			throw new Exception(e.getMessage());
		}
		
		return listLightTaxonomyRow;
		
	}
	
	@Override
	public List<LightTaxonomyRow> retrieveSynonymsByRefId(String refId) throws Exception{
		logger.trace("retrieveSynonymsById id: " + refId);
		
		//FOR DEBUG
//		System.out.println("retrieveSynonymsById id: " + refId);
		
		List<LightTaxonomyRow> listLightTaxonomyRow = new ArrayList<LightTaxonomyRow>();
		try {
			
			SpeciesService taxonomyService = getSpeciesService();

			CloseableIterator<TaxonomyItem> streamIterator = taxonomyService.retrieveSynonymsById(refId);
			
			ASLSession session = getASLSession();
			TaxonomyItemConverter taxonomyItemConverter = new TaxonomyItemConverter(getASLSession());
			
			Map<String, TaxonomyRow> mapSysnosyms = SessionUtil.getHashMapSynonymsTaxonomyCache(getASLSession());
			
			if(mapSysnosyms==null){
				logger.trace("Cache synonyms doesn't exists into session, creating..");
				mapSysnosyms = new HashMap<String, TaxonomyRow>();
			}
			
//			int i = 1;
			while (streamIterator.hasNext()) {
				TaxonomyItem tax = streamIterator.next();
				
				TaxonomyRow taxonomy = taxonomyItemConverter.convert(tax);
				
				if(mapSysnosyms.get(taxonomy.getServiceId())==null){
					logger.trace("Taxonomy with service id: "+taxonomy.getServiceId()+" doesn't exists into Map Synonyms, adding..");
					mapSysnosyms.put(taxonomy.getServiceId(),taxonomy);
				}
				
				LightTaxonomyRow lightTaxRow  = ClusterStructuresForTaxonomyRow.convetTaxonomyRowToLigthTaxonomyRow(taxonomy);
				listLightTaxonomyRow.add(lightTaxRow);
			}

			SessionUtil.setHashMapSynonymsTaxonomyCache(session, mapSysnosyms);

			streamIterator.close();

		} catch (Exception e) {

			e.printStackTrace();
			logger.error("An error retrieve synonyms by Id",e);
		}
		
		return listLightTaxonomyRow;
		
	}
	
	
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public List<JobOccurrencesModel> createOccurrencesJob(List<JobOccurrencesModel> listJobOccurrenceModel, SaveFileFormat saveFileFormat, OccurrencesSaveEnum csvType, boolean isByDataSource, int expectedOccurrence) throws Exception {
		logger.trace("createOccurencesJobFromSelection...");
		
		List<JobOccurrencesModel> listResultJobModel = new ArrayList<JobOccurrencesModel>();
		@SuppressWarnings("unchecked")
		FetchingSession<ResultRow> searchSession = (FetchingSession<ResultRow>) getSearchSession();
		SpeciesService taxonomyService = getSpeciesService();
		ASLSession aslSession = getASLSession();
		String dataSourceAsXml = "";
		
		try {
			
			List<String> keys = null;
			//Get Dao with list occurrences jobs
			OccurrenceJobPersistence occurrencesJobDao = DaoSession.getOccurrencesJobDAO(aslSession);

			if(!isByDataSource){  //NOT IS BY DATASOURCE - CREATE ONE JOB
				
				keys = OccurrenceJobUtil.getListOfSelectedKey(searchSession);
				
				if(listJobOccurrenceModel!=null && listJobOccurrenceModel.get(0)!=null){ //IN THIS CASE - THERE IS ONE JOBMODEL
					
					List<DataSource> dataSourceList = listJobOccurrenceModel.get(0).getDataSources();
					
					//ADDED DATA SOURCE LIST

					listResultJobModel.add(OccurrenceJobUtil.createOccurrenceJobOnServiceByKeys(listJobOccurrenceModel.get(0), taxonomyService, occurrencesJobDao, keys, dataSourceList, saveFileFormat, csvType, expectedOccurrence));
				}
					
			}
			
			else{ //IS BY DATASOURCE - CREATE MORE JOB, ONE FOR EACH DATASOURCE
				
				for (JobOccurrencesModel jobModel : listJobOccurrenceModel) { //IN THIS CASE - FOR EACH JOBMODEL THERE IS ONE DATASOURCE 
					
					dataSourceAsXml = "";
					
					if(jobModel.getDataSources()!=null && jobModel.getDataSources().get(0)!=null){
						
						//recover keys
						DataSource dataSource = jobModel.getDataSources().get(0);
						
						OccurrenceKeys occKey = OccurrenceJobUtil.getListOfSelectedKeyByDataSource(dataSource.getName(), aslSession);
						
						List<DataSource> dataSourceList = jobModel.getDataSources();
						
						//ADDED DATA SOURCE LIST
						
						listResultJobModel.add(OccurrenceJobUtil.createOccurrenceJobOnServiceByKeys(jobModel, taxonomyService, occurrencesJobDao, occKey.getListKey(), dataSourceList, saveFileFormat, csvType, occKey.getTotalOccurrence()));
					}
				}
				
			}
			
			
		return listResultJobModel;
		
		} catch (Exception e) {
			logger.error("An error occurred in createOccurencesJobFromSelection", e);
			//FOR DEBUG
//			System.out.println("Exception in createOccurrencesJob");
			e.printStackTrace();
			throw new Exception(e.getMessage());
		}
		
	}
	
	@Override
	public List<JobOccurrencesModel> resubmitOccurrencesJob(String jobIdentifier) throws Exception {
		logger.trace("createOccurencesJobFromSelection...");
		
		List<JobOccurrencesModel> listResultJobModel = new ArrayList<JobOccurrencesModel>();
		SpeciesService taxonomyService = getSpeciesService();
		ASLSession aslSession = getASLSession();
		List<String> keys = null;
		
		try {
			
			//Get Dao with list occurrences jobs
			OccurrenceJobPersistence occurrencesJobDao = DaoSession.getOccurrencesJobDAO(aslSession);
			
			CriteriaBuilder queryBuilder = occurrencesJobDao.getCriteriaBuilder();
			CriteriaQuery<Object> cq = queryBuilder.createQuery();
			Predicate pr1 =  queryBuilder.equal(occurrencesJobDao.rootFrom(cq).get(OccurrencesJob.ID_FIELD), jobIdentifier);
			cq.where(pr1);
			
			Iterator<OccurrencesJob> iterator = occurrencesJobDao.executeCriteriaQuery(cq).iterator();

			OccurrencesJob job;
			
			if(iterator.hasNext())
				 job = iterator.next();
			else
				return listResultJobModel;
			
			
			//recover keys
			keys = OccurrenceJobUtil.revertListKeyFromStoredXMLString(job.getResultRowKeysAsXml());
			
			//recover file format
			SaveFileFormat fileFormat = OccurrenceJobUtil.converFileFormat(job.getFileFormat());
			
			//recover csv type
			OccurrencesSaveEnum csvType = OccurrenceJobUtil.convertCsvType(job.getCsvType());
			
			String name = RESUBMIT + ": "+job.getName();
			
			JobOccurrencesModel jobModel = new JobOccurrencesModel("",name, job.getScientificName(), job.getDataSources(), fileFormat, csvType, job.isByDataSource());
			jobModel.setTotalOccurrences(job.getExpectedOccurrence());
			
			listResultJobModel.add(OccurrenceJobUtil.createOccurrenceJobOnServiceByKeys(jobModel, taxonomyService, occurrencesJobDao, keys, job.getDataSources(), fileFormat, csvType, jobModel.getTotalOccurrences()));
				
		} catch (Exception e) {
			logger.error("An error occurred in createOccurencesJobFromSelection", e);
			//FOR DEBUG
//			System.out.println("Exception in createOccurrencesJob");
			e.printStackTrace();
			throw new Exception(e.getMessage());
		}
		
		return listResultJobModel;
		
	}
	
	@Override
	public List<JobOccurrencesModel> getListOccurrencesJob() throws Exception{
		logger.trace("getListOccurencesJob... ");
		// System.out.println("getListSpeciesJobs...");
		List<JobOccurrencesModel> listJobs = new ArrayList<JobOccurrencesModel>();

		try {

			OccurrenceJobPersistence occurrencesJobDao = DaoSession.getOccurrencesJobDAO(getASLSession());

			Iterator<OccurrencesJob> iterator = occurrencesJobDao.getList().iterator();
			
			SpeciesService taxonomyService = getSpeciesService();

			while (iterator!=null && iterator.hasNext()) {
				OccurrencesJob job = iterator.next();
				
			 	Status statusResponse = taxonomyService.getOccurrenceJobById(job.getId());
			 	
			 	logger.trace("get occurrence job "+job.getId()+ " from service");
			 	
			 	//FOR DEBUG
//				System.out.println("get occurrence job "+job.getId()+ " from service");
			 	
			 	try{
			 		
					if(statusResponse!=null){
			    		logger.trace("statusResponse of occurrence job is not null..." + job.getId());
//			    		System.out.println("statusResponse is not null..." + job.getId());
				    	JobOccurrencesModel jobOccurrenceModel = OccurrenceJobUtil.convertJob(job, statusResponse, occurrencesJobDao);
				   
				    	if(jobOccurrenceModel!=null){
					    	//FOR DEBUG
					    	logger.trace("added list jobOccurrenceId: "+jobOccurrenceModel.getJobIdentifier() + " status "+jobOccurrenceModel.getDownloadState());
					    	//FOR DEBUG
//					    	System.out.println("added list jobOccurrenceId: "+jobOccurrenceModel.getJobIdentifier() + " status "+jobOccurrenceModel.getDownloadState());
//					    	System.out.println("keys: "+job.getResultRowKeysAsXml());
//							System.out.println("dataSources: "+job.getDataSourcesAsXml());
				    		
				    		listJobs.add(jobOccurrenceModel);
				    	}
					}
			    	else{
			    		logger.trace("statusResponse of occurrence job is null..." + job.getId());
			     		logger.trace("delete job ..." + job.getId());
//			    		System.out.println("statusResponse is null..." + job.getId());
			    		OccurrenceJobUtil.deleteOccurrenceJobById(job.getId(),occurrencesJobDao);
			    	}
		    	
		    	}catch (Exception e) {
					e.printStackTrace();
					logger.error("Error on getListOccurencesJob ", e);
					throw new Exception("Error on getListOccurencesJob", e);
				}
			}

//			if(iterator!=null)
//				iterator.close();
//			else
//				logger.warn("Warn on getListOccurrencesJob, iterator  is null");
			
		} catch (Exception e) {
			logger.error("Error on get iterator "+e, e);
		}
		
		return listJobs;

	}
	
	@Override
	public boolean saveOccurrenceJob(JobOccurrencesModel jobModel, String destinationFolderId, String fileName, String scientificName, String dataSourceName) throws Exception {
		
		logger.trace("saveOccurrenceJob jobId: "+jobModel.getJobIdentifier()+" destinationFolderId: "+destinationFolderId+" fileName: "+fileName + " file format: "+jobModel.getFileFormat());
//		System.out.println("saveSpeciesJob jobId: "+jobIdentifier+" destinationFolderId: "+destinationFolderId+" fileName: "+fileName);
		
		try {
			
			SpeciesService taxonomyService = getSpeciesService();

			String mimeType = null;

			switch (jobModel.getFileFormat()) {
				case CSV: {
					mimeType = "text/csv";
				} break;
				case DARWIN_CORE:{
					mimeType = "application/xhtml+xml";
				} break;
			}
			
			InputStream inputStream = taxonomyService.getOccurrenceJobFileById(jobModel.getJobIdentifier());
			
			if(inputStream!=null){
				Workspace workspace = GetWorkspaceUtil.getWorskspace(getASLSession());
				logger.trace("input stream is not null");
//				System.out.println("input stream is not null");
	
				WorkspaceFolder folder = (WorkspaceFolder) workspace.getItem(destinationFolderId);
				fileName = WorkspaceUtil.getUniqueName(fileName, folder);
				folder.createExternalFileItem(fileName,"Occurrence job generated files", mimeType, inputStream);
				logger.trace("Save file with occurrences was completed");
			}
			else{
				
				logger.trace("input stream is null");
				return false;
			}
				

			return true;

		} catch (Exception e) {

			logger.error("An error occurred saving the generated file into the workspace",e);
			throw new SearchServiceException(e.getMessage());
		}
		
	}
	
	@Override
	public boolean saveOccurrenceJobError(JobOccurrencesModel jobModel, String destinationFolderId, String fileName, String scientificName, String dataSourceName) throws Exception {
		
		logger.trace("saveOccurrenceJobError jobId: "+jobModel.getJobIdentifier()+" destinationFolderId: "+destinationFolderId+" fileName: "+fileName + " file format: "+jobModel.getFileFormat());
//		System.out.println("saveSpeciesJob jobId: "+jobIdentifier+" destinationFolderId: "+destinationFolderId+" fileName: "+fileName);
		
		try {
			
			SpeciesService taxonomyService = getSpeciesService();

//			String mimeType = null;
//			switch (jobModel.getFileFormat()) {
//				case CSV: {
//					mimeType = "text/csv";
//				} break;
//				case DARWIN_CORE:{
//					mimeType = "application/xhtml+xml";
//				} break;
//			}
			
			String mimeType = "text/plain";
			
			InputStream inputStream = taxonomyService.getOccurrenceJobErrorFileById(jobModel.getJobIdentifier());
			
			if(inputStream!=null){
				Workspace workspace = GetWorkspaceUtil.getWorskspace(getASLSession());
				logger.trace("input stream is not null");
//				System.out.println("input stream is not null");
	
				WorkspaceFolder folder = (WorkspaceFolder) workspace.getItem(destinationFolderId);
				fileName = WorkspaceUtil.getUniqueName(fileName, folder);
				folder.createExternalFileItem(fileName,"Report errors occurred on occurrence job", mimeType, inputStream);
				logger.trace("Save report file with errors occurred was completed");
			}
			else{
				
				logger.trace("input stream is null");
				return false;
			}
				

			return true;

		} catch (Exception e) {

			logger.error("An error occurred saving the generated file into the workspace",e);
			throw new SearchServiceException(e.getMessage());
		}
		
	}
	
	@Override
	public boolean cancelOccurrenceJob(String jobIdentifier) throws Exception {
	
		try{
	
			SpeciesService taxonomyService = getSpeciesService();

			//REMOVE JOB ON THE SERVICE
			taxonomyService.cancelTaxonomyJobById(jobIdentifier);

			OccurrenceJobPersistence occurrenceJobDAO = DaoSession.getOccurrencesJobDAO(getASLSession());

			int count = OccurrenceJobUtil.deleteOccurrenceJobById(jobIdentifier, occurrenceJobDAO);
			
			if(count==1)
				return true;

		}catch (Exception e) {
			logger.error("Error on cancel occurrence job ", e);
			throw new Exception("Error on cancel occurrence job", e);
		}
		
		return false;
	}

	@Override
	public ClusterStructuresForResultRow loadStructuresForResultRowClustering() throws Exception {

		int countSelectedRow = countOfSelectedRow();
		boolean isReduced = false;
		int totalRow = countSelectedRow;
		
		ASLSession session = getASLSession();
		HashMap<String, ClusterCommonNameDataSourceForResultRow> hashCluster = SessionUtil.getCurrentClusterCommonNameForResultRow(session);
		
		//Reset cluster for common name
		if(hashCluster!=null)
			SessionUtil.setCurrentClusterCommonNameForResultRow(session, null);
		
		
		//LIMIT NUMBER OF ITEMS TO ConstantsSpeciesDiscovery.LIMIT_ITEM_DETAILS
		if(countSelectedRow>ConstantsSpeciesDiscovery.RESULT_ROW_LIMIT_ITEM_DETAILS)
			countSelectedRow = ConstantsSpeciesDiscovery.RESULT_ROW_LIMIT_ITEM_DETAILS; 
		
		ResultFilter filter = new ResultFilter(false, true, true);
		
		SearchResult<ResultRow> searchResults = getSearchResultRows(0, countSelectedRow, filter, true);

		ClusterStructuresForResultRow cluster = new ClusterStructuresForResultRow(searchResults,isReduced, totalRow);
		
		
		//TODO USE THREAD?
		ManagerClusterCommonNameDataSourceForResultRow manager = new ManagerClusterCommonNameDataSourceForResultRow(cluster.getHashClusterScientificNameResultRowServiceID(), cluster.getHashResult());
		
		SessionUtil.setCurrentClusterCommonNameForResultRow(getASLSession(), manager.getHashClusterCommonNameDataSource());

		//THIS OBJECT IS NOT USED ON CLIENT
		cluster.setHashResult(null);
		
		return cluster;
		
	}
	
	@Override
	public ClusterCommonNameDataSourceForResultRow loadClusterCommonNameForResultRowByScientificName(String scientificName) throws Exception {

		logger.trace("loadClusterCommonNameForResultRowByScientificName for scientific name: "+scientificName);
		
		HashMap<String, ClusterCommonNameDataSourceForResultRow> hashCluster = SessionUtil.getCurrentClusterCommonNameForResultRow(getASLSession());
		
		
		if(hashCluster==null){
			logger.warn("Error in loadClusterCommonNameForResultRowByScientificName, hashCluster was not found in session");
			return null;
			
		}
		
		ClusterCommonNameDataSourceForResultRow cluster = hashCluster.get(scientificName);
		
		
		if(cluster==null){
			logger.warn("Error in loadClusterCommonNameForResultRowByScientificName, cluster was not found in session");
			return null;
		}
		
		return cluster;
	}
	
	
	@Override
	public List<DataSource> loadDataSourceForResultRow(boolean selected, boolean distinct) throws Exception {

		ResultRowPersistence daoResultRow = null;
		ASLSession session = getASLSession();
		List<DataSource> listDataSource = new ArrayList<DataSource>();

		try {
			daoResultRow = DaoSession.getResultRowDAO(session);
			
			EntityManager em = daoResultRow.createNewManager();
			List<String> listDN = new ArrayList<String>();
			try {
				String selectString = "select ";
				if(distinct)
					selectString+= "distinct ";
				
				Query query = em.createQuery(selectString + "t."+ResultRow.DATASOURCE_NAME+" from ResultRow t where t.selected = "+selected );

				listDN = query.getResultList();
				
			} finally {
				em.close();
			}

			for (String dataSourceName : listDN) {
				//FOR DEBUG
//				System.out.println("row datasource #################################" +resultRow);	
				listDataSource.add(new DataSource(dataSourceName, dataSourceName));
			}
				
//			distinctQueryBuilder.clear();
			
		} catch (Exception e) {
			logger.error("Error in loadDataSourceForResultRow " +e.getMessage(), e);
			throw new Exception("Error in loadDataSourceForResultRow " + e.getMessage(), e);
		}

		return listDataSource;
		
	}

	
	@Override
	public ClusterStructuresForTaxonomyRow loadStructuresForTaxonomyClustering() throws Exception {

		int countSelectedRow = countOfSelectedRow();
		boolean isReduced = false;
		int totalRow = countSelectedRow;

		
		ASLSession session = getASLSession();
		HashMap<String, ClusterCommonNameDataSourceForTaxonomyRow> hashCluster = SessionUtil.getCurrentClusterCommonNameForTaxonomyRow(session);
		
		//Reset cluster for common name
		if(hashCluster!=null)
			SessionUtil.setCurrentClusterCommonNameForTaxonomyRow(session, null);
		
		
		HashMap<String, TaxonomyRow> mapOldChildren = SessionUtil.getHashMapChildrenTaxonomyCache(session);
		//Reset list children to last clustering
		if(mapOldChildren!=null)
			SessionUtil.setHashMapChildrenTaxonomyCache(session, null);
		
		HashMap<String, TaxonomyRow> mapSynonyms = SessionUtil.getHashMapSynonymsTaxonomyCache(session);
		//Reset list synonyms to last clustering
		if(mapSynonyms!=null)
			SessionUtil.setHashMapSynonymsTaxonomyCache(session, null);
		
		HashMap<String, TaxonomyRow> mapTaxonomyIds = SessionUtil.getHashMapTaxonomyByIdsCache(session);
		//Reset list synonyms to last clustering
		if(mapTaxonomyIds!=null)
			SessionUtil.setHashMapTaxonomyByIdsCache(session, null);
		
		
		
		//LIMIT NUMBER OF ITEMS TO ConstantsSpeciesDiscovery.LIMIT_ITEM_DETAILS
		if(countSelectedRow>ConstantsSpeciesDiscovery.TAXONOMY_LIMIT_ITEMS_DETAILS){
			countSelectedRow = ConstantsSpeciesDiscovery.TAXONOMY_LIMIT_ITEMS_DETAILS;
			isReduced = true;
		}

		ResultFilter filter = new ResultFilter(false, true, true);
		
		SearchResult<TaxonomyRow> searchResults = getSearchTaxonomyRow(0, countSelectedRow, filter, true);
		
		ClusterStructuresForTaxonomyRow cluster = new ClusterStructuresForTaxonomyRow(searchResults,isReduced, totalRow);
		
		
		//TODO USE THREAD?
		ManagerClusterCommonNameDataSourceForTaxonomyRow manager = new ManagerClusterCommonNameDataSourceForTaxonomyRow(cluster.getHashClusterScientificNameTaxonomyRowServiceID(), cluster.getHashResult());
		
		SessionUtil.setCurrentClusterCommonNameForTaxonomyRow(session, manager.getHashClusterCommonNameDataSource());

		//THIS OBJECT IS NOT USED ON CLIENT
		cluster.setHashResult(null);
		
		return cluster;
		
	}

	@Override
	public boolean changeStatusOccurrenceJob(String jobIdentifier, DownloadState state) throws Exception {

		OccurrenceJobPersistence occurrenceJobDAO = DaoSession.getOccurrencesJobDAO(getASLSession());

		int count = OccurrenceJobUtil.changeStatusOccurrenceJobById(jobIdentifier, state, occurrenceJobDAO);
		
		if(count==1)
			return true;
		
		return false;
		
	}

	@Override
	public boolean changeStatusTaxonomyJob(String jobIdentifier, DownloadState state) throws Exception {
		
		TaxonomyJobPersistence taxonomyJobDAO = DaoSession.getTaxonomyJobDAO(getASLSession());

		int count = TaxonomyJobUtil.changeStatusTaxonomyJobById(jobIdentifier, state, taxonomyJobDAO);
		
		if(count==1)
			return true;
		
		return false;
		
	}

	/* (non-Javadoc)
	 * @see org.gcube.portlets.user.speciesdiscovery.client.rpc.TaxonomySearchService#isAvailableTaxonomyJobReportError(java.lang.String)
	 */
	@Override
	public boolean isAvailableTaxonomyJobReportError(String jobIdentifier) throws Exception {
		
		logger.trace("isAvailableTaxonomyJobReportError jobId: "+jobIdentifier);
		try {
			
			SpeciesService taxonomyService = getSpeciesService();

			return taxonomyService.isAvailableTaxonomyJobErrorFileById(jobIdentifier);
			
		} catch (Exception e) {
			logger.error("An error occurred getting error (taxonomy) file for jobid "+jobIdentifier,e);
			return false;
		}
	}

	/* (non-Javadoc)
	 * @see org.gcube.portlets.user.speciesdiscovery.client.rpc.TaxonomySearchService#isAvailableOccurrenceJobReportError(java.lang.String)
	 */
	@Override
	public boolean isAvailableOccurrenceJobReportError(String jobIdentifier) throws Exception {
		
		logger.trace("isAvailableOccurrenceJobReportError jobId: "+jobIdentifier);
		try {
			
			SpeciesService taxonomyService = getSpeciesService();

			return taxonomyService.isAvailableOccurrenceJobErrorFileById(jobIdentifier);
			
		} catch (Exception e) {
			logger.error("An error occurred getting error (occurrence) file for jobid "+jobIdentifier,e);
			return false;
		}
	}
	
	@Override
	public String getLastQuery(){
		logger.trace("getLastQuery...");
		ASLSession session = getASLSession();
		return SessionUtil.getCurrentQuery(session);
	}

	/* (non-Javadoc)
	 * @see org.gcube.portlets.user.speciesdiscovery.client.rpc.TaxonomySearchService#loadClusterCommonNameForTaxonomyRowByScientificName(java.lang.String)
	 */
	@Override
	public ClusterCommonNameDataSourceForTaxonomyRow loadClusterCommonNameForTaxonomyRowByScientificName(String scientificName) {
	
		System.out.println("loadClusterCommonNameForTaxonomyRowByScientificName for scientific name: "+scientificName);
		logger.trace("loadClusterCommonNameForTaxonomyRowByScientificName for scientific name: "+scientificName);
		
		HashMap<String, ClusterCommonNameDataSourceForTaxonomyRow> hashCluster = SessionUtil.getCurrentClusterCommonNameForTaxonomyRow(getASLSession());
		
		
		if(hashCluster==null){
			logger.warn("Error in loadClusterCommonNameForTaxonomyRowByScientificName, hashCluster was not found in session");
			return null;
			
		}
		
		ClusterCommonNameDataSourceForTaxonomyRow cluster = hashCluster.get(scientificName);
		
		
		if(cluster==null){
			logger.warn("Error in loadClusterCommonNameForTaxonomyRowByScientificName, cluster was not found in session");
			return null;
		}
		
		return cluster;
	}
}
