package simulate;

import java.util.Random;

public final class IntegratorVelocityVerlet extends IntegratorMD {

    AtomPair.Iterator.All pairIterator;
    Atom.Iterator atomIterator;
    AtomPair.Action forceSum;
    
    //Fields for Andersen thermostat
    Random random = new Random();
    double nu = 0.001;  //heat bath "collision" frequency
                
    public IntegratorVelocityVerlet() {
        super();
    }

    public void registerPhase(Phase p) {
        super.registerPhase(p);
        pairIterator = p.iteratorFactory().makeAtomPairIteratorAll();
        atomIterator = p.iteratorFactory().makeAtomIteratorUp();
//        spacePotential = (PotentialHard)p.space().makePotential(p);
    }
    
    public void setController(Controller c) {
        super.setController(c);
    //anonymous class; cannot do in constructor because need simulation()
        forceSum = new AtomPair.Action() {
            private Space.Vector f = Simulation.space.makeVector();
            public void action(AtomPair pair) {
                f.E(((PotentialSoft)Simulation.getPotential(pair)).force(pair));
                ((Agent)pair.atom1().ia).force.PE(f);
                ((Agent)pair.atom2().ia).force.ME(f);
            }
        };
    }
 
  private double t2;
  public final void setTimeStep(double t) {
    super.setTimeStep(t);
    t2 = timeStep*timeStep;
  }
  

          
//--------------------------------------------------------------
// steps all particles across time interval tStep

    public void doStep() {

        atomIterator.reset();              //reset iterator of atoms
        while(atomIterator.hasNext()) {    //loop over all atoms
            Atom a = atomIterator.next();  //  advancing positions full step
            Agent agent = (Agent)a.ia;     //  and momenta half step
            Space.Vector r = a.position();
            Space.Vector p = a.momentum();
            p.PEa1Tv1(0.5*timeStep,agent.force);  // p += f(old)*dt/2
            r.PEa1Tv1(timeStep*a.rm(),p);         // r += p*dt/m
            agent.force.E(0.0);
        }
        pairIterator.allPairs(forceSum);    //compute forces on each atom
        atomIterator.reset();
        while(atomIterator.hasNext()) {     //loop over atoms again
            Atom a = atomIterator.next();   //  finishing the momentum step
            a.momentum().PEa1Tv1(0.5*timeStep,((Agent)a.ia).force);  //p += f(new)*dt/2
        }
        if(isothermal) {  //Andersen thermostat
            atomIterator.reset();
            double nut = nu*timeStep;
            while(atomIterator.hasNext()) {
                Atom a = atomIterator.next();
                if(random.nextDouble() < nut) a.randomizeMomentum(temperature);  //this method in Atom needs some work
            }
        }
        return;
    }
    
    public void setAndersenNu(double n) {nu = n;}
    public double getAndersenNu() {return nu;}
            
//--------------------------------------------------------------

    public void initialize() {
        deployAgents();
        atomIterator.reset();
        while(atomIterator.hasNext()) {
            Atom a = atomIterator.next();
            Agent agent = (Agent)a.ia;
            agent.force.E(0.0);
        }
        pairIterator.allPairs(forceSum);
    }
              
//--------------------------------------------------------------

    public final Integrator.Agent makeAgent(Atom a) {
        return new Agent(a);
    }
            
    public final static class Agent implements Integrator.Agent {  //need public so to use with instanceof
        public Atom atom;
        public Space.Vector force;

        public Agent(Atom a) {
            atom = a;
            force = Simulation.space.makeVector();
        }
    }
}

