/*
 * History
 * Created on Aug 23, 2004 by kofke
 */
package etomica.atom.iterator;

import etomica.Atom;
import etomica.AtomsetIterator;
import etomica.IteratorDirective.Direction;
import etomica.action.AtomsetAction;
import etomica.action.AtomsetActionAdapter;
import etomica.action.AtomsetCount;
import etomica.atom.AtomsetFilter;

/**
 * Wraps an AtomIterator and filters its iterates so that
 * only those meeting specified criteria are returned.
 */

public class AtomsetIteratorFiltered implements AtomsetIteratorTargetable, AtomsetIteratorDirectable {
	/**
	 * Default constructor that causes no atoms to be filtered.
	 * Iterator will give all iterates of the given iterator
	 * until another filter is specified.
	 */
	public AtomsetIteratorFiltered(AtomsetIterator iterator) {
		this(iterator, AtomsetFilter.ACCEPT_ALL);
	}
	
	/**
	 * Returns the iterates of the given iterator that meet
	 * the critertia of the given filter.
	 * @param iterator
	 * @param filter
	 */
	public AtomsetIteratorFiltered(AtomsetIterator iterator, AtomsetFilter filter) {
		this.iterator = iterator;
		this.filter = filter;
		nextAtoms = new Atom[iterator.nBody()];
	}

	
	/**
	 * Returns true if the iterator contains the given atom and
	 * atom meets the filter's criteria.
	 */
	public boolean contains(Atom[] atom) {
		return filter.accept(atom) && iterator.contains(atom);
	}
	
	/**
	 * Indicates whether iterator has another iterate to return.
	 */
	public boolean hasNext() {
		return next != null;
	}

	/**
	 * Puts iterator in state ready for iteration.
	 */
	public void reset() {
		iterator.reset();
		next = null;
		while(iterator.hasNext() && next == null) {
			next = iterator.next();
			if(!filter.accept(next)) next = null;
		}
	}

	/**
	 * Sets iterator so that hasNext returns false.
	 */
	public void unset() {
		iterator.unset();
		next = null;
	}

	/**
	 * Returns the next atom from the iterator that meets the 
	 * filter's criteria.
	 */
	public Atom[] next() {
		System.arraycopy(next,0,nextAtoms,0,nextAtoms.length);
		next = null;
		while(iterator.hasNext() && next == null) {
			next = iterator.next();
			if(!filter.accept(next)) next = null;
		}
		return nextAtoms;
	}
	
	/**
	 * Returns next atom without advancing the iterator.
	 */
	public Atom[] peek() {
		return next;
	}

	/**
	 * Performs the given action on all atoms from iterator that 
	 * meet the filter criteria.
	 */
	public void allAtoms(AtomsetAction action) {
		iterator.allAtoms(actionWrapper(filter, action));
	}

	/**
	 * Returns the number of iterates given by the
	 * iterator that meet the criteria of the 
	 * filter.
	 */
	public int size() {
		AtomsetCount counter = new AtomsetCount();
		allAtoms(counter);
		return counter.callCount();
	}
	
	public final int nBody() {
		return iterator.nBody();
	}
	
	
	
	/**
	 * @return Returns the filter.
	 */
	public AtomsetFilter getFilter() {
		return filter;
	}
	/**
	 * @param filter The filter to set.
	 */
	public void setFilter(AtomsetFilter filter) {
		this.filter = filter;
	}
	
	/**
	 * @return the iterator wrapped by this filter.
	 */
	public AtomsetIterator getIterator() {
		return iterator;
	}
	
	public void setDirection(Direction direction) {
		if (iterator instanceof AtomsetIteratorDirectable) {
		    ((AtomsetIteratorDirectable)iterator).setDirection(direction);
        }
	}
	public void setTarget(Atom[] targetAtoms) {
	    if (iterator instanceof AtomsetIteratorTargetable) {
	        ((AtomsetIteratorTargetable)iterator).setTarget(targetAtoms);
        }
	}

	private final AtomsetIterator iterator;
	private AtomsetFilter filter;
	private Atom[] next;
	private final Atom[] nextAtoms;

	/**
	 * Returns a new action that wraps the given action such that action is performed
	 * only on the atoms meeting the filter's criteria.
	 */
	private static AtomsetAction actionWrapper(final AtomsetFilter filter, final AtomsetAction action) {
		return new AtomsetActionAdapter() {
			public void actionPerformed(Atom[] atom) {
				if(filter.accept(atom)) action.actionPerformed(atom);
			}
		};
	}
}

@@ -451,16 +451,99 @@
 d.%0A */%0A%0A
+//XXX this shouldn't have to be AtomsetIteratorBasisDependent, but as used it does%0A
 public c
@@ -634,16 +634,60 @@
 rectable
+, %0A            AtomsetIteratorBasisDependent
  %7B%0A%09/**%0A
@@ -888,32 +888,46 @@
 (AtomsetIterator
+BasisDependent
  iterator) %7B%0A%09%09t
@@ -1112,24 +1112,24 @@
 filter%0A%09 */%0A
-
 %09public Atom
@@ -1155,32 +1155,46 @@
 (AtomsetIterator
+BasisDependent
  iterator, Atoms
@@ -2950,18 +2950,178 @@
 ;%0A%09%7D%0A%09%0A%09
-%0A%09
+public int basisSize() %7B%0A        return iterator.basisSize();%0A    %7D%0A    %0A    public void setBasis(Atom%5B%5D atoms) %7B%0A        iterator.setBasis(atoms);%0A    %7D%0A        
 %0A%09/**%0A%09 
@@ -3693,28 +3693,24 @@
  %7B%0A%09    
-if (
 iterator
  instanc
@@ -3705,95 +3705,8 @@
 ator
- instanceof AtomsetIteratorTargetable) %7B%0A%09        ((AtomsetIteratorTargetable)iterator)
 .set
@@ -3726,26 +3726,16 @@
 Atoms);%0A
-        %7D%0A
 %09%7D%0A%0A%09pri
@@ -3760,16 +3760,30 @@
 Iterator
+BasisDependent
  iterato

make this basis dependent and require sub-iterator to be basis dependent because (as commonly used) it has to be that.