package eu.dnetlib.enabling.ui.server;

import java.io.InputStream;
import java.io.StringReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.io.SAXReader;
import org.springframework.beans.factory.annotation.Required;

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

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.enabling.ui.common.beans.AllDSInfo;
import eu.dnetlib.enabling.ui.common.beans.BlackBoardInfo;
import eu.dnetlib.enabling.ui.common.beans.CountryInfo;
import eu.dnetlib.enabling.ui.common.beans.DHNInfo;
import eu.dnetlib.enabling.ui.common.beans.IndexInfo;
import eu.dnetlib.enabling.ui.common.beans.MDStoreInfo;
import eu.dnetlib.enabling.ui.common.beans.RepositoryDetailsInfo;
import eu.dnetlib.enabling.ui.common.beans.RepositoryStatusInfo;
import eu.dnetlib.enabling.ui.common.beans.ServiceInfo;
import eu.dnetlib.enabling.ui.common.beans.UnibiServiceInfo;
import eu.dnetlib.enabling.ui.common.services.LookupService;
import eu.dnetlib.enabling.ui.common.services.MyGwtException;
import eu.dnetlib.enabling.ui.server.auth.AuthenticationManager;
import eu.dnetlib.enabling.ui.server.auth.Principal;
import eu.dnetlib.enabling.ui.server.session.SessionManager;
import eu.dnetlib.miscutils.datetime.DateUtils;

public class LookupServlet extends RemoteServiceServlet implements LookupService {
	private static final long serialVersionUID = 228602254444074392L;
	private ServiceLocator<ISLookUpService> isLookUpLocator;
	private SessionManager sessionManager;
	private AuthenticationManager authenticationManager;
	private String smokepingBaseUrl;
	private String muninBaseUrl;
	private String mdAggrFormat;
	private String mdHarvFormat;
	private String aggregatorPolicyAction;

	private static final Log log = LogFactory.getLog(LookupServlet.class); // NOPMD by marko on 11/24/08 5:02 PM

	public LookupServlet() {
		super();
	}

	public ServiceLocator<ISLookUpService> getIsLookUpLocator() {
		return isLookUpLocator;
	}

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

	public String getSmokepingBaseUrl() {
		return smokepingBaseUrl;
	}

	@Required
	public void setSmokepingBaseUrl(String smokepingBaseUrl) {
		this.smokepingBaseUrl = smokepingBaseUrl;
	}

	public String getMuninBaseUrl() {
		return muninBaseUrl;
	}

	@Required
	public void setMuninBaseUrl(String muninBaseUrl) {
		this.muninBaseUrl = muninBaseUrl;
	}

	@Override
	public List<String> executeQuery(String query) {
		log.debug("Executing xquery: " + query);

		List<String> res = null;
		try {
			res = isLookUpLocator.getService().quickSearchProfile(query);
		} catch (Exception e) {
			return new ArrayList<String>();
		}
		if (res == null)
			return new ArrayList<String>();
		return res;
	}

	@Override
	public List<ServiceInfo> listServices() {
		return _listServices(null);
	}

	private List<ServiceInfo> _listServices(String DHNid) {
		String query = "";
		if ((DHNid == null) || (DHNid.isEmpty())) {
			query = "for $x in collection('/db/DRIVER/ServiceResources/') " + "order by $x/RESOURCE_PROFILE/HEADER/RESOURCE_TYPE/@value " + "return $x";
		} else {
			query = "for $x in collection('/db/DRIVER/ServiceResources') " + "where $x//PARENT_ID/@value/string()='" + DHNid + "' " + "return $x";
		}

		List<String> elems = executeQuery(query);
		List<ServiceInfo> ret = new ArrayList<ServiceInfo>();
		SAXReader xmlParser = new SAXReader();

		for (String xml : elems) {
			try {
				Document doc = xmlParser.read(new StringReader(xml));
				ServiceInfo row = new ServiceInfo();
				row.setId(doc.selectSingleNode("//RESOURCE_IDENTIFIER/@value").getText());
				row.setType(doc.selectSingleNode("//RESOURCE_TYPE/@value").getText());
				row.setDate(doc.selectSingleNode("//DATE_OF_CREATION/@value").getText());
				row.setUri(doc.selectSingleNode("//RESOURCE_URI/@value").getText());
				ret.add(row);
			} catch (DocumentException e) {

			}
		}
		return ret;

	}

	@Override
	public List<String> listSchemas() {
		try {
			List<String> list = isLookUpLocator.getService().listResourceTypes();
			Collections.sort(list);
			return list;
		} catch (Exception e) {
			return new ArrayList<String>();
		}
	}

