package eu.dnetlib.enabling.resultset;

import java.util.Map;
import java.util.WeakHashMap;

import eu.dnetlib.enabling.resultset.observer.DelegationObserver;
import eu.dnetlib.enabling.resultset.observer.Observable;


/**
 * Common stuff for resultset implementations bound to a ResultSetRegistry.
 * 
 * @author marko
 * 
 */
public abstract class AbstractObservableResultset implements ResultSet, Observable {
	/**
	 * true if the resultset is destroyed.
	 */
	private boolean destroyed = false;

	/**
	 * true if the resultset is open.
	 */
	private boolean open;

	/**
	 * delegate the real job to a java.util.Observable object.
	 */
	private transient final java.util.Observable observable = new DelegationObservable();

	/**
	 * weak map for removing single observers later on.
	 */
	private transient Map<ResultSetRegistry, DelegationObserver> observers = new WeakHashMap<ResultSetRegistry, DelegationObserver>();

	/**
	 * java.util.Observable requires a "change" in the object to be made for the notifyObservers methods to trigger
	 * actually the notification. We don't think this should be part of the base Observable/Observer pattern and thus
	 * hide this fact. If an user of AbstractObservable wants this behavior it should implement her own change tracking
	 * and avoid calling notifyObservers altogether. The rationale behind this choice is that not all dirty states can
	 * be expressed using a stateful boolean instance variable like that implemented by java.util.Observable; some users
	 * may prefer/need a lazy dirty flag instead.
	 * 
	 * @author marko
	 * 
	 */
	static class DelegationObservable extends java.util.Observable {

		/**
		 * {@inheritDoc}
		 * 
		 * @see java.util.Observable#notifyObservers()
		 */
		@Override
		public void notifyObservers() {
			setChanged();
			super.notifyObservers();
		}

		/**
		 * {@inheritDoc}
		 * 
		 * @see java.util.Observable#notifyObservers(java.lang.Object)
		 */
		@Override
		public void notifyObservers(final Object arg) {
			setChanged();
			super.notifyObservers(arg);
		}

	}	

	/**
	 * {@inheritDoc}
	 * 
	 * @see eu.dnetlib.enabling.resultset.ResultSet#destroy()
	 */
	@Override
	public void destroy() {
		if (!isDestroyed()) {
			setDestroyed(true);
			notifyObservers();
			deleteObservers();
		}
	}

	public void setDestroyed(final boolean destroyed) {
		this.destroyed = destroyed;
	}

	@Override
	public boolean isDestroyed() {
		return destroyed;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see eu.dnetlib.enabling.resultset.ResultSet#isOpen()
	 */
	@Override
	public boolean isOpen() {
		return open;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see eu.dnetlib.enabling.resultset.ResultSet#close()
	 */
	@Override
	public void close() {
		open = false;
	}

	public void setOpen(final boolean open) {
		this.open = open;
	}

	@Override
	public void addObserver(final ResultSetRegistry observer) {
		final DelegationObserver delegate = new DelegationObserver(this, observer);
		observers.put(observer, delegate);
		observable.addObserver(delegate);
	}

	protected void notifyObservers() {
		observable.notifyObservers();
	}

	protected void notifyObservers(final Object arg) {
		observable.notifyObservers(arg);
	}

	public int countObservers() {
		return observable.countObservers();
	}

	public void deleteObserver(final java.util.Observer observer) {
		observable.deleteObserver(observer);
	}

	public void deleteObserver(ResultSetRegistry observer) {
		DelegationObserver delegate = observers.get(observer);
		if(delegate != null) {
			observable.deleteObserver(delegate);
		}
	}	

	public void deleteObservers() {
		observable.deleteObservers();
	}

}
