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

import etomica.Atom;
import etomica.AtomIterator;
import etomica.AtomSet;
import etomica.AtomTreeNodeGroup;
import etomica.IteratorDirective;

/**
 * Returns iterates from from the childList of a single basis atom.  Behavior is set
 * via iterator directive: if no atom is specified there, all pairs formed from the childList
 * are given; otherwise, if an atom is specified there, pairs will be formed from the
 * childList atoms with the basis' child from which the target atom is descended.  
 */
public final class ApiIntragroup extends AtomPairIteratorAdapter implements
		AtomsetIteratorBasisDependent, AtomsetIteratorDirectable, ApiComposite {

	/**
	 * Constructor makes iterator that must have basis specified and then be 
	 * reset() before iteration.
	 */
	public ApiIntragroup() {
		this(new ApiInnerVariable(
					new AtomIteratorBasis(),
					new AtomIteratorSequenceDirectable()));
        ((AtomIteratorSequenceDirectable)aiInner).setNumToSkip(1);
    }
    
    public ApiIntragroup(ApiComposite pairIterator) {
        super(pairIterator);
		aiOuter = (AtomIteratorBasis)pairIterator.getOuterIterator();
		aiInner = (AtomsetIteratorDirectable)pairIterator.getInnerIterator();
	}

	public void setTarget(AtomSet targetAtoms) {
        if(targetAtoms == null) throw new IllegalArgumentException("Cannot set target to null; use AtomSet.NULL");
		aiOuter.setTarget(targetAtoms);
        oneTarget = targetAtoms.count() != 0 && targetAtoms.getAtom(0) != null && (targetAtoms.count() == 1 || targetAtoms.getAtom(1) == null);
	}
    
    public boolean haveTarget(AtomSet targetAtoms) {
        for(int i=targetAtoms.count()-1; i>=0; i--) {
            if(!aiOuter.haveTarget(targetAtoms.getAtom(i))) return false;
        }
        return true;
    }
	
	/**
	 * Puts iterator in a state to begin iteration.
	 */
	public void reset() {
        //iterate in the prescribed direction from the target
        if(oneTarget) aiInner.setDirection(direction);
        
        //no target given -- iterate over all pairs
        else aiInner.setDirection(IteratorDirective.UP);
        iterator.reset();
	}

	/**
	 * Specifies the parent atom of the iterates. Pairs given by the
	 * iterator will be formed from the childList of the first atom
	 * in the given array. If argument is null or otherwise does not
	 * specify a childList, iterator will not return iterates 
	 * (hasNext false) until a valid basis is specified.  Length of given
	 * array should match the value returned by setBasis, but if it
	 * is greater no error results; only first atom in array is used.
	 */
	public void setBasis(AtomSet atoms) {
		basis = (atoms != null) ? (Atom)atoms : null;
		aiOuter.setBasis(atoms);
	}
	
    /**
     * Specifies the direction, which applies only if iterating pairs
     * with a target atom; otherwise, if all pairs from group are indicated,
     * direction is ignored.
     */
	public void setDirection(IteratorDirective.Direction direction) {
        this.direction = direction;
	}
	
	/**
	 * Returns the number of atom pairs the iterator will return if
	 * reset and iterated in its present state.
	 */
	public int size() {
		int n = ((AtomTreeNodeGroup)basis.node).childList.size();
		return oneTarget ? n-1 : n*(n-1)/2;
	}
	
	/**
	 * Returns 1, indicating the the array given to the setBasis method
	 * should have only one element.
	 */
	public int basisSize() {
		return 1;
	}
	
    public AtomIterator getInnerIterator() {
        return (AtomIterator)aiInner;
    }
    
    public AtomIterator getOuterIterator() {
        return aiOuter;
    }
    
	private final AtomIteratorBasis aiOuter;//local, specifically typed copy
	private final AtomsetIteratorDirectable aiInner;//local, specifically typed copy
	private boolean oneTarget;
    private IteratorDirective.Direction direction;
	private Atom basis;//atom most recently specified in setBasis; used by size() and contains(Atom[]) methods
}

@@ -98,16 +98,24 @@
 ica.Atom
+Iterator
 ;%0Aimport
@@ -123,32 +123,61 @@
 etomica.Atom
-Iterator
+Set;%0Aimport etomica.IteratorDirective
 ;%0Aimport eto
@@ -173,39 +173,52 @@
 %0Aimport etomica.
+action.
 Atom
-S
+s
 et
+Action
 ;%0Aimport etomica
@@ -222,59 +222,64 @@
 ica.
-AtomTreeNodeGroup;%0Aimport etomica.IteratorDirective
+action.AtomsetCount;%0Aimport etomica.action.AtomsetDetect
 ;%0A%0A/
@@ -2759,56 +2759,8 @@
 ) %7B%0A
-%09%09basis = (atoms != null) ? (Atom)atoms : null;%0A
 %09%09ai
@@ -3232,107 +3232,1059 @@
 ) %7B%0A
-%09%09int n = ((AtomTreeNodeGroup)basis.node).childList.size();%0A%09%09return oneTarget ? n-1 : n*(n-1)/2;%0A%09
+        if(counter == null) %7B%0A            counter = new AtomsetCount();%0A        %7D else %7B%0A            counter.reset();%0A        %7D%0A        allAtoms(counter);%0A        return counter.callCount();%0A%09%7D%0A    %0A    public boolean contains(AtomSet atoms) %7B%0A        if(atoms == null %7C%7C atoms.count() != 2) return false;%0A        if(detector == null) %7B%0A            detector = new AtomsetDetect(atoms);%0A        %7D else %7B%0A            detector.setAtoms(atoms);%0A            detector.reset();%0A        %7D%0A        allAtoms(detector);%0A        return detector.detectedAtom();%0A    %7D%0A    %0A    /**%0A     * Performs action on all iterates for iterator as currently%0A     * conditioned (basis, target, direction).  Clobbers%0A     * iteration state.%0A     */%0A    public void allAtoms(AtomsetAction action) %7B%0A        //iterate in the prescribed direction from the target%0A        if(oneTarget) aiInner.setDirection(direction);%0A        %0A        //no target given -- iterate over all pairs%0A        else aiInner.setDirection(IteratorDirective.UP);%0A        %0A        super.allAtoms(action);%0A    
 %7D%0A%09%0A
@@ -4848,17 +4848,20 @@
 ection;%0A
-%09
+    
 private 
@@ -4868,101 +4868,60 @@
 Atom
- basis;//atom most recently specified in setBasis; used by size() and contains(Atom%5B%5D) methods
+setCount counter;%0A    private AtomsetDetect detector;
 %0A%7D%0A

fix error with size, contains, allAtoms that were uncovered by unit test