	@Override
	public List<DHNInfo> listDHNs() {
		String query = "for $x in collection('/db/DRIVER/ServiceResources/HostingNodeManagerServiceResourceType') return $x";

		SAXReader reader = new SAXReader();

		List<DHNInfo> res = new ArrayList<DHNInfo>();
		for (String s : executeQuery(query)) {
			Document doc;
			try {

				doc = reader.read(new StringReader(s));

				String id = doc.valueOf("//RESOURCE_IDENTIFIER/@value");

				String addr;
				int port;
				try {
					URL url = new URL(doc.valueOf("//RESOURCE_URI/@value"));
					addr = url.getHost();
					port = url.getPort();
				} catch (MalformedURLException e) {
					addr = "0.0.0.0";
					port = 80;
				}

				DHNInfo dhn = new DHNInfo();
				dhn.setIdentifier(id);
				dhn.setAddress(addr + ":" + port);
				dhn.setName(doc.valueOf("//PROPERTY[@key='name']/@value"));
				dhn.setLat(Float.parseFloat(doc.valueOf("//PROPERTY[@key='latitude']/@value")));
				dhn.setLng(Float.parseFloat(doc.valueOf("//PROPERTY[@key='longitude']/@value")));
				dhn.setTimezone(Float.parseFloat(doc.valueOf("//PROPERTY[@key='timezone']/@value")));
				dhn.setMuninUrl(muninBaseUrl + addr);
				dhn.setSmokepingUrl(smokepingBaseUrl + addr);
				dhn.setServices(_listServices(id));
				res.add(dhn);
			} catch (DocumentException e) {
				log.error(e.getMessage());
			}
		}
		return res;
	}

	@Override
	public List<AllDSInfo> infoDataStructures() {

		List<AllDSInfo> response = new ArrayList<AllDSInfo>();

		List<String> elems = executeQuery("for $x in distinct-values(collection('/db/DRIVER')//RESOURCE_TYPE/@value)\n"
				+ "let $dates := collection('/db/DRIVER')//RESOURCE_TYPE[@value = $x]/../DATE_OF_CREATION/@value/string(),\n"
				+ "$m := max($dates), $c := count($dates)\n" + "where ends-with($x, 'DSResourceType')\n" + "order by $c descending\n"
				+ "return <r name='{$x}' date='{$m}' count='{$c}' />");
		SAXReader xmlParser = new SAXReader();

		for (String xml : elems) {
			Document doc;
			try {
				doc = xmlParser.read(new StringReader(xml));
				String name = doc.valueOf("//r/@name");
				if (name.endsWith("DSResourceType")) {
					AllDSInfo info = new AllDSInfo(name.replace("DSResourceType", ""), doc.valueOf("//r/@date"), Integer.parseInt(doc
							.valueOf("//r/@count")));
					response.add(info);
				}
			} catch (DocumentException e) {
			}

		}

		return response;
	}

	@Override
	public List<Map<String, String>> listRecentDataStructures(int limit) {
		String query = "subsequence(" + "for $x in collection('/db/DRIVER') " + "where ends-with($x//RESOURCE_TYPE/@value, 'DSResourceType') "
				+ "order by $x//DATE_OF_CREATION/@value descending " + "return $x,0," + limit + ")";
		List<String> elems = executeQuery(query);

		return _prepareListDataStructures(elems, null);
	}

	@Override
	public List<Map<String, String>> listDataStructures(String type) {
		if (!type.endsWith("ResourceType"))
			type += "DSResourceType";

		List<String> elems = executeQuery("for $x in collection('/db/DRIVER/')\n" + "where $x//RESOURCE_TYPE/@value/string() = '" + type + "'\n"
				+ "order by $x//DATE_OF_CREATION/@value/string()\n" + "return $x");

		return _prepareListDataStructures(elems, type);
	}

	private List<Map<String, String>> _prepareListDataStructures(List<String> list, String type) {
		boolean mixed = false;
		if (type == null)
			mixed = true;

		List<Map<String, String>> response = new ArrayList<Map<String, String>>();
		SAXReader xmlParser = new SAXReader();
		for (String xml : list) {
			Document doc;
			try {
				doc = xmlParser.read(new StringReader(xml));
				if (mixed)
					type = doc.valueOf("//RESOURCE_TYPE/@value");
				Map<String, String> row = new HashMap<String, String>();
				row.put("Identifier", doc.valueOf("//RESOURCE_IDENTIFIER/@value"));
				row.put("Last Update", doc.valueOf("//DATE_OF_CREATION/@value"));
				row.put("Type", type.replaceAll("DSResourceType", ""));
				Properties props = new Properties();
				InputStream in = this.getClass().getResourceAsStream("datastructures.prop");
				props.load(in);
				in.close();
				for (Object k : props.keySet()) {
					String key = k.toString();
					if (key.startsWith(type + ".")) {
						row.put(key.substring(type.length() + 1), doc.valueOf(props.getProperty(key)));
					}
				}
				response.add(row);
			} catch (Exception e) {
				log.error(e.getMessage());
			}
		}
		return response;
	}

