/*
!=======================================================================
!
!  PROGRAM  PHASE-Viewer  (PHASE-Viewer 2014.01 ver.3.3.0)
!
!  Created on ----
!  AUTHOR(S): KOGA, Junichiro
!  File : AtomObject.java
!  
!  Contact address :  Phase System Consortium
!                     E-mail: phase_system@nims.go.jp URL https://azuma.nims.go.jp
!
!
!   Since 2002, this program set had been intensively developed as a part of the following 
!  national projects supported by the Ministry of Education, Culture, Sports, Science and
!  Technology (MEXT) of Japan; "Frontier Simulation Software for Industrial Science
!  (FSIS)" from 2002 to 2005, "Revolutionary Simulation Software (RSS21)" from 2006 to
!  2008. "Research and Development of Innovative Simulation Software (RISS)" from 2008
!  to 2013. These projects is lead by the Center for Research on Innovative Simulation 
!  Software (CISS), the Institute of Industrial Science (IIS), the University of Tokyo.
!   Since 2013, this program set has been further developed centering on PHASE System
!  Consortium. 
!   The activity of development of this program set has been supervised by Takahisa Ohno.
!
!=======================================================================
 */
package ciss.phase_viewer.acviewer.scenegraphelements.atom;

import java.awt.Font;
import java.awt.Point;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
import java.text.DecimalFormat;

import javax.media.j3d.Appearance;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.Bounds;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.ColoringAttributes;
import javax.media.j3d.Material;
import javax.media.j3d.Node;
import javax.media.j3d.OrientedShape3D;
import javax.media.j3d.RenderingAttributes;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.media.j3d.TransparencyAttributes;
import javax.swing.JLabel;
import javax.swing.JPopupMenu;
import javax.vecmath.Color3f;
import javax.vecmath.Point3d;
import javax.vecmath.Point3f;
import javax.vecmath.Vector3d;

import org.apache.log4j.Logger;

import ciss.phase_viewer.acviewer.BaseJ3DPanel;
import ciss.phase_viewer.acviewer.ConfigDataUpdateEvent;
import ciss.phase_viewer.acviewer.CylinderCreator;
import ciss.phase_viewer.acviewer.mouselistener.MouseMotionListenerAction;
import ciss.phase_viewer.acviewer.scenegraphelements.CapableText2D;
import ciss.phase_viewer.acviewer.scenegraphelements.CylinderCreatorTG;
import ciss.phase_viewer.acviewer.scenegraphelements.DynamicallyEditableTG;
import ciss.phase_viewer.acviewer.scenegraphelements.SceneGraphElement;
import ciss.phase_viewer.acviewer.scenegraphelements.TGAtom;
import ciss.phase_viewer.atomcoord.AtomCoords;
import ciss.phase_viewer.common.ElementInfo;
import ciss.phase_viewer.common.VectorOperations;
import ciss.phase_viewer.settings.GlobalProperties;
import ciss.phase_viewer.settings.PropertiesManager;

import com.sun.j3d.utils.geometry.Sphere;
import com.sun.j3d.utils.geometry.Text2D;

public class AtomObject extends DynamicallyEditableTG {
    private static Logger logger = Logger.getLogger(AtomObject.class.getName());

    private int mobile;

    private int weight;

    private int atomicNumber;

    private String ID;

    private double[] dp;

    private double[] force;

    private Vector3d pos;

    private Vector3d pos_buff;

    private Vector3d vforce;

    private Transform3D translate = new Transform3D();

    private int labelOffset = 0;

    private float mobilityFactor = 1.f;

    private float[] color = new float[3];

    private float scaleatom = 1.f;

    private float radius = 1.f;

    private int numNodes = 20;

    private Canvas3D canvas;

    private Atom atom;

    private boolean selected = false;

    private Circle circle;

    private BranchGroup forceArrow;

    private GlobalProperties gp = PropertiesManager
            .getGlobalProperties(PropertiesManager.PROPERTIES_ACV);

    private JPopupMenu popup = new JPopupMenu();

