package eu.dnetlib.data.collector.plugins.ariadneplus.isidore; /**
 * log.debug(...) equal to  log.trace(...) in the application-logs
 * <p>
 * known bug: at resumptionType 'discover' if the (resultTotal % resultSizeValue) == 0 the collecting fails -> change the resultSizeValue
 */

import java.io.InputStream;
import java.net.URL;
import java.util.Iterator;
import java.util.Queue;
import java.util.concurrent.PriorityBlockingQueue;

import com.google.common.collect.Maps;
import eu.dnetlib.miscutils.functional.xml.SaxonHelper;
import eu.dnetlib.rmi.data.CollectorServiceException;
import net.sf.saxon.s9api.*;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * @author Jochen Schirrwagen, Aenne Loehden, Andreas Czerniak, Alessia Bardi
 * @date 2018-09-03
 *
 */
public class IsidoreIterator implements Iterator<String> {

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

	private SaxonHelper saxonHelper;
	private Queue<String> recordQueue = new PriorityBlockingQueue<String>();
	private InputStream resultStream;
	private String query;
	private String querySize;
	private String queryPage;
	private int resultTotal = -1;

	private String baseUrl;
	private String queryParams;
	private String pageParam;
	private int page;
	private String pageSizeParam;
	private int pageSize;

	private XPathSelector xprEntity;
	private XPathSelector xprResultTotalPath;
	private XPathSelector xprNextPagePath;

	public IsidoreIterator(
			final SaxonHelper saxonHelper,
			final String baseUrl,
			final String queryParams,
			final String pageParam,
			final int startFromPage,
			final String nextPagePath,
			final String pageSizeParam,
			final int pageSize,
			final String resultTotalXpath,
			final String entityXpath
	) {
		this.saxonHelper = saxonHelper;
		this.baseUrl = baseUrl;
		this.queryParams = queryParams;
		this.pageParam = pageParam;
		this.page = startFromPage;
		this.queryPage = this.pageParam+"="+this.page;
		this.pageSizeParam = pageSizeParam;
		this.pageSize = pageSize;
		this.querySize = this.pageSizeParam+"="+this.pageSize;
		try {
			initXpathSelector(resultTotalXpath, nextPagePath, entityXpath);
		} catch (Exception e) {
			throw new IllegalStateException("xml transformation init failed: " + e.getMessage());
		}
		initQuery();
	}

	private void initXpathSelector(final String resultTotalXpath,final String nextPagePath, final String entityXpath)
			throws  SaxonApiException {
		xprResultTotalPath = this.saxonHelper.help().prepareXPathSelector(resultTotalXpath, Maps.newHashMap());
		xprNextPagePath = this.saxonHelper.help().prepareXPathSelector(nextPagePath, Maps.newHashMap());
		xprEntity = this.saxonHelper.help().prepareXPathSelector(entityXpath, Maps.newHashMap());
	}

	private void initQuery() {
		query = baseUrl + "?" + queryParams +"&"+ querySize +"&"+ queryPage;
	}

	private void disconnect() {
		// TODO close inputstream
	}

	/* (non-Javadoc)
	 * @see java.util.Iterator#hasNext()
	 */
	@Override
	public boolean hasNext() {
		if (recordQueue.isEmpty() && query.isEmpty()) {
			disconnect();
			return false;
		} else {
			return true;
		}
	}

	/* (non-Javadoc)
	 * @see java.util.Iterator#next()
	 */
	@Override
	public String next() {
		synchronized (recordQueue) {
			while (recordQueue.isEmpty() && !query.isEmpty()) {
				try {
					log.info("get Query: " + query);
					query = downloadPage(query);
					log.debug("next queryURL from downloadPage(): " + query);
				} catch (CollectorServiceException e) {
					log.debug("CollectorPlugin.next()-Exception: " + e);
					throw new RuntimeException(e);
				}
			}
			return recordQueue.poll();
		}
	}

	/*
	 * download page and return nextQuery
	 */
	private String downloadPage(String query) throws CollectorServiceException {
		String nextQuery = "";
		XdmValue nodeList;

		try {
			URL qUrl = new URL(query);
			resultStream = qUrl.openStream();
			String resultPage = IOUtils.toString(resultStream);
			nodeList = this.saxonHelper.help().evaluate(resultPage, xprEntity);
			log.debug("nodeList.size: " + nodeList.size());
			for(XdmItem entity : nodeList){
				recordQueue.add(this.saxonHelper.help().serialize((XdmNode) entity));
			}

			String nextPage = this.saxonHelper.help().evaluateSingleAsString(resultPage, xprNextPagePath);
			if(StringUtils.isBlank(nextPage)){
				log.info("No next page available, we reached the end");
			}
			else{
				this.queryPage = pageParam+"="+nextPage;
				return baseUrl + "?" + queryParams +"&"+ querySize +"&"+ queryPage;
			}

			if (resultTotal == -1) {
				String tot = this.saxonHelper.help().evaluateSingleAsString(resultPage, xprResultTotalPath);
				resultTotal = Integer.parseInt(tot);
				log.info("resultTotal was -1 is now: " + resultTotal);
			}
			log.info("resultTotal: " + resultTotal);
			log.debug("nextQueryUrl: " + nextQuery);
			return nextQuery;

		} catch (Exception e) {
			log.error(e);
			throw new IllegalStateException("collection failed: " + e.getMessage());
		}
	}


}