	@Override
	public List<RepositoryStatusInfo> listRepositories(boolean withTransformator) throws MyGwtException {
		log.warn("DEPRECATED list repositories without users");

		return listRepositoriesForUser(null, withTransformator);
	}

	@Override
	public List<RepositoryStatusInfo> listRepositoriesForUser(String session, boolean withTransformator) throws MyGwtException {

		final List<String> aggregators = listAggregatorsForUser(session);

		List<RepositoryStatusInfo> response = new ArrayList<RepositoryStatusInfo>();

		String filter = "";

		if (!aggregators.isEmpty()) {
			filter = "let $aggregator := $e//EXTRA_FIELDS/FIELD[./key='aggregatorName']/value where not($aggregator) ";
			for (String aggregator : aggregators) {
				if (!"ALL".equals(aggregator))
					filter += " or $aggregator = '" + aggregator + "'";
			}
		}
		log.debug("Filter: " + filter);

		String xquery = "for $e in collection('/db/DRIVER/RepositoryServiceResources/RepositoryServiceResourceType') "
				+ filter
				+ "return concat('0',' | ',$e//RESOURCE_IDENTIFIER/@value/string(),' | ',$e//OFFICIAL_NAME,' | ',$e//COUNTRY,' | ',$e//NUMBER_OF_OBJECTS,' | ',$e//LATITUDE,' | ',$e//LONGITUDE,' | ',$e//EXTRA_FIELDS/FIELD[./key='aggregatorName']/value,' | ',$e//DATE_OF_CREATION/@value,' | ')"
				+ ", "
				+ "for $e in collection('/db/DRIVER/PendingRepositoryResources/RepositoryServiceResourceType') "
				+ filter
				+ "return concat('1',' | ',$e//RESOURCE_IDENTIFIER/@value/string(),' | ',$e//OFFICIAL_NAME,' | ',$e//COUNTRY,' | ',$e//NUMBER_OF_OBJECTS,' | ',$e//LATITUDE,' | ',$e//LONGITUDE,' | ',$e//EXTRA_FIELDS/FIELD[./key='aggregatorName']/value,' | ',$e//DATE_OF_CREATION/@value,' | ')";

		List<String> elems = executeQuery(xquery);

		String xqueryIndexed = "for $x in collection('/db/DRIVER/ManagerServiceMapDSResources/ManagerServiceMapDSResourceType') return $x//INDEX/MDSTORE/@id/string()";

		Map<String, String> statusImages = null;
		if (withTransformator) {
			String xqueryStored = "for $r in collection('/db/DRIVER/RepositoryServiceResources/RepositoryServiceResourceType') " + "let $harv := ( "
					+ "	for $h in collection('/db/DRIVER/HarvestingDSResources/HarvestingDSResourceType') "
					+ "	where $h//CONFIGURATION/REPOSITORY_SERVICE_IDENTIFIER = $r//RESOURCE_IDENTIFIER/@value "
					+ "	return concat($h//METADATA_FORMAT,'|',$h//DATA_SINK)  " + ") " + "let $tran := ( "
					+ "	for $h in collection('/db/DRIVER/TransformationDSResources/TransformationDSResourceType') "
					+ "	where $h//CONFIGURATION/REPOSITORY_SERVICE_IDENTIFIER = $r//RESOURCE_IDENTIFIER/@value "
					+ "	return concat($h//SINK_METADATA_FORMAT/@name,'|',$h//DATA_SINK) " + ") "
					+ "return concat($r//RESOURCE_IDENTIFIER/@value,'|',$harv[1],'|',string-join($tran,'|')) ";

			statusImages = calculateStatusImages(executeQuery(xqueryStored), executeQuery(xqueryIndexed));
		} else {
			String xqueryStored = "for $h in collection('/db/DRIVER/HarvestingInstanceDSResources/HarvestingInstanceDSResourceType') "
					+ "return concat($h//CONFIGURATION/REPOSITORY_SERVICE_IDENTIFIER,'|harv_',$h//MDSTORE_DS_IDENTIFIER[../METADATA_FORMAT/text() = '"
					+ mdHarvFormat + "'],'|aggr_',$h//MDSTORE_DS_IDENTIFIER[../METADATA_FORMAT/text() = '" + mdAggrFormat + "'])";
			statusImages = calculateStatusImagesOLD(executeQuery(xqueryStored), executeQuery(xqueryIndexed));
		}

		for (String s : elems) {
			RepositoryStatusInfo repo = new RepositoryStatusInfo();
			String[] arr = s.split("\\|");
			if (arr.length != 8) {
				log.error("Error parsing repository info in repository profile: " + repo.getIdentifier());
			} else {
				if (arr[0].trim().equals("0"))
					repo.setPending(false);
				else
					repo.setPending(true);
				repo.setIdentifier(arr[1].trim());
				repo.setName(arr[2].trim());
				repo.setCountry(arr[3].trim());
				repo.setSize(Integer.parseInt(arr[4].trim()));
				repo.setLat(Float.parseFloat(arr[5].trim()));
				repo.setLng(Float.parseFloat(arr[6].trim()));
				repo.setAggregator(arr[7].trim());
				try {
					repo.setLastUpdate(new DateUtils().parse(arr[8].trim()));
				} catch (Exception e) {
					log.error("Error parsing date '" + arr[8].trim() + "' in repository profile: " + repo.getIdentifier());
					repo.setLastUpdate(new Date(0));
				}

				repo.setDeleted(false);
				if (repo.isPending())
					repo.setStatusImage(0 + mdHarvFormat + ".0" + mdAggrFormat);
				else if (statusImages.containsKey(arr[1].trim()))
					repo.setStatusImage(statusImages.get(arr[1].trim()));
				else
					repo.setStatusImage(2 + mdHarvFormat + ".2" + mdAggrFormat);
				response.add(repo);
			}
		}
		return response;
	}