    private JLabel menuItem = new JLabel();

    private JLabel menuDist = new JLabel();

    public AtomObject(String ID, int atomicNumber, double[] dp, int mobile,
            int weight) {
        super();

        this.ID = ID;
        this.atomicNumber = atomicNumber;
        this.mobile = mobile;
        this.weight = weight;
        this.dp = dp;

        pos = new Vector3d(dp);
        pos_buff = new Vector3d(pos);
        translate.set(pos);
        translate.setScale(1.d);
        this.setTransform(translate);
        // set capabilities
        setCapability(TransformGroup.ALLOW_BOUNDS_READ);
        setCapability(TransformGroup.ALLOW_BOUNDS_WRITE);
    }

    public AtomObject(String ID, int atomicNumber, double[] dp, double[] force,
            int mobile, int weight) {
        super();

        this.ID = ID;
        this.atomicNumber = atomicNumber;
        this.mobile = mobile;
        this.weight = weight;
        this.dp = dp;
        this.force = force;

        pos = new Vector3d(dp);
        pos_buff = new Vector3d(pos);
        vforce = new Vector3d(force);

        translate.set(pos);
        translate.setScale(1.d);
        this.setTransform(translate);
        setCapability(TransformGroup.ALLOW_BOUNDS_READ);
        setCapability(TransformGroup.ALLOW_BOUNDS_WRITE);
    }

    private java.text.Format format = ciss.phase_viewer.common.ConstParameters.formater_smaller_digits;

    public void showPopup(int canvx, int canvy, double scale) {
        TransformGroup tg = getTransformBuffer();
        if (tg == null) {
            return;
        }

        Point loc = getLocationOnScreen(canvx, canvy, scale, tg);
        double[] cds = getCoordinatesInRealWorld(tg);
        menuItem.setText("atom No. " + ID + ", " + elementName + ", x: "
                + format.format(String.valueOf(cds[0])) + ", y: "
                + format.format(String.valueOf(cds[1])) + " z: "
                + format.format(String.valueOf(cds[2])));
        double[] origPos = getCoordinatesInRealWorld(this);
        double[] diff = { cds[0] - origPos[0], cds[1] - origPos[1],
                cds[2] - origPos[2] };

        String ori = format.format(String.valueOf(VectorOperations.norm(diff)));
        menuDist.setText("distance from original position: " + ori);
        popup.setLocation(loc.x, loc.y);

        if (!popup.isVisible()) {
            popup.setVisible(true);
        }
    }

    public Point getLocationOnScreen(int canvx, int canvy, double scale,
            TransformGroup atomTransform) {
        if (atomTransform == null) {
            return new Point(0, 0);
        }

        Transform3D tf3d = new Transform3D();
        parent.getTransform(tf3d);

        Transform3D atomTrans = new Transform3D();
        atomTransform.getTransform(atomTrans);
        Vector3d vec = new Vector3d();
        atomTrans.get(vec);

        Point3d atomvec = new Point3d();
        atomvec.x = vec.x;
        atomvec.y = vec.y;
        atomvec.z = vec.z;

        tf3d.transform(atomvec);

        int x = (int) ((atomvec.x + scale) * ((double) canvx) / (2.d * scale));
        int y = (int) ((-atomvec.y + scale) * ((double) canvy) / (2.d * scale));
        return new Point(x
                + ((BaseJ3DPanel) mACVD.getParent()).getLocationOnScreen().x, y
                + ((BaseJ3DPanel) mACVD.getParent()).getLocationOnScreen().y);
    }

    public void removePopup() {
        popup.setVisible(false);
    }

    String getAtomRenderingMethod() {
        return mACVD.getAtomRenderingMethod();
    }

    private BranchGroup getCopyOfAtom(float transparency, Transform3D transform) {
        Appearance app = createAppearance(transparency);
        Atom tmpatom = new Atom(radius * scaleatom, Sphere.GENERATE_NORMALS,
                numNodes, app, this);
        tmpatom.isTemp(true);

        TransformGroup group = new TransformGroup();
        group.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
        group.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
        group.setTransform(transform);
        group.addChild(tmpatom);

        AtomBufferBranchGroup ret = new AtomBufferBranchGroup();
        ret.addChild(group);
        ret.setTransformGroup(group);
        ret.setAtomObject(this);

        return ret;
    }

