package simulate;
import java.awt.*;

 /**
  * Each instance of the class Atom holds the coordinates of one physical atom; 
  * all simulation kinetics and dynamics are performed by operating on these values.
  * The coordinate is provided the the Space object when the atom is instantiated.
  * In addition, an Atom has a type that holds information such as how it is drawn,
  * and possibly information to be used as parameters in a potential.  Each atom
  * holds an Integrator.Agent object that may be used to store information needed
  * by the integrator.
  * Atoms are constructed when Species creates a Molecule.
  * 
  * @author David Kofke
  * @author C. Daniel Barnes
  * @version 2.0
  * @since 1.0
  * @see Molecule
  */
public final class Atom implements Space.Occupant {

    /**
     * Constructs an atom with default values for mass, diameter, and color.
     * Default values are mass = 1.0 amu; diameter = 0.1 A; color = black.
     * Defaults for all coordinates and momenta are zero.
     * 
     * @param parent molecule in which atom resides
     * @param index sequential index of atom as assigned by parent molecule
     * @param t the type of the atom
     */
    public Atom(Molecule parent, AtomType t, int index) {
        parentMolecule = parent;
        type = t;
        atomIndex = index;
        coordinate = Simulation.space.makeCoordinate(this);
        r = coordinate.position();
        p = coordinate.momentum();
        workVector = coordinate.makeVector();
        rLast = coordinate.makeVector();
        velocity = coordinate.makeVector();
        setStationary(false);
        useTypeColor();
// don't know why this is needed        if(sim.hasIntegrator()) setIntegratorAgent(sim.integrator().makeAgent(this));
    }
                    
    /**
     * Assigns the atom's integrator agent to the given instance
     * 
     * @param ia
     */
    public void setIntegratorAgent(Integrator.Agent ia) {this.ia = ia;}
        
    /**
     * Molecule that contains this atom
     */
    public final Molecule parentMolecule() {return parentMolecule;}

    /**
     * Phase in which this atom resides
     */
    public final Phase parentPhase() {return parentMolecule.parentPhase();}
        
    /**
     * Coordinates of this atom.
     * When the atom is constructed the coordinate class is provided by the 
     * governing Space for the simulation.
     */
    public final Space.Coordinate coordinate() {return coordinate;}
            
    /**
     * Index identifying the species of this atom's molecule.
     */
    public final int speciesIndex() {return parentMolecule.speciesIndex();}

    /**
     * Integer assigned to this atom by its parent molecule.
     * Assigned during construction of atom.
     */
    public final int atomIndex() {return atomIndex;}
        
    /**
     * The color of the atom when drawn to the screen.
     * The atom color is often decided by the ColorScheme class, via the atom's setColor method.
     * If the ColorScheme does nothing, the color is given by the atom's type field.
     * 
     * @see ColorScheme
     */
    public final Color getColor() {return color;}

    /**
     * Sets the color of the atom for drawing to the screen.
     * 
     * @param c
     * @see ColorScheme
     * @see getColor
     */
    public final void setColor(Color c) {this.color = c;}

    /**
     * Sets the color of the atom to be whatever is returned by the atom's type field.
     */
    public final void useTypeColor() {this.color = type.color();}  //indicates that atom color is determined by its type
        
    /**
     * Sets the atom to be stationary or movable.
     * The atom does not enforce the condition of being stationary, in that it does not
     * override attempts to move it if it is set as stationary.  Rather, this is a flag
     * that can be set and checked by an integrator when deciding how or whether to 
     * move the atom.
     * 
     * @param b If true, the atom becomes stationary, if false it can be moved.
     */
    public void setStationary(boolean b) {
        stationary = b;
        if(!stationary) scaleMomentum(0.0);
    }

    /**
     * Flag to indicate of the atom can or cannot be moved
     * 
     * @see setStationary
     */
    public final boolean isStationary() {return stationary;}

    /**
     * Sets atom following this one in linked list, and sets this to be that atom's previous atom in list
     * 
     * @param atom the next atom in the list
     */
    public final void setNextAtom(Atom atom) {
        nextAtom = atom;
        if(atom!=null) {atom.previousAtom = this;}
    }

    /**
     * Sets this atom's previous atom to be null
     * 
     * @since 1.0
     * @see setNextAtom
     */
    public final void clearPreviousAtom() {previousAtom = null;}

    /**
     * @return the next atom in the linked list of atoms
     * @see setNextAtom
     */
    public final Atom nextAtom() {return nextAtom;}

    /**
     * @return the previous atom in the linked list of atoms
     * @see setNextAtom
     */
    public final Atom previousAtom() {return previousAtom;} 
    
    /**
     * @return the first atom on the next molecule in the linked list of molecules
     * @deprecated This method is useful for terminating loops of the molecule's atoms.  The
     * molecule's atom iterator is the preferred method.
     */
    public final Atom nextMoleculeFirstAtom() {return parentMolecule.lastAtom.nextAtom();}  //first atom on next molecule