	private Map<String, String> calculateStatusImages(List<String> list, List<String> indexed) {
		Map<String, String> res = new HashMap<String, String>();

		for (String line : list) {
			String[] arr = line.split("\\|");
			String repoId = arr[0];
			log.debug("Processing image for repo: " + repoId);
			boolean mdHarvFormatFound = false;
			boolean mdAggrFormatFound = false;
			String image = "";

			for (int i = 1; i < arr.length - 1; i += 2) {
				String mdFormat = arr[i];
				String mdId = arr[i + 1];
				if (mdFormat.equals(mdHarvFormat))
					mdHarvFormatFound = true;
				if (mdFormat.equals(mdAggrFormat))
					mdAggrFormatFound = true;
				if (mdId.startsWith("dnet://MDStoreDS/")) {
					mdId = mdId.substring("dnet://MDStoreDS/".length(), mdId.indexOf("?"));
				}
				log.debug(" - mdID : " + mdId);
				String stHarv = indexed.contains(mdId) ? "1" : "2";
				image += stHarv + mdFormat + ".";
			}

			if (!mdHarvFormatFound) {
				image += "0" + mdHarvFormat + ".";
			}
			if (!mdAggrFormatFound) {
				image += "0" + mdAggrFormat + ".";
			}

			if (image.endsWith("."))
				image = image.substring(0, image.length() - 1);
			log.debug(" - image : " + image);
			res.put(repoId, image);
		}

		return res;
	}

	@Deprecated
	private Map<String, String> calculateStatusImagesOLD(List<String> list, List<String> indexed) {
		Map<String, String> res = new HashMap<String, String>();

		for (String line : list) {
			String[] arr = line.split("\\|");
			String repoId = arr[0];
			String harvMdId = arr[1].substring(5); //remove harv_ from beginning
			String aggrMdId = arr[2].substring(5); //remove aggr_ from beginning

			if (harvMdId.startsWith("dnet://MDStoreDS/")) {
				harvMdId = harvMdId.substring("dnet://MDStoreDS/".length(), harvMdId.indexOf("?"));
			}
			if (aggrMdId.startsWith("dnet://MDStoreDS/")) {
				aggrMdId = aggrMdId.substring("dnet://MDStoreDS/".length(), aggrMdId.indexOf("?"));
			}

			log.debug("Processing:");
			log.debug(" - repo : " + repoId);
			log.debug(" - harvMdID : " + harvMdId);
			log.debug(" - aggMdId : " + aggrMdId);

			String stHarv = "";
			if (isMissingMDF(harvMdId)) {
				stHarv = "0";
			} else if (indexed.contains(harvMdId)) {
				stHarv = "1";
			} else {
				stHarv = "2";
			}

			String stAggr = "";
			if (isMissingMDF(aggrMdId)) {
				stAggr = "0";
			} else if (indexed.contains(aggrMdId)) {
				stAggr = "1";
			} else {
				stAggr = "2";
			}

			res.put(repoId, stHarv + mdHarvFormat + "." + stAggr + mdAggrFormat);
		}

		return res;
	}

	private boolean isMissingMDF(String id) {
		if (id == null)
			return true;
		if (id.equals(""))
			return true;
		return false;
	}

	@Override
	public RepositoryDetailsInfo getRepositoryDetailsInfo(String id, boolean withTransformator) throws MyGwtException {
		return Common.getRepoDetails(id, withTransformator, isLookUpLocator.getService());
	}

	@Override
	public List<MDStoreInfo> getMDStoreInfo(List<String> ids) throws MyGwtException {
		List<MDStoreInfo> res = new ArrayList<MDStoreInfo>();

		for (String id : ids) {
			MDStoreInfo info = Common.getMDStoreInfo(id, isLookUpLocator.getService());
			res.add(info);
		}
		return res;
	}