    private Appearance createAppearance(float transparency) {
        Appearance app = new Appearance();
        app.setCapability(Appearance.ALLOW_MATERIAL_READ);
        ColoringAttributes ca = new ColoringAttributes();
        RenderingAttributes ra = new RenderingAttributes();
        ra.setIgnoreVertexColors(true);
        app.setRenderingAttributes(ra);

        Material material = new Material();
        material.setCapability(Material.ALLOW_COMPONENT_READ);
        material.setDiffuseColor(new Color3f(color));
        material.setShininess(120.0f);
        app.setMaterial(material);

        TransparencyAttributes ta = new TransparencyAttributes(
                TransparencyAttributes.BLENDED, 0.0f);
        ta.setTransparency(transparency);
        app.setTransparencyAttributes(ta);

        return app;
    }

    private void setAttributes(boolean calscale) {
        if (mACVD.getText()) {
            this.setLabelOffset(mACVD.getLabelOffset());
        }

        int[] foo = TGAtom.getMobileAndWeight(mCD.getAuxils(),
                Integer.parseInt(ID) - 1);
        if (foo != null && foo.length >= 1)
            this.setMobile(String.valueOf(foo[0]));

        if (mACVD.getMobile()) {
            this.setMobilityDisplayMode(mACVD.getMobilityDisplayMode());
        }

        if (calscale) {
            // scaleatom = 2.2f * mACVD.getScaleAtom() / (float)
            // (Math.pow((float) (mCD.getNumAt()), 1.0f / 3.0f));
            scaleatom = mCD.getScaleAtom();
            // scaleatom = mACVD.getScaleAtom();
        }

        if (atomicNumber < 0)
            atomicNumber = 1;

        this.setColor(mACVD.getColor()[atomicNumber]);
        this.setRad(mACVD.getRad()[atomicNumber]);
        this.setNumNodes(mACVD.getNumNodesAtom());
    }

    public void setAttributes() {
        setAttributes(true);
    }

    // シーングラフを, mCDの変更とともに変更する.
    public void configDataUpdate() {
        // if ( mACVD.getParent().doRedraw() ) {
        // return;
        // }
        logger.debug("at configdata update");
        double lenmax = parent.getLenMax();
        double[] jusin = parent.getCOM();
        if (Integer.parseInt(ID) > mCD.getNumAt()) {
            // ((BranchGroup)getParent()).detach();
            // logger.error("error... wrong index");
            return;
        }
        // double [] pos_moto = parent.getPos(Integer.parseInt(ID)-1);
        double[] pos_moto = mCD.getPos(Integer.parseInt(ID) - 1);

        int atnumNow = mCD.getAtomicNumber(Integer.parseInt(ID) - 1);
        logger.debug("atom no. " + ID + " is updating.");
        for (int i = 0; i < 3; i++) {
            dp[i] = (pos_moto[i] - jusin[i]) / lenmax;
        }

        pos = new Vector3d(dp);

        if (!mCD.forceUpdate()) {
            if (forceArrow != null)
                forceArrow.detach();
            getArrowProps();
            force = mCD.getForce(Integer.parseInt(ID) - 1);
            if (force != null && drawArrow) {
                forceArrow = createForceArrow();
                if (forceArrow != null)
                    this.addChild(forceArrow);
            }
        }

        if (pos.epsilonEquals(pos_buff, 0.0001)
                && atnumNow == this.atomicNumber && !mCD.forceUpdate())
            return;

        pos_buff = new Vector3d(pos);
        translate.set(pos);
        this.setTransform(translate);
        if (atnumNow == this.atomicNumber && !mCD.forceUpdate()) {
            logger.debug("updating : " + ID);
            // force = mCD.getForce(Integer.parseInt(ID) - 1);
            // if (forceArrow != null) {
            // forceArrow.detach();
            // }
            // getArrowProps();
            // if ( force != null && drawArrow ) {
            // forceArrow = createForceArrow();
            // if ( forceArrow != null ) {
            // this.addChild(forceArrow);
            // }
            // }
        } else {
            logger.debug("recreating : " + ID);
            atomicNumber = atnumNow;
            removeAllChildren();
            // setAttributes(false);
            setAttributes();
            if (atomicNumber < 0)
                return;
            create();
            getArrowProps();
            if (force != null && drawArrow) {
                forceArrow = createForceArrow();
                if (forceArrow != null) {
                    this.addChild(forceArrow);
                }
            }
            if (isSelected())
                setSelected();
        }
    }

