/*
!=======================================================================
!
!  PROGRAM  PHASE-Viewer  (PHASE-Viewer 2014.01 ver.3.3.0)
!
!  Created on ----
!  AUTHOR(S): KOGA, Junichiro
!  File : MainPanel.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;

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.util.Enumeration;
import java.util.Vector;

import javax.media.j3d.Appearance;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.ColoringAttributes;
import javax.media.j3d.Geometry;
import javax.media.j3d.GeometryArray;
import javax.media.j3d.Group;
import javax.media.j3d.LineAttributes;
import javax.media.j3d.LineStripArray;
import javax.media.j3d.Node;
import javax.media.j3d.RestrictedAccessException;
import javax.media.j3d.Shape3D;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.media.j3d.VirtualUniverse;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JPanel;
import javax.vecmath.Point3f;
import javax.vecmath.Vector3d;
import javax.vecmath.Vector3f;

import org.apache.log4j.Logger;

import ciss.phase_viewer.acviewer.mouselistener.ACVMouseMotionListener;
import ciss.phase_viewer.acviewer.operations.BasicOperations;
import ciss.phase_viewer.acviewer.operations.UnitCellOperations;
import ciss.phase_viewer.acviewer.scenegraphelements.TGAtom;
import ciss.phase_viewer.acviewer.scenegraphelements.atom.AtomObject;
import ciss.phase_viewer.atomcoord.AtomCoords;
import ciss.phase_viewer.atomcoord.VolumetricData;
import ciss.phase_viewer.mainpanel.Desk;

import com.sun.j3d.utils.universe.PlatformGeometry;

/**
 * 原子配置表示のためのトップレベルのGUIを提供するクラス. このクラスは, 以下のことを行う. -# 呼び出し元からデータを取得し,
 * ACVDataManagerやConfigDataManagerに渡す. -#
 * 原子配置表示用のSimpleUniverseオブジェクトやCanvas3Dオブジェクトを生成. -# 操作用のボタンの配備. -#
 * 描画用のメソッドなどを用意.
 * 
 * @author KOGA Junichiro,
 */
