/*
 * History
 * Created on Nov 21, 2004 by kofke
 */
package etomica.nbratom.cell;

import etomica.Atom;
import etomica.Integrator;
import etomica.Phase;
import etomica.PhaseEvent;
import etomica.PhaseListener;
import etomica.SimulationEvent;
import etomica.Space;
import etomica.atom.AtomPositionDefinition;
import etomica.atom.AtomPositionDefinitionSimple;
import etomica.atom.iterator.AtomIteratorAllMolecules;
import etomica.atom.iterator.AtomIteratorPhaseDependent;
import etomica.lattice.CellLattice;

/**
 * Class that defines and manages construction and use of lattice of cells 
 * for cell-based neighbor listing.
 */

//TODO modify assignCellAll to loop through cells to get all atoms to be assigned
//no need for index when assigning cell
//different iterator needed

public class NeighborCellManager implements Integrator.IntervalListener {

    private final CellLattice lattice;
    private final Space space;
    private final AtomIteratorPhaseDependent atomIterator;
    private int iieCount;
    private int updateInterval;
    private int priority;
    private final AtomPositionDefinition positionDefinition;
    
    /**
     * Constructs manager for neighbor cells in the given phase.  The number of
     * cells in each dimension is given by nCells. 
     */
    public NeighborCellManager(Phase phase, int nCells) {
        this(phase,nCells,new AtomPositionDefinitionSimple());
    }
    
    public NeighborCellManager(Phase phase, int nCells, AtomPositionDefinition positionDefinition) {
        this.positionDefinition = positionDefinition;
        space = phase.space();
        atomIterator = new AtomIteratorAllMolecules(phase);
        setPriority(150);
        setUpdateInterval(1);

        lattice = new CellLattice(phase.boundary().dimensions(), NeighborCell.FACTORY);
        phase.setLattice(lattice);
        int[] size = new int[space.D()];
        for(int i=0; i<space.D(); i++) size[i] = nCells;
        lattice.setSize(size);

        //listener to phase to detect addition of new SpeciesAgent
        //or new atom
        phase.speciesMaster.addListener(new PhaseListener() {
            public void actionPerformed(SimulationEvent evt) {
                actionPerformed((PhaseEvent)evt);
            }
           public void actionPerformed(PhaseEvent evt) {
                if(evt.type() == PhaseEvent.ATOM_ADDED) {
                    Atom atom = evt.atom();
                    //new species agent requires another list in each cell
                    if(atom.type.getNbrManagerAgent().getPotentials().length > 0) {
                        assignCell(atom);
                    }
                }
            }
        });
    }

    /**
     * @return the lattice of cells.
     */
    public CellLattice getCellLattice() {
        return lattice;
    }
    
    /**
     * Assigns cells to all molecules in the phase.
     */
    public void assignCellAll() {
        atomIterator.reset();
        while(atomIterator.hasNext()) {
            assignCell(atomIterator.nextAtom());
        }
    }
    
    /**
     * Assigns the cell for the given atom.
     * @param atom
     */
    public void assignCell(Atom atom) {
        AtomSequencerCell seq = (AtomSequencerCell)atom.seq;
        NeighborCell newCell = (NeighborCell)lattice.site(positionDefinition.position(atom));
        if(newCell != seq.cell) {assignCell(seq, newCell);}
    }
    
    /**
     * Assigns atom sequencer to given cell in the list of the given index.
     */
    public void assignCell(AtomSequencerCell seq, NeighborCell newCell) {
        if(seq.cell != null) seq.cell.occupants().remove(seq.nbrLink);
        seq.cell = newCell;
        if(newCell != null) {
            newCell.occupants().add(seq.nbrLink);
        }
    }//end of assignCell
    
    public void intervalAction(Integrator.IntervalEvent event) {
        if (event.type() == Integrator.IntervalEvent.INITIALIZE) {
            assignCellAll();
        }
        else if (event.type() == Integrator.IntervalEvent.INTERVAL) {
            if (--iieCount == 0) {
                assignCellAll();
                iieCount = updateInterval;
            }
        }
    }
    
    public void setUpdateInterval(int updateInterval) {
        this.updateInterval = updateInterval;
    }

    public int getUpdateInterval() {
        return updateInterval;
    }
    
    /**
     * @return the priority of this as an integrator interval-listenter (default is 150)
     */
    public int getPriority() {
        return priority;
    }

    /**
     * @param priority the priority of this as an integrator interval-listenter (default is 150)
     */
    public void setPriority(int priority) {
        this.priority = priority;
    }
}//end of NeighborCellManager

@@ -103,35 +103,8 @@
 om;%0A
-import etomica.Integrator;%0A
 impo
@@ -422,30 +422,20 @@
 Iterator
-PhaseDependent
+Tree
 ;%0Aimport
@@ -774,47 +774,8 @@
 ger 
-implements Integrator.IntervalListener 
 %7B%0A%0A 
@@ -877,119 +877,25 @@
 ator
-PhaseDependent atomIterator;%0A    private int iieCount;%0A    private int updateInterval;%0A    private int priority
+Tree atomIterator
 ;%0A  
@@ -1467,82 +1467,106 @@
 ator
-AllMolecules(phase);%0A        setPriority(150);%0A        setUpdateInterval(1
+Tree();%0A        atomIterator.setDoAllNodes(true);%0A        atomIterator.setRoot(phase.speciesMaster
 );%0A%0A
@@ -2533,138 +2533,8 @@
  %7D%0A%0A
-    /**%0A     * @return the lattice of cells.%0A     */%0A    public CellLattice getCellLattice() %7B%0A        return lattice;%0A    %7D%0A    %0A
     
@@ -2709,35 +2709,36 @@
             
-assignCell(
+Atom atom = 
 atomIterator
@@ -2748,18 +2748,142 @@
 xtAtom()
-)
 ;
+%0A            if (atom.type.getNbrManagerAgent().getPotentials().length %3E 0) %7B%0A                assignCell(atom);%0A            %7D
 %0A       
@@ -3630,948 +3630,8 @@
    %0A
-    public void intervalAction(Integrator.IntervalEvent event) %7B%0A        if (event.type() == Integrator.IntervalEvent.INITIALIZE) %7B%0A            assignCellAll();%0A        %7D%0A        else if (event.type() == Integrator.IntervalEvent.INTERVAL) %7B%0A            if (--iieCount == 0) %7B%0A                assignCellAll();%0A                iieCount = updateInterval;%0A            %7D%0A        %7D%0A    %7D%0A    %0A    public void setUpdateInterval(int updateInterval) %7B%0A        this.updateInterval = updateInterval;%0A    %7D%0A%0A    public int getUpdateInterval() %7B%0A        return updateInterval;%0A    %7D%0A    %0A    /**%0A     * @return the priority of this as an integrator interval-listenter (default is 150)%0A     */%0A    public int getPriority() %7B%0A        return priority;%0A    %7D%0A%0A    /**%0A     * @param priority the priority of this as an integrator interval-listenter (default is 150)%0A     */%0A    public void setPriority(int priority) %7B%0A        this.priority = priority;%0A    %7D%0A
 %7D//e

make NeighborCellManager not an IntervalEventListener, assign only atoms associated with a concrete potential