    public void setPos(Vector3d newpos) {
        double[] dnewpos = mCD.getPosfromVirtualWorld(newpos.x, newpos.y,
                newpos.z);
        AtomCoords coords = mCD.getAtomCoords();
        ciss.phase_viewer.atomcoord.Atom atnow = coords.getAtomList()
                .getAtomAt(Integer.parseInt(ID) - 1);
        ciss.phase_viewer.atomcoord.Atom newatom = atnow.getCopy();
        newatom.setPos(dnewpos);
        coords.getAtomList().replaceAtomAt(Integer.parseInt(ID) - 1, newatom);
        mCD.setCoordsNoUpdate(coords);
    }

    private double[] getCoordinatesInRealWorld(TransformGroup trans) {
        Transform3D t3d = new Transform3D();
        trans.getTransform(t3d);
        Vector3d vec3d = new Vector3d();
        t3d.get(vec3d);

        double lenmax = parent.getLenMax();
        double[] jusin = parent.getCOM();
        double[] ret = new double[3];

        ret[0] = vec3d.x * lenmax + jusin[0];
        ret[1] = vec3d.y * lenmax + jusin[1];
        ret[2] = vec3d.z * lenmax + jusin[2];

        return ret;
    }

    public Vector3d getCoordinatesInVirtualWorld() {
        return getCoordinatesInVirtualWorld(this);
    }

    public Vector3d getCoordinatesInVirtualWorld(TransformGroup trans) {
        Transform3D t3d = new Transform3D();
        trans.getTransform(t3d);
        double lenmax = parent.getLenMax();
        double[] jusin = parent.getCOM();
        double[] pos_moto = mCD.getPos(Integer.parseInt(ID) - 1);

        logger.debug("atom no. " + ID + " is updating.");
        for (int i = 0; i < 3; i++) {
            dp[i] = (pos_moto[i] - jusin[i]) / lenmax;
        }
        pos = new Vector3d(dp);
        pos_buff = new Vector3d(pos);
        t3d.transform(pos);
        return pos;
    }

    public void configDataUpdate(boolean foo, ConfigDataUpdateEvent e) {
        configDataUpdate();
    }

    public boolean needsUpdate() {
        return true;
    }

    public int getAtomicNumber() {
        return this.atomicNumber;
    }

    public void setAtomicNumber(int atomicNumber) {
        this.atomicNumber = atomicNumber;
    }

    public String getMobile() {
        return String.valueOf(mobile);
    }

    public void setMobile(String mobile) {
        try {
            this.mobile = Integer.parseInt(mobile);
        } catch (NumberFormatException nfe) {
            this.mobile = 0;
        }
    }

    public String getWeight() {
        return String.valueOf(weight);
    }

    public void setWeight(String weight) {
        try {
            this.weight = Integer.parseInt(weight);
        } catch (NumberFormatException nfe) {
            this.weight = 1;
        }
    }

    public int getType() {
        return SceneGraphElement.ATOM;
    }

    public Point3d getPos() {
        return new Point3d(dp);
    }

    public double[] getPosDouble() {
        return dp;
    }

    public void setPos(double[] pos) {
        this.dp = dp;
        this.pos = new Vector3d(dp);
        this.pos_buff = new Vector3d(pos);
    }

    public void setForce(double[] force) {
        this.force = force;
    }

