package eu.dnetlib.msro.workflows.sarasvati.registry;

import java.util.*;
import java.util.Map.Entry;

import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.googlecode.sarasvati.GraphProcess;
import com.googlecode.sarasvati.ProcessState;
import eu.dnetlib.msro.workflows.util.ProcessUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Required;

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

	private BiMap<String, GraphProcess> procs = HashBiMap.create();
	private Map<String, Collection<GraphProcess>> byResource = new HashMap<String, Collection<GraphProcess>>();

	private int maxSize;
	
	public GraphProcess findProcess(final String identifier) {
		return procs.get(identifier);
	}

	public Collection<GraphProcess> findProcessesByResource(final String identifier) {
		synchronized (this) {
			final Collection<GraphProcess> res = byResource.get(identifier);
			if (res == null) {
				return new ArrayList<GraphProcess>();
			}
			return res;
		}
	}

	public String associateProcessWithResource(final GraphProcess process, final String identifier) {
		registerProcess(process);
		synchronized (this) {
			final Collection<GraphProcess> processes = findProcessesByResource(identifier);
			processes.add(process);
			byResource.put(identifier, processes);
		}
		return identifier;
	}

	public String registerProcess(final GraphProcess process) {
		if (procs.containsValue(process)) {
			return procs.inverse().get(process);
		}
		final String id = ProcessUtils.generateProcessId();
		
		if (procs.size() >= maxSize) {
			removeOldestProcess();
		}
		
		procs.put(id, process);
		log.info("Registered proc " + process.getGraph().getName() + " with id " + id);
		
		return id;
	}
	
	public int countRunningWfs() {
		return countRunningWfs(true);
	}

	public int countRunningWfs(boolean includeCreated) {
		synchronized (this) {
			int count = 0;

			for (Entry<String, GraphProcess> e : procs.entrySet()) {
				final GraphProcess proc = e.getValue();
				final ProcessState state = proc.getState();

				if (!proc.isComplete() && !proc.isCanceled()) {
					if (includeCreated) {
						count++;
					} else if (!state.equals(ProcessState.Created)) {
						count++;
					}
				}
			}

			return count;
		}
	}

	private void removeOldestProcess() {
		Date oldDate = new Date();
		String oldId = null;
		
		for (Entry<String, GraphProcess> e : procs.entrySet()) {
			final GraphProcess proc = e.getValue();
			
			if (proc.isComplete() || proc.isCanceled()) {
				final Date date = ProcessUtils.calculateLastActivityDate(proc);
				if (date.before(oldDate)) {
					oldDate = date;
					oldId = e.getKey();
				}
			}
		}
		
		if (oldId != null) {
			unregisterProcess(oldId);
		}
		
	}

	public void unregisterProcess(final String identifier) {
		final GraphProcess process = findProcess(identifier); // NOPMD
		procs.remove(identifier);

		for (final Collection<GraphProcess> processes : byResource.values()) {
			processes.remove(process);
		}
	}

	public Collection<String> listIdentifiers() {
		return procs.keySet();
	}
	
	public Map<String, Collection<GraphProcess>> getByResource() {
		return byResource;
	}

	public int getMaxSize() {
		return maxSize;
	}

	@Required
	public void setMaxSize(int maxSize) {
		this.maxSize = maxSize;
	}

}