public class MainPanel extends BaseJ3DPanel implements ConfigData,
        CoordsViewerInterface {
    // public class MainPanel extends InternalFrameChase {
    // public class MainPanel extends InternalHeavyFrameChase implements
    // ConfigData {
    private static Logger logger = Logger.getLogger(MainPanel.class.getName());

    private ConfigDataManager mCD;

    private Show3D s3d;

    private JDesktopPane desktop;

    private String[][] xyz;

    private String[][] cellvec;

    private String[] label;

    private int NumAt;

    private boolean cellorigin;

    private Vector frames;

    private Container cont;

    private int insetx, insety;

    private JPanel panel;

    private ACVData acvdata;

    private RectangleCreator rectangleCreator;

    private int mode = FROM_CHASE;

    private Animater animater = null;

    public MainPanel() {
        super("AtomicConfigurationViewer", defaultSize);
        init();
        this.setJMenuBar(new ACVMenuBar(this));
    }

    public MainPanel(int mode) {
        super("AtomicConfigurationViewer", defaultSize);
        this.mode = mode;
        init();
        this.setJMenuBar(new ACVMenuBar(this));
    }

    public MainPanel(ACVData acvdata) {
        super("AtomicConfigurationViewer " + acvdata.getFileName(), defaultSize);
        this.acvdata = acvdata;
        this.frames = acvdata.getFrames();
        this.parentDir = acvdata.getParentDir();

        init();
        this.setJMenuBar(new ACVMenuBar(this));
    }

    public MainPanel(ACVData acvdata, int mode) {
        super("AtomicConfigurationViewer " + acvdata.getFileName(), defaultSize);
        this.acvdata = acvdata;
        this.mode = mode;
        this.frames = acvdata.getFrames();
        this.parentDir = acvdata.getParentDir();

        init();
        this.setJMenuBar(new ACVMenuBar(this));
    }

    public MainPanel(Vector frames, String parentDir) {
        super("AtomicConfigurationViewer", defaultSize);
        this.frames = frames;
        if (parentDir != null) {
            this.parentDir = parentDir;
        }
        init();
        this.setJMenuBar(new ACVMenuBar(this));
    }

    public MainPanel(Vector frames) {
        super("AtomicConfigurationViewer", defaultSize);
        this.frames = frames;
        init();
        this.setJMenuBar(new ACVMenuBar(this));
    }

    public boolean supportsFrame() {
        return true;
    }

    protected void stopAllAnimations() {
        if (associatedGUIs != null) {
            int num = associatedGUIs.size();
            for (int i = 0; i < num; i++) {
                Object obj = associatedGUIs.get(i);
                if (obj instanceof Stoppable) {
                    ((Stoppable) obj).stop();
                }
            }
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * ciss.phase_viewer.acviewer.CoordsViewerInterface#getAssociatedAnimater()
     */
    public Animater getAssociatedAnimater() {
        if (animater == null) {
            animater = new Animater(this);
        }
        return animater;
    }

    private UnitCellOperations unitcell;

    /*
     * (non-Javadoc)
     * 
     * @see ciss.phase_viewer.acviewer.CoordsViewerInterface#doUnitCell()
     */
    public void doUnitCell() {
        doUnitCell(true);
    }

    /**
     * すべての設定を明示的に設定した上で.
     * 
     * @param update
     * @param pack
     * @param ab
     * @param ac
     * @param bc
     */
    public void doUnitCell(Boolean update, Boolean bpack, Boolean bab,
            Boolean bac, Boolean bbc) {
        // if (getCD() == null || getCD().getAtomCoords() == null)
        // return;

        try {
            if (update.booleanValue())
                getCD().getAtomCoords().saveState();

            boolean ab = new Boolean(prop.getProperty("add_boundary_atoms_ab"))
                    .booleanValue();
            boolean ac = new Boolean(prop.getProperty("add_boundary_atoms_ac"))
                    .booleanValue();
            boolean bc = new Boolean(prop.getProperty("add_boundary_atoms_bc"))
                    .booleanValue();
            boolean pack = new Boolean(
                    prop.getProperty("pack_atoms_into_unit_cell"))
                    .booleanValue();

            if (bab != null && bac != null && bbc != null) {
                ab = bab.booleanValue();
                ac = bac.booleanValue();
                bc = bbc.booleanValue();
            }

            if (bpack != null)
                pack = bpack.booleanValue();

            String seps = prop.getProperty("pack_atoms_into_unit_cell_eps");
            double eps = -1.0d;

            if (seps != null) {
                try {
                    eps = Double.parseDouble(seps);
                } catch (NumberFormatException nfe) {
                }
            }

            if (unitcell == null) {
                unitcell = new UnitCellOperations(frames, this);
            }

            if (eps >= 0.d) {
                unitcell.setEps(eps);
            }

            unitcell.setPackAtomsIntoUnitCell(pack);
            unitcell.setAddBoundaryAtoms(ab, ac, bc);
            unitcell.processFrame();

            if (update.booleanValue()) {
                getCD().setCoords(getCD().getAtomCoords());
                getCD().getAtomCoords().finalizeState();
            }
        } catch (Exception e) {
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see ciss.phase_viewer.acviewer.CoordsViewerInterface#doUnitCell(boolean)
     */
    public void doUnitCell(boolean update) {
        doUnitCell(new Boolean(update), null, null, null, null);
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * ciss.phase_viewer.acviewer.CoordsViewerInterface#setData(ciss.phase_viewer
     * .acviewer.ACVData)
     */
    public void setData(ACVData data) {
        this.acvdata = data;
        this.frames = data.getFrames();
        this.parentDir = data.getParentDir();

        // super.setTitle(super.getTitle() + " " + acvdata.getFileName());
        super.setTitle("AtomicConfigurationViewer " + " "
                + acvdata.getFileName());
        animater = null;
        for (int i = 0; i < associatedGUIs.size(); i++) {
            Object editor = associatedGUIs.get(i);
            if (editor instanceof JInternalFrame) {
                javax.swing.JInternalFrame window = (javax.swing.JInternalFrame) associatedGUIs
                        .get(i);
                if (window != null) {
                    window.dispose();
                }
            } else if (editor instanceof JFrame) {
                javax.swing.JFrame window = (javax.swing.JFrame) associatedGUIs
                        .get(i);
                if (window != null) {
                    window.dispose();
                }
            }
        }
        init();
        revalidate();
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * ciss.phase_viewer.acviewer.CoordsViewerInterface#setFrame(java.util.Vector
     * )
     */
    public void setFrame(Vector frames) {
        this.frames = frames;
        init();
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * ciss.phase_viewer.acviewer.CoordsViewerInterface#setFrame(ciss.phase_viewer
     * .atomcoord.AtomCoords[])
     */
    public void setFrame(AtomCoords[] cds) {
        if (cds == null) {
            return;
        }
        frames = new Vector();
        for (int i = 0; i < cds.length; i++) {
            frames.addElement(cds[i]);
        }
        init();
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * ciss.phase_viewer.acviewer.CoordsViewerInterface#getUnitCellManipulator()
     */
    public UnitCellOperations getUnitCellManipulator() {
        return this.unitcell;
    }

    private ACVButtons btns;

    private void init() {
        // if (mode == STAND_ALONE) {
        // addWindowListener(new WindowAdapter() {
        // public void windowClosing(WindowEvent we) {
        // dispose();
        // System.exit(0);
        // }
        //
        // public void windowClosed(WindowEvent we) {
        // dispose();
        // System.exit(0);
        // }
        // });
        // }
        desktop = Desk.getDesktop();

        if (frames != null && frames.size() != 0) {

            doUnitCell(false);

            mCD = new ConfigDataManager((AtomCoords) frames.get(0));
            if (dataManager == null) {
                logger.error("dataManager is null");
            }
            mCD.setJ3DDataManager(dataManager);
            mCD.register(this);
            mCD.setParent(this);
            mCD.getAtomCoords().getAtomList().initializeUndoRedo();
            this.setLabel();
        }
        // try {
        // int x = Integer.parseInt(propacv.getProperty("winsizex"));
        // int y = Integer.parseInt(propacv.getProperty("winsizey"));
        // setSize(x,y);
        // } catch(Exception exc) {
        // setSize(512,512);
        // }

        // cont.removeAll();
        // cont.add(jCanvas3D,BorderLayout.CENTER);

        rootBranch = new BranchGroup();
        rootBranch.setCapability(BranchGroup.ALLOW_DETACH);
        rootBranch.setCapability(BranchGroup.ALLOW_CHILDREN_READ);
        rootBranch.setCapability(BranchGroup.ALLOW_CHILDREN_WRITE);
        rootBranch.setCapability(BranchGroup.ALLOW_CHILDREN_EXTEND);

        s3d = new Show3D(this);

        rectangleCreator = new RectangleCreator(this, 0.f, 0.f);
        logger.debug("simpleU: " + simpleU);
        simpleU.getViewingPlatform().setPlatformGeometry(
                rectangleCreator.getPlatformGeometry(0.0f, 0.0f));
        rectangleCreator.detach();

        // JPanel panel = new JPanel();
        // panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));

        if (btns == null) {
            btns = new ACVButtons(this);
            getContentPane().add(btns, BorderLayout.NORTH);
        }
        // jCanvas3D.add(btns);
        //
        // panel.add(btns);
        // panel.add(canvas3d);
        // cont.setLayout(new BorderLayout());
        // cont.add(panel);

        calculateInsets();
        //
        // addComponentListener(new ComponentAdapter() {
        // public void componentResized(ComponentEvent e) {
        // Dimension canvdim = getSize();
        // // canvdim.width = canvdim.width - insetx;
        // canvdim.height = canvdim.height - insety;
        // canvas3d.setSize(canvdim);
        // canvas3d.setPreferredSize(canvdim);
        // simpleU.getLocale().replaceBranchGraph(objRoot_wk, objRoot_wk);
        // // logger.debug("atomconfig viewer resize...");
        // // logger.debug("canv width, height: "+canvdim.width+",
        // "+canvdim.height);
        // // propacv.setProperty("winsizex",String.valueOf(getSize().width));
        // // propacv.setProperty("winsizey",String.valueOf(getSize().height));
        // // propacv.storeProperty();
        // }
        // });

        ACVMouseMotionListener mouseMotionListener = new ACVMouseMotionListener(
                this);
        if (useJCanvas3D()) {
            getJCanvas3D().addMouseMotionListener(mouseMotionListener);
            getJCanvas3D().addMouseListener(mouseMotionListener);
            getJCanvas3D().requestFocusInWindow();
        } else {
            getCanvas().addMouseMotionListener(mouseMotionListener);
            getCanvas().addMouseListener(mouseMotionListener);
        }

    }

    //
    // public PluginParser getPluginParser() {
    // if (plugparser == null) {
    // plugparser = new PluginParser(this);
    // }
    // return plugparser;
    // }

    public void configDataUpdate() {
        redraw();
    }

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

    public boolean needsUpdate() {
        return true;
    }

    //
    // private void createScene() {
    // GraphicsConfiguration configuration = SimpleUniverse
    // .getPreferredConfiguration();
    // logger.debug("class of GraphicsConfiguration: "
    // + configuration.getClass().getName());
    // canvas3d = new ChaseCanvas3D(configuration);
    // canvas3d.setDoubleBufferEnable(true);
    //
    // simpleU = new SimpleUniverse(canvas3d);
    // simpleU.getViewingPlatform().setNominalViewingTransform();
    // panel.removeAll();
    // panel.add(canvas3d);
    // }

    private void calculateInsets() {
        Dimension canvdim = canvas3d.getSize();
        Dimension dim = getSize();

        insetx = dim.width - canvdim.width;
        insety = dim.height - canvdim.height;
    }

    /**
     * 原子のほか, 軸のTransformGroupも返す.
     * 
     * @return 回転操作の対象たりうるTransformGroup
     */
    public TransformGroup[] getRotatingTransform() {
        return new TransformGroup[] { getRootTransform(), s3d.getTGAxis() };
    }

    /*
     * (non-Javadoc)
     * 
     * @see ciss.phase_viewer.acviewer.CoordsViewerInterface#getScene()
     */
    public Show3D getScene() {
        return s3d;
    }

    public void postCleanUp() {
        s3d.cleanUp();
        s3d = null;
        rectangleCreator = null;
        rootBranch = null;
    }

    /*
     * (non-Javadoc)
     * 
     * @see ciss.phase_viewer.acviewer.CoordsViewerInterface#getFramesAC()
     */
    public Vector getFramesAC() {
        return this.frames;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * ciss.phase_viewer.acviewer.CoordsViewerInterface#initializeRectangle(
     * float, float)
     */
    public void initializeRectangle(float fx, float fy) {
        rectangleCreator.setLocation(fx, fy);
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * ciss.phase_viewer.acviewer.CoordsViewerInterface#updateRectangle(float,
     * float)
     */
    public void updateRectangle(float w, float h) {
        rectangleCreator.updateRectangle(w, h);
    }

    /*
     * (non-Javadoc)
     * 
     * @see ciss.phase_viewer.acviewer.CoordsViewerInterface#finalizeRectangle()
     */
    public void finalizeRectangle() {
        rectangleCreator.detach();
    }

    /*
     * (non-Javadoc)
     * 
     * @see ciss.phase_viewer.acviewer.CoordsViewerInterface#setLabel()
     */
    public void setLabel() {
        label = new String[mCD.getNumAt()];
        for (int i = 0; i < mCD.getNumAt(); i++) {
            label[i] = Integer.toString(i + 1);
        }
        dataManager.setLabel(label);
    }

    private boolean foo = false;

    private BranchGroup rootRootBranch;

    /*
     * (non-Javadoc)
     * 
     * @see ciss.phase_viewer.acviewer.CoordsViewerInterface#display3D()
     */
    public final void display3D() throws Exception {

        if (mCD != null) {
            s3d.createNewElement();
            rootBranch = s3d.getObjRoot();

            s3d.setMouseMotionBehaviours();
            rootBranch.setCapability(BranchGroup.ALLOW_DETACH);
            rootBranch.compile();

            VirtualUniverse.setJ3DThreadPriority(Thread.MAX_PRIORITY);

            if (rootRootBranch == null) {
                rootRootBranch = new BranchGroup();
                rootRootBranch.setCapability(BranchGroup.ALLOW_CHILDREN_EXTEND);
                rootRootBranch.setCapability(BranchGroup.ALLOW_CHILDREN_READ);
                rootRootBranch.setCapability(BranchGroup.ALLOW_CHILDREN_WRITE);
                rootRootBranch.addChild(rootBranch);
                simpleU.addBranchGraph(rootRootBranch);
            } else {
                rootRootBranch.removeAllChildren();
                rootRootBranch.addChild(rootBranch);
            }
            getContentPane().requestFocusInWindow();
            canvas3d.requestFocusInWindow();
            foo = true;
        }

        doUnitCell();

        setVisible(true);
    }

    private void setAllowIntersect(Node node) {
        if (node instanceof Shape3D)
            try {
                ((Shape3D) node).getGeometry().setCapability(
                        Geometry.ALLOW_INTERSECT);
            } catch (RestrictedAccessException rse) {
            }
        if (node instanceof Group) {
            Group gp = (Group) node;
            for (int i = 0; i < gp.numChildren(); i++) {
                Node nd = gp.getChild(i);
                setAllowIntersect(nd);
            }
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see ciss.phase_viewer.acviewer.CoordsViewerInterface#getData()
     */
    public ACVData getData() {
        return this.acvdata;
    }

    /*
     * (non-Javadoc)
     * 
     * @see ciss.phase_viewer.acviewer.CoordsViewerInterface#getCD()
     */
    public ConfigDataManager getCD() {
        return this.mCD;
    }

    /*
     * (non-Javadoc)
     * 
     * @see ciss.phase_viewer.acviewer.CoordsViewerInterface#selectedRow(int)
     */
    public void selectedRow(int row) {
        if (row < 0) {
            return;
        }
        AtomObject[] aobjs = ((TGAtom) s3d.getTGAtom()).getAtomObjects();
        if (aobjs == null)
            return;
        for (int i = 0; i < aobjs.length; i++) {
            AtomObject obj = aobjs[i];
            obj.unSelect();
            if (obj.getID().equals(Integer.toString(row + 1))) {
                logger.debug("foobar : caught atomobject: " + obj.getID());
                obj.setSelected();
            }
        }
        // Enumeration enu = s3d.getTGAtom().getAllChildren();
        // while (enu.hasMoreElements()) {
        // Object node = enu.nextElement();
        // if (node instanceof AtomObjectBG) {
        // AtomObject obj = ((AtomObjectBG) node).getAssociatedAtomObject();
        // obj.unSelect();
        // if (obj.getID().equals(Integer.toString(row + 1))) {
        // logger.debug("foobar : caught atomobject: " + obj.getID());
        // obj.setSelected();
        // }
        // }
        // }
        // super.pseudoSelection();
        // canvas3d.repaint();
        // revalidate();
        calculateInsets();
    }

    /*
     * (non-Javadoc)
     * 
     * @see ciss.phase_viewer.acviewer.CoordsViewerInterface#lightRedraw()
     */
    public void lightRedraw() {
        mCD.update();
    }

    /*
     * (non-Javadoc)
     * 
     * @see ciss.phase_viewer.acviewer.CoordsViewerInterface#redraw()
     */
    public void redraw() {
        // lightRedraw();
        // if (!doRedraw) {
        // return;
        // }
        //
        // logger.debug("redrawing the whole panel ...");
        // s3d.setmACVD(dataManager);
        // s3d.storeState();
        //
        // BranchGroup objRoot_wk_new = s3d.getObjRoot();
        // if (rootBranch == null || objRoot_wk_new == null) {
        // // scene not ready!
        // return;
        // }
        //
        // simpleU.getLocale().replaceBranchGraph(rootBranch, objRoot_wk_new);
        // rootBranch = objRoot_wk_new;
        // // super.pseudoSelection();
        // s3d.doPost();
        // calculateInsets();
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * ciss.phase_viewer.acviewer.CoordsViewerInterface#heavyRedraw(boolean)
     */
    public void heavyRedraw(boolean always) {
        if (!always && frames.size() == 1) {
            redraw();
            return;
        }

        logger.info("redrawing the whole scene...");
        removeFromDesktop();
        init();

        try {
            display3D();
        } catch (Exception exc) {
            logger.error("failed 'heavy' redraw");
        }
    }

    public VolumetricData[] getAssociatedVolumetricData() {
        return getCD().getAtomCoords().getChargeDensities();
    }

    public TransformGroup getRootTransform() {
        return getScene().getTGAtom();
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * ciss.phase_viewer.acviewer.CoordsViewerInterface#getBasicOperations()
     */
    public BasicOperations getBasicOperations() {
        return ((TGAtom) getScene().getTGAtom()).getBasicOperations();
    }

}

/**
 * 画面上に長方形を描くクラス.
 */
class RectangleCreator {
    private Logger logger = Logger.getLogger(RectangleCreator.class.getName());

    private MainPanel parent;

    private PlatformGeometry platform;

    private float x = 0.0f;

    private float y = 0.0f;

    private float width = 0.0f;

    private float height = 0.0f;

    private TransformGroup transform;

    private Rectangle rectangle;

    private float shift = -0.0f;

    private float factor = 1f;

    private float yfactor = 0.875f;

    /**
     * creates a rectangle at 0f,0f
     * 
     * @param parent
     *            the 'MainPanel' object to which the rectangle is associated.
     */
    protected RectangleCreator(MainPanel parent) {
        this.parent = parent;
        init();
    }

    /**
     * creates a rectangle at a specified location.
     * 
     * @param parent
     *            the 'MainPanel' object to which the rectangle is associated.
     * @param x
     *            the x-coordinate of the rectangle
     * @param y
     *            the y-coordinate of the rectangle
     */
    protected RectangleCreator(MainPanel parent, float x, float y) {
        this.parent = parent;
        this.x = x;
        this.y = y;
        init();
    }

    /**
     * creates a rectangle at a specified location with the specified width &
     * height.
     * 
     * @param parent
     *            the 'MainPanel' object to which the rectangle is associated.
     * @param x
     *            the x-coordinate of the rectangle
     * @param y
     *            the y-coordinate of the rectangle
     * @param width
     *            the width of the rectangle
     * @param height
     *            the height of the rectangle
     */
    protected RectangleCreator(MainPanel parent, float x, float y, float width,
            float height) {
        this.parent = parent;
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
        init();
    }

    private double dviewplat;

    private void init() {

        Transform3D viewplat = new Transform3D();
        parent.getUniverse().getViewingPlatform().getViewPlatformTransform()
                .getTransform(viewplat);
        Vector3d vvd = new Vector3d();
        viewplat.get(vvd);
        dviewplat = vvd.z;
        shift = -0.5f * (float) vvd.z;
        factor = Math.abs(shift) / (float) dviewplat;

        logger.debug("dviewplat x,y,z: " + vvd.x + ", " + vvd.y + ", " + vvd.z);
        platform = new PlatformGeometry();
        transform = new TransformGroup();

        transform.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
        transform.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
        transform.setCapability(TransformGroup.ALLOW_CHILDREN_EXTEND);
        transform.setCapability(TransformGroup.ALLOW_CHILDREN_READ);
        transform.setCapability(TransformGroup.ALLOW_CHILDREN_WRITE);

        Transform3D t3d = new Transform3D();
        Vector3f translate = new Vector3f(x, y, shift);
        t3d.setTranslation(translate);
        transform.setTransform(t3d);

        rectangle = new Rectangle(width, height);
        transform.addChild(rectangle);

        platform.addChild(transform);

    }

    protected void updateRectangle(float width, float height) {
        rectangle.setRectangleShape(width * factor, height * factor * yfactor);
        rectangle.updateRectangle();
        boolean found = false;
        Enumeration en = transform.getAllChildren();
        while (en.hasMoreElements()) {
            if (en.nextElement() instanceof Rectangle) {
                found = true;
                break;
            }
        }
        if (!found) {
            transform.addChild(rectangle);
        }
    }

    protected PlatformGeometry getPlatformGeometry() {
        return this.platform;
    }

    protected PlatformGeometry getPlatformGeometry(float width, float height) {
        updateRectangle(width, height);
        return this.platform;
    }

    protected void setLocation(float x, float y) {
        this.x = x * factor;
        this.y = y * factor * yfactor;
        logger.debug("location: " + x + ", " + y);
        Transform3D t3d = new Transform3D();
        Vector3f translate = new Vector3f(this.x, this.y, shift);
        t3d.setTranslation(translate);
        transform.setTransform(t3d);
    }

    protected void detach() {
        rectangle.detach();
    }

}

/**
 * 「長方形」オブジェクト.
 */
class Rectangle extends BranchGroup {
    private Shape3D rectangle;

    private float width;

    private float height;

    private int resolution = 4;

    private int length = resolution + 1;

    private Appearance appearance = new Appearance();

    private LineAttributes lineAttrib = new LineAttributes();

    protected Rectangle(float width, float height) {
        this.width = width;
        this.height = height;

        appearance.setColoringAttributes(new ColoringAttributes(0.f, 0.f, 0.f,
                ColoringAttributes.NICEST));
        lineAttrib.setLineWidth(1.0f);
        appearance.setLineAttributes(lineAttrib);
        setCapability(BranchGroup.ALLOW_DETACH);
        rectangle = createRectangle();
        rectangle.setCapability(Shape3D.ALLOW_GEOMETRY_READ);
        rectangle.setCapability(Shape3D.ALLOW_GEOMETRY_WRITE);
        this.addChild(rectangle);
    }

    protected void setRectangleShape(float width, float height) {
        this.width = width;
        this.height = height;
    }

    protected void updateRectangle() {
        rectangle.setGeometry(getGeometry());
    }

    private Shape3D createRectangle() {
        LineStripArray lsa = getGeometry();
        Shape3D shape = new Shape3D(lsa, appearance);
        return shape;
    }

    private LineStripArray getGeometry() {
        LineStripArray lsa = new LineStripArray(length,
                GeometryArray.COORDINATES, new int[] { length });

        Point3f pt0 = new Point3f(0.0f, 0.0f, 0.0f);
        Point3f pt1 = new Point3f(0.0f, height, 0.0f);
        Point3f pt2 = new Point3f(width, height, 0.0f);
        Point3f pt3 = new Point3f(width, 0.0f, 0.0f);
        Point3f pt4 = new Point3f(0.0f, 0.0f, 0.0f);

        lsa.setCoordinate(0, pt0);
        lsa.setCoordinate(1, pt1);
        lsa.setCoordinate(2, pt2);
        lsa.setCoordinate(3, pt3);
        lsa.setCoordinate(4, pt4);
        try {
            lsa.setCapability(LineStripArray.ALLOW_INTERSECT);
        } catch (RestrictedAccessException rae) {
        }
        return lsa;
    }

}