	@Override
	public Map<String, String> loadCountries() throws MyGwtException {
		Map<String, String> map = new HashMap<String, String>();
		String xquery = "for $x in collection('/db/DRIVER/VocabularyDSResources/VocabularyDSResourceType')//CONFIGURATION[./VOCABULARY_NAME='Names of Countries']/TERMS/TERM return concat($x/@encoding,'|',$x/@english_name)";
		for (String line : executeQuery(xquery)) {
			String[] arr = line.split("\\|");
			if (arr.length == 2) {
				map.put(arr[0], arr[1]);
			}
		}
		return map;
	}

	@Override
	public List<IndexInfo> getOldOrDuplicatedIndexes() throws MyGwtException {

		List<IndexInfo> res = new ArrayList<IndexInfo>();

		String queryOLD = "for $mi in collection('/db/DRIVER/ManagerServiceMapDSResources/ManagerServiceMapDSResourceType')//INDEX,"
				+ "$md in collection('/db/DRIVER/MDStoreDSResources/MDStoreDSResourceType')//RESOURCE_PROFILE[.//RESOURCE_IDENTIFIER/@value = $mi/MDSTORE/@id],"
				+ "$ix in collection('/db/DRIVER/IndexDSResources/IndexDSResourceType')//RESOURCE_PROFILE[.//RESOURCE_IDENTIFIER/@value = $mi/@id],"
				+ "$hi in collection('/db/DRIVER/HarvestingInstanceDSResources/HarvestingInstanceDSResourceType')//RESOURCE_PROFILE[.//AGGREGATING_UNIT/MDSTORE_DS_IDENTIFIER = $mi/MDSTORE/@id],"
				+ "$re in collection('/db/DRIVER/RepositoryServiceResources/RepositoryServiceResourceType')//RESOURCE_PROFILE[.//RESOURCE_IDENTIFIER/@value = $hi//REPOSITORY_SERVICE_IDENTIFIER] "
				+ "where $md//LAST_STORAGE_DATE/string() != ''"
				+ "and xs:dateTime($ix//INDEX_LAST_UPDATE/string()) < xs:dateTime($md//DATE_OF_CREATION/@value/string()) "
				+
				//"order by xs:integer($md//NUMBER_OF_RECORDS) descending " +
				"return concat(" + "$re//RESOURCE_IDENTIFIER/@value/string(),'|', " + "$re//OFFICIAL_NAME,'|'," + "$re//COUNTRY,'|',"
				+ "$hi//RESOURCE_IDENTIFIER/@value/string(),'|'," + "$ix//RESOURCE_IDENTIFIER/@id/string(),'|'," + "$ix//INDEX_SIZE,'|',"
				+ "$ix//DATE_OF_CREATION/@value)";

		for (String s : executeQuery(queryOLD)) {
			String[] arr = s.split("\\|");

			String repoId = arr[0];
			String repoName = arr[1];
			String repoCountry = arr[2];
			String hiid = arr[3];
			String indexId = arr[4];
			int indexSize = Integer.parseInt(arr[5]);
			String indexDate = arr[6];

			IndexInfo info = new IndexInfo(indexId, indexSize, false, false, repoId, repoName, repoCountry, hiid, indexDate);
			res.add(info);
		}

		String queryDupl = "for $ma in collection('/db/DRIVER/ManagerServiceMapDSResources/ManagerServiceMapDSResourceType')//INDEX,"
				+ "$mb in collection('/db/DRIVER/ManagerServiceMapDSResources/ManagerServiceMapDSResourceType')//INDEX[@id != $ma/@id and MDSTORE/@id = $ma/MDSTORE/@id],"
				+ "$ix in collection('/db/DRIVER/IndexDSResources/IndexDSResourceType')//RESOURCE_PROFILE[.//RESOURCE_IDENTIFIER/@value = $mb/@id],"
				+ "$hi in collection('/db/DRIVER/HarvestingInstanceDSResources/HarvestingInstanceDSResourceType')//RESOURCE_PROFILE[.//AGGREGATING_UNIT/MDSTORE_DS_IDENTIFIER = $mb/MDSTORE/@id],"
				+ "$re in collection('/db/DRIVER/RepositoryServiceResources/RepositoryServiceResourceType')//RESOURCE_PROFILE[.//RESOURCE_IDENTIFIER/@value = $hi//REPOSITORY_SERVICE_IDENTIFIER] "
				+ "order by $re//OFFICIAL_NAME,xs:dateTime($ix//DATE_OF_CREATION/@value) descending " + "return concat("
				+ "$re//RESOURCE_IDENTIFIER/@value/string(),'|', " + "$re//OFFICIAL_NAME,'|'," + "$re//COUNTRY,'|',"
				+ "$hi//RESOURCE_IDENTIFIER/@value/string(),'|'," + "$ix//RESOURCE_IDENTIFIER/@id/string(),'|'," + "$ix//INDEX_SIZE,'|',"
				+ "$ix//DATE_OF_CREATION/@value)";

		for (String s : executeQuery(queryDupl)) {
			String[] arr = s.split("\\|");

			String repoId = arr[0];
			String repoName = arr[1];
			String repoCountry = arr[2];
			String hiid = arr[3];
			String indexId = arr[4];
			int indexSize = Integer.parseInt(arr[5]);
			String indexDate = arr[6];

			IndexInfo info = new IndexInfo(indexId, indexSize, true, true, repoId, repoName, repoCountry, hiid, indexDate);
			res.add(info);
		}
		return res;
	}

