package eu.dnetlib.data.utility.cleaner;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
//import org.perf4j.StopWatch;
//import org.perf4j.commonslog.CommonsLogStopWatch;
import org.springframework.beans.factory.annotation.Required;

import com.google.common.collect.Lists;

import eu.dnetlib.data.utility.cleaner.rmi.CleanerException;
import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpService;
import eu.dnetlib.enabling.tools.ServiceLocator;
import eu.dnetlib.miscutils.datetime.DateUtils;

public class VocabularyRule extends XPATHCleaningRule {

	private class Synonym {
		protected String term;
		protected String context;
		protected String dest;

		public Synonym(String term, String context, String dest) {
			this.term = term;
			this.context = context;
			this.dest = dest;
		}
	}

	private static final long VOCABULARY_EXPIRATION_TIME = 3600000; // 1 hour

	private String vocabulary;
	private ServiceLocator<ISLookUpService> lookupLocator;

	// Normal fields
	private long lastupdate = 0;

	private static final Log log = LogFactory.getLog(VocabularyRule.class); // NOPMD by marko on 11/24/08 5:02 PM
	private List<Synonym> vocabularyTerms = new ArrayList<Synonym>();

	@Override
	public void applyXpathRule(Document doc, String context) throws CleanerException {
//		StopWatch stopWatch = new CommonsLogStopWatch(log);
		verifyVocabulary();
//		stopWatch.stop("checking.verify");
		super.applyXpathRule(doc, context);
	}

	@Override
	protected String calculateNewValue(String oldValue, String context) throws CleanerException {
		log.debug("calculating new value " + oldValue + " context " + context);

		List<Synonym> terms = null;
//		synchronized (this) {
			terms = vocabularyTerms;
//		}

		if (terms.isEmpty())
			throw new CleanerException("Vocabulary document is null");

//		StopWatch stopWatch = new CommonsLogStopWatch(log);
//		StopWatch globalStopWatch = new CommonsLogStopWatch(log);

		for (Synonym s : terms) {
			if (context == null) {
				if (s.term.equalsIgnoreCase(oldValue)) {
//					globalStopWatch.stop("vocabulary.search.nocontext.found");
//					stopWatch.stop("vocabulary.search.nocontext.found." + vocabulary);
					return s.dest;
				}
			} else {
				if (s.term.equalsIgnoreCase(oldValue) && context.equals(s.context)) {
//					globalStopWatch.stop("vocabulary.search.context.found");
//					stopWatch.stop("vocabulary.search.context.found." + vocabulary);
					return s.dest;
				}
			}
		}
//		globalStopWatch.stop("vocabulary.nocontext.notFound");
//		stopWatch.stop("vocabulary.search.notFound." + vocabulary);

		log.debug("Synonym " + oldValue + " not found in vocabulary");
		return oldValue;
	}

	private void verifyVocabulary() throws CleanerException {
		synchronized (this) {
			if (DateUtils.now() - lastupdate < VOCABULARY_EXPIRATION_TIME)
				return;

//			StopWatch stopWatch = null;
			try {
				String query = "for $x in collection('/db/DRIVER/VocabularyDSResources/VocabularyDSResourceType')"
						+ "//RESOURCE_PROFILE[.//VOCABULARY_NAME='" + vocabulary + "']//SYNONYM "
						+ "return concat($x/@term,'|-:-|',$x/@encoding,'|-:-|',$x/../../@english_name)";

				List<Synonym> newVocabularyTerms = Lists.newArrayList();
				final List<String> vocabularyProfiles = lookupLocator.getService().quickSearchProfile(query);

//				stopWatch = new CommonsLogStopWatch(log);
				for (String s : vocabularyProfiles) {
					log.debug("SYNONYM : " + s);
					String[] arr = s.split("\\|-:-\\|");
					if (arr[0] == null || arr[0].isEmpty()) {
						continue;
					}
					newVocabularyTerms.add(new Synonym(arr[0], arr[1], arr[2]));
				}

				log.info("VOCABULARY " + vocabulary + "terms size " + newVocabularyTerms.size());

				lastupdate = DateUtils.now();

				vocabularyTerms = newVocabularyTerms;
//				stopWatch.stop("vocabolary.build.success");
			} catch (Exception e) {
//				if (stopWatch != null)
//					stopWatch.stop("vocabolary.build.failure");
				throw new CleanerException("Error obtaining vocabulary " + vocabulary, e);
			}
		}
	}

	@Required
	public void setVocabulary(String vocabulary) {
		this.vocabulary = vocabulary;
	}

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

}