    /**
     * @return the last atom on the previous molecule in the linked list of molecules
     * @see nextMoleculeFirstAtom
     * @deprecated
     */
    public final Atom previousMoleculeLastAtom() {return parentMolecule.firstAtom.previousAtom();}  //first atom on next molecule

    /**
     * @return mass of the atom, in atomic mass units (amu)
     */
    public final double mass() {return type.mass();}

    /**
     * @return reciprocal of the mass of the atom
     */
    public final double rm() {return type.rm();}

    /**
     * Draws the atom to the given graphics object
     * 
     * @param g
     * @param origin
     * @param scale
     */
    public void draw(Graphics g, int[] origin, double scale) {type.draw(g, origin, scale, this);}

    /**
     * Moves the atom by some vector distance
     * 
     * @param u
     */
    public final void translateBy(Space.Vector u) {r.PE(u);}
    /**
     * Moves the atom by some vector distance
     * 
     * @param u
     */
    public final void translateBy(double d, Space.Vector u) {r.PEa1Tv1(d,u);}
    /**
     * Moves the atom by some vector distance
     * 
     * @param u
     */
    public final void translateTo(Space.Vector u) {r.E(u);}      
    public final void translateToRandom(simulate.Phase p) {translateTo(p.boundary().randomPosition());}
    public final void displaceBy(Space.Vector u) {rLast.E(r); translateBy(u);}
    public final void displaceBy(double d, Space.Vector u) {rLast.E(r); translateBy(d,u);}
    public final void displaceTo(Space.Vector u) {rLast.E(r); translateTo(u);}  
    public final void displaceWithin(double d) {workVector.setRandomCube(); displaceBy(d,workVector);}
    public final void displaceToRandom(simulate.Phase p) {rLast.E(r); translateToRandom(p);}
    public final void replace() {r.E(rLast);}
//    public final void inflate(double s) {r.TE(s);}

    public final void accelerateBy(Space.Vector u) {p.PE(u);}
    public final void accelerateBy(double d, Space.Vector u) {p.PEa1Tv1(d,u);}

    public final double kineticEnergy() {return coordinate.kineticEnergy(type.mass());}
    public final void randomizeMomentum(double temperature) {  //not very sophisticated; random only in direction, not magnitude
        double magnitude = Math.sqrt(type.mass()*temperature*(double)Simulation.D);  //need to divide by sqrt(m) to get velocity
        p.setRandomSphere();
        p.TE(magnitude);
    }
    public final void scaleMomentum(double scale) {p.TE(scale);}

    public final Space.Vector position() {return r;}
    public final Space.Vector momentum() {return p;}
    public final double position(int i) {return r.component(i);}
    public final double momentum(int i) {return p.component(i);}
    public final Space.Vector velocity() {velocity.E(p); velocity.TE(type.rm()); return velocity;}  //returned vector is not thread-safe

    public Integrator.Agent ia;
        
    /**
    * Color of the atom when drawn on the screen
    * This color is set by the colorScheme object in the atom's species
    */
    Color color = Color.black;
        
    /**
    * Flag indicating whether atom is stationary or mobile.
    * Default is false (atom is mobile)
    */
    private boolean stationary;
       
    /**
    * Instance of molecule in which this atom resides.
    * Assigned in Atom constructor.
    */
//    final Molecule parentMolecule;
     Molecule parentMolecule;
        
    /**
    * Identifier of atom within molecule.
    * Assigned by parent molecule when invoking Atom constructor.
    */
//    final int atomIndex;
     int atomIndex;
    
    private Atom nextAtom, previousAtom;
        
    public final Space.Coordinate coordinate;
    public  Space.Vector r, p;  //position, momentum
        
    public  AtomType type;
    public  Space.Vector workVector, rLast, velocity;
    
 /** This is an array of AtomLinkers that enable lists of atoms to be constructed
  *  and associated with this atom.  Useful for setting up neighbor lists, for example.
  *  Each element of this array points to the first linker in some linked list of atoms.
  *  In many situations, this array is not used at all.
  *  @see Iterator.FixedNeighbors
  */
    public Atom.Linker[] atomLink;
 
 /**
  * Class that is passed to an Iterator to specify an action to be performed on all
  * of the atoms that would be given by a full sweep of the iterator.
  * 
  * @author David A. Kofke
  * @version 2.0
  * @since 2.0
  * @see Atom.Iterator
  */
    public interface Action {

        /**
         * Method called by the iterator, once for each of its atoms.
         * 
         * @param a Atom passed to method by iterator
         */
        public void action(Atom a);
    }
 /**
  * Base interface for atom iterators.
  * Atom iterators yield a sequence of atoms in successive calls to the next() method.
  * When all atoms have been returned, hasNext() returns false.
  */
    public interface Iterator {        

        /**
         * @return true if the iterator will return another atom with a subsequent call to next(), false otherwise
         */
        public boolean hasNext();

        /**
         * @return the next atom in the list
         */
        public Atom next();