	private List<Map<String, String>> _clean_repo_without_hi(boolean delete) {
		List<Map<String, String>> res = new ArrayList<Map<String, String>>();

		String query = "for $repo in "
				+ "collection('/db/DRIVER/RepositoryServiceResources/RepositoryServiceResourceType')//RESOURCE_IDENTIFIER/@value/string() "
				+ "where not(contains(collection('/db/DRIVER/HarvestingInstanceDSResources/HarvestingInstanceDSResourceType')//REPOSITORY_SERVICE_IDENTIFIER/text(),$repo)) "
				+ "return $repo";

		for (String item : executeQuery(query)) {
			Map<String, String> map = new HashMap<String, String>();
			map.put("id", item);
			res.add(map);
		}
		;
		return res;
	}

	private List<Map<String, String>> _clean_up_hi_without_repo(boolean delete) {
		List<Map<String, String>> res = new ArrayList<Map<String, String>>();

		String query = "for $hi in "
				+ "collection('/db/DRIVER/HarvestingInstanceDSResources/HarvestingInstanceDSResourceType') "
				+ "where not(contains(collection('/db/DRIVER/RepositoryServiceResources/RepositoryServiceResourceType')//RESOURCE_IDENTIFIER/@value/string(),$hi//REPOSITORY_SERVICE_IDENTIFIER/text())) "
				+ "return $hi//RESOURCE_IDENTIFIER/@value/string()";

		for (String item : executeQuery(query)) {
			Map<String, String> map = new HashMap<String, String>();
			map.put("id", item);
			res.add(map);
		}
		;
		return res;
	}

	private List<Map<String, String>> _clean_up_hi_without_store(boolean delete) {
		List<Map<String, String>> res = new ArrayList<Map<String, String>>();

		String query = "for $hi in "
				+ "collection('/db/DRIVER/HarvestingInstanceDSResources/HarvestingInstanceDSResourceType') "
				+ "where not(contains(collection('/db/DRIVER/MDStoreDSResources/MDStoreDSResourceType')//RESOURCE_IDENTIFIER/@value/string(),$hi//MDSTORE_DS_IDENTIFIER/text())) "
				+ "return $hi//RESOURCE_IDENTIFIER/@value/string()";

		for (String item : executeQuery(query)) {
			Map<String, String> map = new HashMap<String, String>();
			map.put("id", item);
			res.add(map);
		}

		return res;
	}

	private List<Map<String, String>> _cleanUP_indices_not_managed(boolean delete) {
		List<Map<String, String>> res = new ArrayList<Map<String, String>>();

		String query = "for $idx in "
				+ "collection('/db/DRIVER/IndexDSResources/IndexDSResourceType')//RESOURCE_IDENTIFIER/@value/string() "
				+ "where not(contains(collection('/db/DRIVER/ManagerServiceMapDSResources/ManagerServiceMapDSResourceType')//INDEX_MAP/INDEX/@id/string(), $idx)) "
				+ "return $idx";

		for (String item : executeQuery(query)) {
			Map<String, String> map = new HashMap<String, String>();
			map.put("id", item);
			res.add(map);
		}
		;

		return res;
	}

	private List<Map<String, String>> _cleanUP_stores_without_hi(boolean delete) {
		List<Map<String, String>> res = new ArrayList<Map<String, String>>();

		String query = "for $md  in "
				+ "collection('/db/DRIVER/MDStoreDSResources/MDStoreDSResourceType')//RESOURCE_IDENTIFIER/@value/string() "
				+ "where not(contains(collection('/db/DRIVER/HarvestingInstanceDSResources/HarvestingInstanceDSResourceType')//MDSTORE_DS_IDENTIFIER/text(),$md)) "
				+ "return $md";

		for (String item : executeQuery(query)) {
			Map<String, String> map = new HashMap<String, String>();
			map.put("id", item);
			res.add(map);
		}
		;

		return res;
	}