    private void recreate() {

    }

    public void create() {

        Appearance app = new Appearance();
        app.setCapability(Appearance.ALLOW_MATERIAL_READ);
        app.setCapability(Appearance.ALLOW_TRANSPARENCY_ATTRIBUTES_READ);
        app.setCapability(Appearance.ALLOW_TEXTURE_READ);
        ColoringAttributes ca = new ColoringAttributes();
        RenderingAttributes ra = new RenderingAttributes();
        ra.setIgnoreVertexColors(true);
        app.setRenderingAttributes(ra);

        Material material = new Material();
        material.setCapability(Material.ALLOW_COMPONENT_READ);
        material.setDiffuseColor(new Color3f(color));
        material.setShininess(120.0f);
        app.setMaterial(material);
        //
        // TextureLoader loader = new
        // TextureLoader(System.getProperty("pviewer.home")+System.getProperty("file.separator")+"lib"+System.getProperty("file.separator")+"resource"+System.getProperty("file.separator")+"one.png","LUMINANCE",
        // new Container());
        // Texture texture = loader.getTexture();
        // texture.setBoundaryModeS(Texture.CLAMP);
        // texture.setBoundaryModeT(Texture.CLAMP);
        // texture.setBoundaryColor( new Color4f( 0.0f, 1.0f, 0.0f, 0.0f ) );
        // TextureAttributes texAttr = new TextureAttributes();
        // texAttr.setTextureMode(TextureAttributes.REPLACE);
        // app.setTexture(texture);
        // app.setTextureAttributes(texAttr);

        atom = new Atom(radius * scaleatom, Sphere.GENERATE_NORMALS, numNodes,
                app, this);

        this.addChild(atom);

        // BranchGroup tmpb = new Circle(radius*scaleatom);
        // this.addChild(tmpb);
        //
        // PickTranslateBehavior trb = new
        // PickTranslateBehavior(bra,mACVD.getParent().getCanvas(),atom.getBounds());
        // addChild(trb);

        getArrowProps();
        if (force != null && drawArrow) {
            forceArrow = createForceArrow();
            if (forceArrow != null) {
                this.addChild(forceArrow);
            }
        }

        ElementInfo info = ElementInfo.getElementInfo();
        elementName = info.getSymbolFromNumber(atomicNumber + 1);

        // initialize pop-up menu.
        popup.setLightWeightPopupEnabled(false);
        popup.add(menuItem);
        popup.add(menuDist);
        popup.setInvoker((BaseJ3DPanel) mACVD.getParent());
    }

    private String elementName;

    private boolean drawArrow = false;

    private boolean drawValue = false;

    private double arrowScale = 1.d;

    private double arrowRadius = 0.005d;

    private float arrowHeadRadius = 0.02f;

    private float arrowHeadHeight = 0.04f;

    float[] color_wk = new float[] { 0.2f, 0.2f, 0.5f };

    private void getArrowProps() {
        try {
            drawArrow = new Boolean(gp.getProperty("arrow_draw"))
                    .booleanValue();
            drawValue = new Boolean(gp.getProperty("arrow_draw_value"))
                    .booleanValue();

            arrowScale = Double.parseDouble(gp.getProperty("arrow_scale"));
            arrowHeadRadius = Float.parseFloat(gp
                    .getProperty("arrow_head_radius"));
            arrowHeadHeight = Float.parseFloat(gp
                    .getProperty("arrow_head_height"));
            arrowRadius = Double.parseDouble(gp.getProperty("arrow_radius"));
            color_wk[0] = Float.parseFloat(gp.getProperty("arrow_color_r"));
            color_wk[1] = Float.parseFloat(gp.getProperty("arrow_color_g"));
            color_wk[2] = Float.parseFloat(gp.getProperty("arrow_color_b"));
        } catch (Exception ex) {
        }
    }

    private CylinderCreatorTG arrowCreator;

    private final DecimalFormat df = new DecimalFormat("#.###");

