package eu.dnetlib.functionality.modular.ui.repositories;

import java.io.StringReader;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Resource;

import org.antlr.stringtemplate.StringTemplate;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.springframework.core.io.ClassPathResource;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

import eu.dnetlib.data.collector.rmi.CollectorService;
import eu.dnetlib.data.collector.rmi.ProtocolDescriptor;
import eu.dnetlib.data.collector.rmi.ProtocolParameter;
import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpException;
import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpService;
import eu.dnetlib.enabling.locators.UniqueServiceLocator;
import eu.dnetlib.functionality.modular.ui.repositories.objects.RepoInterfaceEntry;
import eu.dnetlib.functionality.modular.ui.repositories.objects.RepoMetaWfEntry;
import eu.dnetlib.functionality.modular.ui.repositories.objects.SimpleParamEntry;
import eu.dnetlib.functionality.modular.ui.repositories.objects.VocabularyEntry;
import eu.dnetlib.msro.workflows.util.WorkflowsConstants.WorkflowStatus;

public class RepoUIUtils {

	@Resource
	private UniqueServiceLocator serviceLocator;

	private final ClassPathResource getRepoApiQueryTmpl =
			new ClassPathResource("/eu/dnetlib/functionality/modular/ui/repositories/templates/getRepoApi.xquery.st");

	private static final Log log = LogFactory.getLog(RepoUIUtils.class);

	private final Map<String, List<ProtocolParameter>> parametersMap = Maps.newHashMap();