	private List<Map<String, String>> _cleanUP_stores_without_index(boolean delete) {
		List<Map<String, String>> res = new ArrayList<Map<String, String>>();
		String query = "for $md in "
				+ "collection('/db/DRIVER/MDStoreDSResources/MDStoreDSResourceType') "
				+ "where not(contains(collection('/db/DRIVER/ManagerServiceMapDSResources/ManagerServiceMapDSResourceType')//INDEX_MAP/INDEX/MDSTORE/@id/string(),$md//RESOURCE_IDENTIFIER/@value/string())) "
				+ "order by $md//METADATA_FORMAT/text(),$md//STATUS/NUMBER_OF_RECORDS/number() descending "
				+ "return concat($md//METADATA_FORMAT/text(),'|',$md//STATUS/NUMBER_OF_RECORDS/text(),'|',$md//RESOURCE_IDENTIFIER/@value/string(),'|')";

		for (String item : executeQuery(query)) {
			Map<String, String> map = new HashMap<String, String>();
			String[] arr = item.split("\\|");
			if (arr.length == 3) {
				map.put("type", arr[0]);
				map.put("size", arr[1]);
				map.put("id", arr[2]);
			}

			res.add(map);
		}

		return res;
	}

	@Override
	public Map<String, List<Map<String, String>>> cleanUP(Boolean delete) throws MyGwtException {
		Map<String, List<Map<String, String>>> res = new HashMap<String, List<Map<String, String>>>();

		res.put("MDSTORES without index - to FEED (manually)", _cleanUP_stores_without_index(delete));
		res.put("MDSTORES without HI - Cleaning will DELETE them", _cleanUP_stores_without_hi(delete));
		res.put("Indices not registered in the ManagerServiceMap - Cleaning will DELETE them", _cleanUP_indices_not_managed(delete));
		res.put("HIs without MDSTORE - Cleaning will DELETE them", _clean_up_hi_without_store(delete));
		res.put("HIs without REPOSITORY - Cleaning will DELETE them", _clean_up_hi_without_repo(delete));
		res.put("REPOs without HI - Cleaning will INVALIDATE them", _clean_repo_without_hi(delete));

		return res;
	}

	@Override
	public List<String> listMetadataFormats() {
		List<String> list = executeQuery("distinct-values(//METADATAFORMAT/@Prefix)");
		Collections.sort(list);
		return list;
	}

	@Override
	public List<UnibiServiceInfo> listAggregators() throws MyGwtException {
		return listAggregatorsOrTranformators("AggregatorServiceResourceType");
	}

	@Override
	public List<UnibiServiceInfo> listTransformators() throws MyGwtException {
		return listAggregatorsOrTranformators("TransformationManagerServiceResourceType");
	}

	@Override
	public List<UnibiServiceInfo> listHarvesters() throws MyGwtException {
		return listAggregatorsOrTranformators("HarvestingManagerServiceResourceType");
	}

	private List<UnibiServiceInfo> listAggregatorsOrTranformators(String type) {
		List<UnibiServiceInfo> list = new ArrayList<UnibiServiceInfo>();

		try {
			String query = "for $x in " + "collection('/db/DRIVER/ServiceResources/" + type + "') " + "return $x";

			SAXReader reader = new SAXReader();

			for (String item : executeQuery(query)) {
				Document doc = reader.read(new StringReader(item));

				String name = doc.valueOf("//SERVICE_PROPERTIES/PROPERTY[@key='name']/@value");
				if ((name == null) || (name.isEmpty())) {
					name = "unknown";
				}
				String country = doc.valueOf("//SERVICE_PROPERTIES/PROPERTY[@key='country']/@value");
				if ((country == null) || (country.isEmpty())) {
					country = "unknown";
				}
				String host = doc.valueOf("//SERVICE_PROPERTIES/PROPERTY[@key='host']/@value");
				if ((host == null) || (host.isEmpty())) {
					host = "unknown";
				}
				String url = doc.valueOf("//SERVICE_PROPERTIES/PROPERTY[@key='url']/@value");
				if ((url != null) && (url.length() > 0)) {
					list.add(new UnibiServiceInfo(name, type, country, url, host));
				}
			}
		} catch (Exception e) {
			log.error(e.getMessage());
		}
		return list;
	}