    private BranchGroup createForceArrow() {
        double len = ciss.phase_viewer.common.VectorOperations
                .norm(new double[] { force[0] * arrowScale,
                        force[1] * arrowScale, force[2] * arrowScale });
        float coneScale = 1.f;
        if (len < radius * scaleatom) {
            return null;
            // arrowScale = 0.d;
            // coneScale = 0.f;
        }
        Point3d siten = new Point3d(0.d, 0.d, 0.d);
        Point3d syuten = new Point3d(force[0] * arrowScale, force[1]
                * arrowScale, force[2] * arrowScale);
        if (siten.epsilonEquals(syuten, 0.00001d)) {
            return null;
        }
        Appearance app = new Appearance();
        Material material = new Material();

        material.setDiffuseColor(new Color3f(color_wk));
        material.setShininess(120.0f);
        app.setMaterial(material);

        CylinderCreator cylindercreate = new CylinderCreator();

        BranchGroup bg = cylindercreate.create(siten, syuten, arrowRadius,
                arrowHeadRadius * coneScale, arrowHeadHeight * coneScale, app);

        if (drawValue) {
            Point3f lp3f = new Point3f(0.0f, 0.0f, 0.0f);
            CapableText2D ct = new CapableText2D(" " + df.format(len), mACVD);
            OrientedShape3D orientedShape = new OrientedShape3D(
                    ct.getGeometry(), ct.getAppearance(),
                    OrientedShape3D.ROTATE_ABOUT_POINT, lp3f);
            bg.addChild(orientedShape);
        }
        return bg;
    }

    public Atom getAtom() {
        return this.atom;
    }

    public String getID() {
        return ID;
    }

    /**
     * idを整数にして返す
     * 
     * @return 整数で表示したID
     */
    public int getIDint() {
        return Integer.parseInt(ID);
    }

    /**
     * この原子のIDを付け直す. 原子を系から削除した場合などで必要
     * 
     * @param id
     *            新しいID
     */
    public void setID(int id) {
        this.ID = String.valueOf(id);
    }

    /**
     * この原子のIDを付け直す. 原子を系から削除した場合などで必要
     * 
     * @param id
     *            新しいID
     */
    public void setID(String ID) {
        this.ID = ID;
    }

    private BranchGroup labelBranch;

    protected void setLabelOffset(int labelOffset) {
        Point3f lp3f = new Point3f(0.0f, 0.0f, 0.0f);
        this.labelOffset = labelOffset;
        Color3f color_wkl2 = super.mACVD.getFontColor();
        String offset = new String();
        for (int i = 0; i < labelOffset; i++) {
            offset = offset + " ";
        }
        Font font = super.mACVD.getFont();
        Transform3D trans = new Transform3D();
        Text2D text2d = new CapableText2D(offset + ID, color_wkl2,
                font.getName(), font.getSize(), font.getStyle());
        OrientedShape3D orientedShape = new OrientedShape3D(
                text2d.getGeometry(), text2d.getAppearance(),
                OrientedShape3D.ROTATE_ABOUT_POINT, lp3f);
        trans.setTranslation(new Vector3d(0, 0, radius));
        TransformGroup tg = new TransformGroup();
        tg.setTransform(trans);
        tg.addChild(orientedShape);
        labelBranch = new BranchGroup();
        labelBranch.setCapability(BranchGroup.ALLOW_DETACH);
        labelBranch.addChild(tg);
        this.addChild(labelBranch);
    }

    protected void setMobilityDisplayMode(int mobilityDisplayMode) {
        if (mobile == 1) {
            if (mobilityDisplayMode == 0) {
                mobilityFactor = 0.6f;
            } else if (mobilityDisplayMode == 1) {
                mobilityFactor = 1.5f;
            }
        } else
            mobilityFactor = 1f;
    }

    protected void setScale(float scaleatom) {
        this.scaleatom = scaleatom;
    }

    protected void setColor(float[] color__) {
        logger.debug("mobile: " + mobile + " mobilityFactor: " + mobilityFactor);
        if (this.color == null)
            this.color = new float[3];
        for (int i = 0; i < 3; i++) {
            color[i] = color__[i] * mobilityFactor;
            if (color[i] >= 1.f) {
                color[i] = 1.f;
            }
        }
    }