        /**
         * Resets the iterator in reference to the given atom.
         * Exactly how the given atom affects the reset depends on the particular iterator.
         * 
         * @param a
         */
        public void reset(Atom a);

        /**
         * Resets the iterator, so that it is ready to go through its list again.
         */
        public void reset();

        /**
         * Performs the given Action on each atom in the list in sequence.
         * 
         * @param act
         * @since 2.0
         * @see Atom.Action
         */
        public void allAtoms(Action act);
            
//        public Phase phase();
//        public Atom first();  //simply returns the first atom, without affecting status of iterator
    
    /**
     * Iterator that expires after returning a single atom
     */
        public static final class Singlet implements Iterator {
            private Atom atom;
            private boolean hasNext;
            public Singlet() {hasNext = false;}
            public Singlet(Atom a) {reset(a);}
            public boolean hasNext() {return hasNext;}
            public void reset() {hasNext = (atom != null);}
            public void reset(Atom a) {atom = a; reset();}
            public Atom next() {hasNext = false; return atom;}
            public void allAtoms(Atom.Action act) {act.action(atom);}
        }
        
        /**
         * Iterator that progresses up a list of atoms.
         */
        public static class Up implements Iterator {
            protected Atom atom, nextAtom;
            protected boolean hasNext;
            private Phase phase;
            public Up(Phase p) {phase = p; hasNext = false;}
            public Up(Phase p, Atom a) {phase = p; reset(a);}
            public Phase phase() {return phase;}
            public boolean hasNext() {return hasNext;}
            public void reset(Atom a) {
                atom = a;
                if(a == null) {hasNext = false; return;}
                hasNext = true;
            }
            public void reset() {reset(phase.firstAtom());}
            public Atom next() {
                nextAtom = atom;
                atom = atom.nextAtom();
                hasNext = (atom != null);
                return nextAtom;
            }
            public void allAtoms(Atom.Action act) {
                for(Atom a=atom; a!=null; a=a.nextAtom()) {act.action(a);}
            }
        } //end of Atom.Iterator.Up
        
        /**
         * Iterates over all neighbors uplist from given atom.
         * This iterator defines <i>all</i> atoms in phase as neighbors, so it
         * simply extends the Up iterator, modifying the reset method to start with
         * the atom following the given atom, rather than the given atom itself.
         */
        public static final class UpNeighbor extends Up {
            private Atom first;
            public UpNeighbor(Phase p) {super(p);}
            public UpNeighbor(Phase p, Atom a) {super(p,a);}
            public void reset(Atom a) {
                atom = a;
                if(a == null) {hasNext = false; return;}
                first = a.nextAtom();
                super.reset(first);
            }
            public void reset() {super.reset(first);}
        }
        
        public static class Down implements Iterator {
            protected Atom atom, nextAtom;
            protected boolean hasNext;
            private Phase phase;
            public Down(Phase p) {phase = p; hasNext = false;}
            public Down(Phase p, Atom a) {phase = p; reset(a);}
            public Phase phase() {return phase;}
            public boolean hasNext() {return hasNext;}
            public void reset(Atom a) {
                atom = a;
                if(a == null) {hasNext = false; return;}
                hasNext = true;
            }
            public void reset() {reset(phase.firstAtom());}
            public Atom next() {
                nextAtom = atom;
                atom = atom.previousAtom();
                if(atom == null) {hasNext = false;}
                return nextAtom;
            }
            public void allAtoms(Atom.Action act) {
                for(Atom a=atom; a!=null; a=a.previousAtom()) {act.action(a);}
            }
        } //end of Atom.Iterator.Down
        
        /**
         * Iterates over all neighbors downlist from given atom.
         * This iterator defines <i>all</i> atoms in phase as neighbors, so it
         * simply extends the Down iterator, modifying the reset method to start with
         * the atom preceding the given atom, rather than the given atom itself.
         * Also, the no-argument reset performs a reset using the current atom, rather than 
         * resetting to neighbor of first atom in phase.
         */
        public static final class DownNeighbor extends Down {
            private Atom first;
            public DownNeighbor(Phase p) {super(p);}
            public DownNeighbor(Phase p, Atom a) {super(p,a);}
            public void reset(Atom a) {
                atom = a;
                if(a == null) {hasNext = false; return;}
                first = a.previousAtom();
                super.reset(first);
            }
            public void reset() {super.reset(first);}
        }
        
    }
    
    /**
     * Class for constructing linked lists of Atoms
     * 
     * @author David A. Kofke
     * @version 2.0
     * @since 2.0
     */
    public static class Linker {
        private Atom atom;
        private Atom.Linker next;
        //Constructors
        public Linker() {}
        public Linker(Atom a) {atom = a;}
        public Linker(Atom a, Linker l) {atom = a; next = l;}
        //Access methods
        public final Atom atom() {return atom;}
        public final Atom.Linker next() {return next;}
        public final void setNext(Atom.Linker l) {next = l;}
        public final void setAtom(Atom a) {atom = a;}
    }
}