	@Override
	public List<String> listAggregatorsForUser(String session) throws MyGwtException {
		try {
			String query = "distinct-values((" + "for $e in collection('/db/DRIVER/RepositoryServiceResources/RepositoryServiceResourceType') "
					+ "return $e//EXTRA_FIELDS/FIELD[./key='aggregatorName']/value,"
					+ "for $e in collection('/db/DRIVER/PendingRepositoryResources/RepositoryServiceResourceType') "
					+ "return $e//EXTRA_FIELDS/FIELD[./key='aggregatorName']/value, "
					+ "for $e in collection('/db/DRIVER/ServiceResources/AggregatorServiceResourceType') "
					+ "return $e//SERVICE_PROPERTIES/PROPERTY[@key='name']/@value, "
					+ "for $e in collection('/db/DRIVER/ServiceResources/HarvestingManagerServiceResourceType') "
					+ "return $e//SERVICE_PROPERTIES/PROPERTY[@key='name']/@value, "
					+ "for $e in collection('/db/DRIVER/ServiceResources/TransformationManagerServiceResourceType') "
					+ "return $e//SERVICE_PROPERTIES/PROPERTY[@key='name']/@value " + "))";

			List<String> aggrs = isLookUpLocator.getService().quickSearchProfile(query);

			List<String> list = new ArrayList<String>();

			Principal principal = sessionManager.getPrincipal(session);

			if (principal == null || principal.getSecurityContext() == null || principal.getSecurityContext().isEmpty()
					|| authenticationManager.authorize(principal)) {
				list.addAll(aggrs);
			} else {
				for (String aggr : aggrs) {
					if (authenticationManager.authorize(principal, aggr, aggregatorPolicyAction)) {
						list.add(aggr);
					}
				}
			}
			Collections.sort(list);

			return list;
		} catch (Exception e) {
			log.error(e);
			throw new MyGwtException();
		}
	}

	@Override
	public List<BlackBoardInfo> listBlackboards() throws MyGwtException {
		List<BlackBoardInfo> list = new ArrayList<BlackBoardInfo>();
		SAXReader reader = new SAXReader();
		try {
			for (String xml : isLookUpLocator
					.getService()
					.quickSearchProfile(
							"for $x in collection('/db/DRIVER/ServiceResources')//MESSAGE return <message>{$x/../../..//RESOURCE_TYPE}{$x/../../..//RESOURCE_IDENTIFIER}{$x}</message>")) {
				BlackBoardInfo info = new BlackBoardInfo();
				Document doc = reader.read(new StringReader(xml));
				info.setProfId(doc.valueOf(".//RESOURCE_IDENTIFIER/@value"));
				info.setMessageId(doc.valueOf(".//@id"));
				info.setResourceType(doc.valueOf(".//RESOURCE_TYPE/@value"));
				info.setAction(doc.valueOf(".//ACTION"));
				info.setDate(doc.valueOf(".//@date"));
				info.setActionStatus(doc.valueOf(".//ACTION_STATUS"));
				info.setError(doc.valueOf(".//PARAMETER[@name='error']/@value"));
				list.add(info);
			}
		} catch (Exception e) {
			log.error(e);
			throw new MyGwtException();
		}
		return list;
	}

	@Override
	public List<String> listIndeces() throws MyGwtException {
		String xquery = "for $x in collection('/db/DRIVER/ServiceResources/IndexServiceResourceType')//PROTOCOLS/PROTOCOL " + "where $x/@name='SOAP' "
				+ "return $x/@address/string()";
		try {
			return isLookUpLocator.getService().quickSearchProfile(xquery);
		} catch (ISLookUpException e) {
			log.error(e);
			throw new MyGwtException();
		}
	}

	@Override
	public List<CountryInfo> listCountries() throws MyGwtException {
		String xquery = "for $x in collection('/db/DRIVER/VocabularyDSResources/VocabularyDSResourceType')[.//VOCABULARY_NAME/string() = 'Names of Countries']//TERM return concat($x/@english_name, '|', $x/@encoding)";

		List<CountryInfo> res = new ArrayList<CountryInfo>();
		try {
			List<String> list = isLookUpLocator.getService().quickSearchProfile(xquery);
			Collections.sort(list);

			for (String s : list) {
				if (s.contains("|")) {
					String[] arr = s.split("\\|");
					if (arr.length == 2) {
						res.add(new CountryInfo(arr[0], arr[1]));
					}
				}
			}
		} catch (ISLookUpException e) {
			log.error(e);
			throw new MyGwtException();
		}
		return res;
	}

	public String getMdAggrFormat() {
		return mdAggrFormat;
	}

	@Required
	public void setMdAggrFormat(String mdAggrFormat) {
		this.mdAggrFormat = mdAggrFormat;
	}

	public String getMdHarvFormat() {
		return mdHarvFormat;
	}

	@Required
	public void setMdHarvFormat(String mdHarvFormat) {
		this.mdHarvFormat = mdHarvFormat;
	}

	@Required
	public void setSessionManager(SessionManager sessionManager) {
		this.sessionManager = sessionManager;
	}

	public SessionManager getSessionManager() {
		return sessionManager;
	}

	@Required
	public void setAuthenticationManager(AuthenticationManager authenticationManager) {
		this.authenticationManager = authenticationManager;
	}

	public AuthenticationManager getAuthenticationManager() {
		return authenticationManager;
	}

	public String getAggregatorPolicyAction() {
		return aggregatorPolicyAction;
	}

	@Required
	public void setAggregatorPolicyAction(String aggregatorPolicyAction) {
		this.aggregatorPolicyAction = aggregatorPolicyAction;
	}

}