	public RepoInterfaceEntry getApi(final String repoId, final String ifaceId) throws Exception {
		final RepoInterfaceEntry ifc = new RepoInterfaceEntry();

		final StringTemplate st = new StringTemplate(IOUtils.toString(getRepoApiQueryTmpl.getInputStream()));
		st.setAttribute("dsId", repoId);
		st.setAttribute("ifaceId", ifaceId);

		final String query = st.toString();

		final SAXReader reader = new SAXReader();

		final String s = serviceLocator.getService(ISLookUpService.class).getResourceProfileByQuery(query);

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

		ifc.setId(doc.valueOf("/api/id"));
		ifc.setLabel(doc.valueOf("/api/label"));
		ifc.setRemovable(doc.valueOf("/api/removable").equalsIgnoreCase("true") && doc.selectNodes("//metaWF").isEmpty());
		ifc.setRepoId(doc.valueOf("/api/repo/@id"));
		ifc.setRepoName(StringEscapeUtils.unescapeHtml(doc.valueOf("/api/repo")));
		ifc.setRepoCountry(doc.valueOf("/api/repo/@country"));
		ifc.setRepoPrefix(doc.valueOf("/api/repo/@prefix"));
		ifc.setRepoType(doc.valueOf("/api/repo/@type"));
		ifc.setEmail(doc.valueOf("/api/repo/@email"));
		final String protocol = doc.valueOf("/api/protocol");
		ifc.setProtocol(protocol);

		final Set<String> toVerifyParams = getParameterNamesForProtocol(ifc.getProtocol());
		for (final Object o : doc.selectNodes("/api/commonParams/param")) {
			final Node n = (Node) o;
			ifc.getCommonParams().add(new SimpleParamEntry(n.valueOf("@name"), n.getText()));
		}

		log.debug("****** " + toVerifyParams);
		for (final Object o : doc.selectNodes("/api/accessParams/param")) {
			final Node n = (Node) o;
			final String pname = n.valueOf("@name");
			if (toVerifyParams.contains(pname)) {
				ifc.getAccessParams().add(new SimpleParamEntry(pname, n.getText()));
				toVerifyParams.remove(pname);
			} else {
				log.warn("Invalid param [" + pname + "] for protocol " + protocol + " in repo " + repoId);
			}
		}
		for (final String pname : toVerifyParams) {
			ifc.getAccessParams().add(new SimpleParamEntry(pname, ""));
			log.info("Adding missing param [" + pname + "] for protocol " + protocol + " in repo " + repoId);
		}

		for (final Object o : doc.selectNodes("/api/extraFields/field")) {
			final Node n = (Node) o;
			final String name = n.valueOf("@name");
			final String value = n.getText();
			if (name.equalsIgnoreCase("overriding_compliance")) {
				for (final SimpleParamEntry e : ifc.getCommonParams()) {
					if (e.getName().equals("compliance")) {
						// The original compliance (assigned by the validator) is stored in otherValue
						e.setOtherValue(value);
					}
				}
			} else if (name.equalsIgnoreCase("last_aggregation_date")) {
				ifc.setAggrDate(value);
			} else if (name.equalsIgnoreCase("last_aggregation_total")) {
				ifc.setAggrTotal(NumberUtils.toInt(value, 0));
			} else if (name.equalsIgnoreCase("last_aggregation_mdId")) {
				ifc.setAggrMdId(value);
			} else if (name.equalsIgnoreCase("last_collection_date")) {
				ifc.setCollDate(value);
			} else if (name.equalsIgnoreCase("last_collection_total")) {
				ifc.setCollTotal(NumberUtils.toInt(value, 0));
			} else if (name.equalsIgnoreCase("last_collection_mdId")) {
				ifc.setCollMdId(value);
			} else if (name.equalsIgnoreCase("last_download_date")) {
				ifc.setDownloadDate(value);
			} else if (name.equalsIgnoreCase("last_download_total")) {
				ifc.setDownloadTotal(NumberUtils.toInt(value, 0));
			} else if (name.equalsIgnoreCase("last_download_objId")) {
				ifc.setDownloadObjId(value);
			} else {
				ifc.getOtherParams().add(new SimpleParamEntry(name, value));
			}
		}

		if (doc.selectNodes("/api/extraFields/field[@name='metadata_identifier_path']").isEmpty()) {
			ifc.getOtherParams().add(new SimpleParamEntry("metadata_identifier_path", ""));
		}

		for (final Object o : doc.selectNodes("//metaWF")) {
			final Node n = (Node) o;

			final String id = n.valueOf("./id");
			final String name = n.valueOf("./name");
			final String status = n.valueOf("./status");
			final String repoByeWfId = n.valueOf("./destroyWorkflow");

			int progress = 0;
			try {
				switch (WorkflowStatus.valueOf(status)) {
				case MISSING:
					progress = 0;
					break;
				case ASSIGNED:
					progress = 25;
					break;
				case WAIT_SYS_SETTINGS:
					progress = 50;
					break;
				case WAIT_USER_SETTINGS:
					progress = 75;
					break;
				case EXECUTABLE:
					progress = 100;
					break;
				}
			} catch (final Exception e) {
				progress = 0;
			}
			ifc.getMetaWFs().add(new RepoMetaWfEntry(id, name, status, repoByeWfId, progress));
		}

		return ifc;
	}

	public List<VocabularyEntry> fetchVocabularyTerms(final String voc) throws ISLookUpException {
		final String xquery = "for $x in collection('/db/DRIVER/VocabularyDSResources/VocabularyDSResourceType')[.//VOCABULARY_NAME/@code = '"
				+ voc.trim() + "']//TERM return concat($x/@code, ' @@@ ', $x/@english_name)";

		final List<VocabularyEntry> list = Lists.newArrayList();
		for (final String s : serviceLocator.getService(ISLookUpService.class).quickSearchProfile(xquery)) {
			final String[] arr = s.split("@@@");
			list.add(new VocabularyEntry(arr[0].trim(), arr[1].trim()));
		}
		Collections.sort(list);

		return list;
	}

	private Set<String> getParameterNamesForProtocol(final String protocol) {
		if (parametersMap.isEmpty()) {
			for (final ProtocolDescriptor d : serviceLocator.getService(CollectorService.class).listProtocols()) {
				parametersMap.put(d.getName().toLowerCase(), d.getParams());
			}
		}
		final Set<String> res = Sets.newHashSet();
		if (parametersMap.containsKey(protocol.toLowerCase())) {
			res.add("baseUrl");
			for (final ProtocolParameter p : parametersMap.get(protocol.toLowerCase())) {
				res.add(p.getName());
			}
		}

		return res;
	}

}