    protected void setRad(float radius) {
        this.radius = radius;
    }

    public float getRad() {
        return this.radius;
    }

    protected void setNumNodes(int numNodes) {
        this.numNodes = numNodes;
    }

    protected void setCanvas(Canvas3D canvas) {
        this.canvas = canvas;
    }

    /**
     * 選択状態をトグルする
     * 
     * @return 非選択⇒選択の場合 true, 選択⇒非選択の場合false
     */
    public boolean toggleSelected() {
        boolean ret = selected;
        selected = !selected;
        doSelection();
        return !ret;
    }

    public void unSelect() {
        selected = false;
        doSelection();

    }

    public void setSelected() {
        selected = true;
        doSelection();
    }

    public void createBuffer() {
        Transform3D t3d = new Transform3D();
        getTransform(t3d);
        float tr = 0.6f;
        try {
            tr = Float.parseFloat(gp
                    .getProperty("transparency_of_temporary_atom"));
        } catch (Exception exc) {
        }
        buffer = getCopyOfAtom(tr, t3d);
        for (int i = 0; i < buffer.numChildren(); i++) {
            if (buffer.getChild(i) instanceof TransformGroup) {
                transformBuffer = (TransformGroup) buffer.getChild(i);
            }
        }
    }

    protected void postIntermediateState(Transform3D diff,
            TransformGroup parent, InputEvent event) {
        if (!selected) {
            return;
        }
        MouseEvent me = (MouseEvent) event;
        TGAtom tgatom = (TGAtom) parent;
        double screenScale = MouseMotionListenerAction.getScreenScale(tgatom
                .getParentFrame());
        Point3d ptmp = MouseMotionListenerAction.getCoordsFromMousePos(
                tgatom.getParentFrame(), me);
        int canvSizex = tgatom.getParentFrame().getCanvas().getSize().width;
        int canvSizey = tgatom.getParentFrame().getCanvas().getSize().height;
        if (((TGAtom) parent).getSelectedAtomCount() == 1
                && me.getModifiersEx() == MouseEvent.CTRL_DOWN_MASK
                        + MouseEvent.BUTTON3_DOWN_MASK) {
            Point patom = getLocationOnScreen(canvSizex, canvSizey,
                    screenScale, getTransformBuffer());
            logger.debug("atom: getLocationOnScreen: " + patom);
            showPopup(canvSizex, canvSizey, screenScale);
        }
    }

    protected boolean intermediateStateNecessary() {
        return selected;
    }

    private void doSelection() {
        if (selected) {
            createBuffer();

            circle = new Circle(radius * scaleatom);
            this.addChild(circle);

            ((AtomSelectionCanvas) parent).selected(this);
        } else {
            if (buffer != null) {
                buffer.detach();
            }

            for (int i = 0; i < numChildren(); i++) {
                Node child = this.getChild(i);
                if (child instanceof Circle) {
                    this.removeChild(i);
                }
            }

            ((AtomSelectionCanvas) parent).deselected(this);
        }

    }

    public boolean isSelected() {
        return selected;
    }

    public BoundingSphere getTransformedBounds() {
        Transform3D translate = new Transform3D();
        getTransform(translate);
        Transform3D etc = new Transform3D();
        parent.getTransform(etc);
        etc.mul(translate);
        Bounds ret = getBounds();
        ret.transform(etc);

        return (BoundingSphere) ret;
    }

    public void dynamicallyEditFinalize(TransformGroup parent) {
        if (selected) {
            Transform3D t3d = new Transform3D();
            transformBuffer.getTransform(t3d);
            Vector3d atompos = new Vector3d();
            t3d.get(atompos);
            setPos(atompos);
            removePopup();
        }
    }

    public String toString() {
        String ret = "ID: " + ID + ", atomic number: "
                + String.valueOf(atomicNumber) + ", pos: " + pos;
        return ret;
    }
}
