package eu.dnetlib.enabling.resultset;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

import javax.annotation.PostConstruct;
import javax.xml.ws.wsaddressing.W3CEndpointReference;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Required;

import eu.dnetlib.enabling.tools.UniqueIdentifierGenerator;
import eu.dnetlib.enabling.tools.UniqueIdentifierGeneratorImpl;

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

	static class PreparedResultSet {
		private String rsId;
		private W3CEndpointReference epr;

		public PreparedResultSet(final String rsId, final W3CEndpointReference epr) {
			super();
			this.rsId = rsId;
			this.epr = epr;
		}

		public String getRsId() {
			return rsId;
		}

		public void setRsId(final String rsId) {
			this.rsId = rsId;
		}

		public W3CEndpointReference getEpr() {
			return epr;
		}

		public void setEpr(final W3CEndpointReference epr) {
			this.epr = epr;
		}
	}

	class PoolFiller implements Runnable {

		@Override
		public void run() {
			while (true)
				try {
					final PreparedResultSet resultSet = prepareResultSet();
					pool.put(resultSet);
				} catch (final InterruptedException e) {
					// eat this exception
					log.debug("cannot add resultset to pool", e);
				}
		}

		private PreparedResultSet prepareResultSet() {
			final String rsId = idGenerator.generateIdentifier();
			return new PreparedResultSet(rsId, createEPR(rsId));
		}

		private W3CEndpointReference createEPR(final String rsId) {
			return getResultSetService().getEprBuilder().getEndpointReference(getResultSetService().getEndpoint(), rsId);
		}
	}

	/**
	 * resultset service.
	 */
	private ResultSetServiceImpl resultSetService;

	private int capacity = 1000;
	private int fillerThreads = 10;
	private final BlockingQueue<PreparedResultSet> pool = new LinkedBlockingQueue<PreparedResultSet>(capacity);

	/**
	 * identifier generator.
	 */
	private UniqueIdentifierGenerator idGenerator = new UniqueIdentifierGeneratorImpl("rs-");

	@PostConstruct
	public void init() {
		log.info("-------<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>> Starting fill threads");
		for (int i = 0; i < fillerThreads; i++)
			startFillter();
	}

	protected void startFillter() {
		final Thread filler = new Thread(new PoolFiller());
		filler.setDaemon(true);
		filler.start();
	}

	public W3CEndpointReference registerResultSet(final ResultSet resultSet) {
		final PreparedResultSet prepared = nextPreparedResultSet();
		if (prepared == null) {
			log.info("EPR cache is empty, creating EPR in caller thread");
			return getResultSetService().getEprBuilder().getEndpointReference(getResultSetService().getEndpoint(), idGenerator.generateIdentifier());
		}

		getResultSetService().getResultsetRegistry().addResultSet(resultSet, prepared.getRsId());
		return prepared.getEpr();
	}

	private PreparedResultSet nextPreparedResultSet() {
		log.info(">>>> fetching a prepared resultset from the pool");
		return pool.poll();
	}

	public int getCapacity() {
		return capacity;
	}

	public void setCapacity(int capacity) {
		this.capacity = capacity;
	}

	public UniqueIdentifierGenerator getIdGenerator() {
		return idGenerator;
	}

	public void setIdGenerator(UniqueIdentifierGenerator idGenerator) {
		this.idGenerator = idGenerator;
	}

	public ResultSetServiceImpl getResultSetService() {
		return resultSetService;
	}

	@Required
	public void setResultSetService(ResultSetServiceImpl resultSetService) {
		this.resultSetService = resultSetService;
	}
}
