/*
!=======================================================================
!
!  PROGRAM  PHASE-Viewer  (PHASE-Viewer 2014.01 ver.3.3.0)
!
!  Created on 2006/07/03, 19:14
!  AUTHOR(S): KOGA, Junichiro
!  File : ContourPanel.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.plugins.viewer.chargedensity;

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.DecimalFormat;
import java.util.Vector;

import javax.media.j3d.Appearance;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.PolygonAttributes;
import javax.media.j3d.QuadArray;
import javax.media.j3d.RestrictedAccessException;
import javax.media.j3d.Shape3D;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.media.j3d.TransparencyAttributes;
import javax.swing.BoxLayout;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.vecmath.Color3f;
import javax.vecmath.Matrix4d;
import javax.vecmath.Point3f;
import javax.vecmath.Vector3d;
import javax.vecmath.Vector3f;

import org.apache.log4j.Logger;

import Jama.Matrix;
import ciss.phase_viewer.acviewer.ChaseTransformGroup;
import ciss.phase_viewer.acviewer.ConfigData;
import ciss.phase_viewer.acviewer.ConfigDataUpdateEvent;
import ciss.phase_viewer.acviewer.CoordsViewerInterface;
import ciss.phase_viewer.acviewer.J3DPanel;
import ciss.phase_viewer.acviewer.MainPanel;
import ciss.phase_viewer.acviewer.colormap.ColorMap;
import ciss.phase_viewer.acviewer.colormap.ColorMapChangeEvent;
import ciss.phase_viewer.acviewer.colormap.ColorMapChangeListener;
import ciss.phase_viewer.acviewer.colormap.ColorMapPanel;
import ciss.phase_viewer.acviewer.fbz.FBZ;
import ciss.phase_viewer.acviewer.geom.Plane;
import ciss.phase_viewer.acviewer.scenegraphelements.Contour;
import ciss.phase_viewer.acviewer.scenegraphelements.TGAtom;
import ciss.phase_viewer.atomcoord.VolumetricData;
import ciss.phase_viewer.mainpanel.InternalFrameChase;
import ciss.phase_viewer.primitiveguis.TransparencySelector;
import ciss.phase_viewer.primitiveguis.ValueSlider;
import ciss.phase_viewer.primitiveguis.ValueSliderListener;

/**
 * 電荷密度のcontourを描画するためのGUI. というかVolumetricなdata全般に使えるようにしたい.
 * 
 * @author
 */
public class ContourPanel extends InternalFrameChase implements
        ColorMapChangeListener, ValueSliderListener, ConfigData {
    private Logger logger = Logger.getLogger(ContourPanel.class.getName());

    private J3DPanel parent;

    private float[][] bounds;

    private double lenmax;

    private double[] jusin;

    /** Creates a new instance of ChargeContourPanel */
    public ContourPanel(J3DPanel parent) {
        super("contour panel", new Dimension(450, 550));
        this.parent = parent;
        parent.addDisposeOnExit(this);
        if (parent instanceof MainPanel) {
            ((CoordsViewerInterface) parent).getCD().register(this);
        }
        this.bounds = ((ChaseTransformGroup) parent.getRootTransform())
                .getEffectiveBounds();
        init();
        setVisible(true);
    }

    private JTextField textNormalX;

    private JTextField textNormalY;

    private JTextField textNormalZ;

    private JTextField textRotX;

    private JTextField textRotY;

    private JTextField textRotZ;

    private ValueSlider vsliderRotX;

    private ValueSlider vsliderRotY;

    private JComboBox planeSelector;

    private String[] planes = { "xy", "xz", "yz" };

    private JButton initRot;

    private ValueSlider sliderCenterX;

    private ValueSlider sliderCenterY;

    private ValueSlider sliderCenterZ;

    private JPanel centerPanel;

    private JButton btnColorBar;

    private JComboBox dataCombo;

    private JComboBox comboContour;

    private MyComboBoxModel comboContourModel;

    private JTextField textMin;

    private JTextField textMax;

    private DecimalFormat formexp = new DecimalFormat("0.00E0");

    private GuidePlane guidePlane;

    private TransparencySelector transp;

    private JCheckBox ontheFly;

    private void updateCenterPanel() {
        sliderCenterX.setMinMax(0, (bounds[0][0] + bounds[0][1] + bounds[0][2])
                * (float) lenmax);
        sliderCenterY.setMinMax(0, (bounds[1][0] + bounds[1][1] + bounds[1][2])
                * (float) lenmax);
        sliderCenterZ.setMinMax(0, (bounds[2][0] + bounds[2][1] + bounds[2][2])
                * (float) lenmax);
    }

    private float[] origin;

    private void init() {
        VolumetricData[] vdata = parent.getAssociatedVolumetricData();
        origin = vdata[0].getOrigin();
        logger.debug("origin: " + origin[0] + ", " + origin[1] + ", "
                + origin[2]);
        JTabbedPane tabbedPane = new JTabbedPane();
        JPanel planepanel = createPlanePanel();
        JPanel apppanel = createAppearancePanel();
        JPanel pa = new JPanel();
        pa.setLayout(new BorderLayout());
        pa.add(apppanel, BorderLayout.NORTH);

        tabbedPane.addTab("plane", planepanel);
        tabbedPane.addTab("appearance", pa);

        // GridBagConstraints gc = new GridBagConstraints();

        dataCombo = new JComboBox();
        for (int i = 0; i < vdata.length; i++) {
            dataCombo.addItem(vdata[i]);
        }
        JPanel pdataCombo = new JPanel();
        pdataCombo.setBorder(new TitledBorder("data"));
        pdataCombo.add(dataCombo);

        JPanel pcomboContour = new JPanel();
        pcomboContour.setBorder(new TitledBorder("plane"));
        comboContourModel = new MyComboBoxModel();
        comboContour = new JComboBox(comboContourModel);
        // comboContour.setRenderer(new MyCellRenderer());

        ChaseTransformGroup tgroup = (ChaseTransformGroup) parent
                .getRootTransform();
        Object[] cdc = tgroup.getChildren(Contour.class);
        if (cdc != null && cdc.length != 0) {
            for (int i = 0; i < cdc.length; i++) {
                comboContour.addItem(cdc[i]);
            }
        }
        pcomboContour.add(comboContour);

        ontheFly = new JCheckBox("render on the fly");
        JPanel pp = new JPanel();
        pp.setLayout(new BoxLayout(pp, BoxLayout.X_AXIS));
        pp.add(pcomboContour);
        // pp.add(ontheFly);

        JPanel pane = new JPanel();
        pane.setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS));
        pane.add(pdataCombo);
        pane.add(pp);

        Container container = getContentPane();
        // container.setLayout(new BoxLayout(container,BoxLayout.Y_AXIS));
        container.setLayout(new BorderLayout());

        JPanel btnPanel = new JPanel();
        // btnPanel.setLayout(new BoxLayout(btnPanel,BoxLayout.X_AXIS));
        JButton apply = new JButton("apply");
        JButton add = new JButton("add");
        JButton remove = new JButton("remove");
        JButton close = new JButton("close");
        btnPanel.add(ontheFly);
        btnPanel.add(apply);
        btnPanel.add(add);
        btnPanel.add(remove);
        btnPanel.add(close);

        container.add(pane, BorderLayout.NORTH);
        container.add(tabbedPane, BorderLayout.CENTER);
        container.add(btnPanel, BorderLayout.SOUTH);

        close.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                removeGuidePlane();
                dispose();
            }
        });

        apply.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                recreateContour();
                removeGuidePlane();
            }
        });

        add.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                drawContour();
                removeGuidePlane();
            }
        });

        remove.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                removeContour();
                removeGuidePlane();
            }
        });

        comboContour.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                updateGUI();
                removeGuidePlane();
            }
        });

        ontheFly.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                if (guidePlane != null)
                    if (ontheFly.isSelected()) {
                        guidePlane.setTransparency(1f);
                        recreateContour();
                    } else
                        guidePlane.resetTransparency();
            }
        });

        updateGUI();
    }

    private void removeGuidePlane() {
        if (guidePlane != null) {
            guidePlane.removeGuidePlane();
        }
    }

    private void setCOMAndLenMax() {
        setCOMAndLenMax(true);
    }

    private void setCOMAndLenMax(boolean check) {
        this.lenmax = 1;

        if (jusin == null) {
            this.jusin = new double[3];
            for (int i = 0; i < 3; i++)
                jusin[i] = 0d;
        }
        if (parent instanceof MainPanel) {
            TGAtom tgatom = (TGAtom) parent.getRootTransform();
            double[] jusin_buff = tgatom.getJusin();
            this.lenmax = tgatom.getLenMax();
            // this.jusin = tgatom.getCOM();
            // double[] jusin_buff =
            // tgatom.getConfigData().getCellOriginVector();
            // double[] celloff = tgatom.getCellOffset();
            boolean comChanged = false;
            for (int i = 0; i < 3; i++) {
                // jusin_buff[i] = -(jusin_buff[i] + celloff[i] * lenmax);
                if (jusin_buff[i] != this.jusin[i])
                    comChanged = true;
            }

            if (!check) {
                this.jusin = jusin_buff;
                return;
            }

            logger.debug("jusin : " + jusin[0] + ", " + jusin[1] + ", "
                    + jusin[2]);
            logger.debug("jusin_buff : " + jusin_buff[0] + ", " + jusin_buff[1]
                    + ", " + jusin_buff[2]);

            // 重心が変更されていた場合, GuidePlaneの原点も相応にずらす
            if (comChanged) {
                double[] diff = new double[3];
                for (int i = 0; i < 3; i++) {
                    diff[i] = this.jusin[i] - jusin_buff[i];
                    this.jusin[i] = jusin_buff[i];
                }
                if (guidePlane != null) {
                    Point3f orig = guidePlane.getOrigin();
                    orig.x += (float) (diff[0] / lenmax);
                    orig.y += (float) (diff[1] / lenmax);
                    orig.z += (float) (diff[2] / lenmax);
                    guidePlane.setOrigin(orig);
                }
            }
            logger.debug("lenmax: " + lenmax);
            logger.debug("jusin: " + jusin[0] + " " + jusin[1] + " " + jusin[2]);
        } else {
            float[] tmp = new float[3];
            if (dataCombo != null && dataCombo.getSelectedIndex() >= 0) {
                tmp = ((VolumetricData) dataCombo.getSelectedItem())
                        .getOrigin();
                for (int i = 0; i < tmp.length; i++)
                    jusin[i] = (double) tmp[i];
            }
        }
    }

    private JPanel createPlanePanel() {
        setCOMAndLenMax(false);
        this.bounds = ((ChaseTransformGroup) parent.getRootTransform())
                .getEffectiveBounds();

        float sizex = (float) Math.sqrt(Math.pow(bounds[0][0], 2)
                + Math.pow(bounds[1][0], 2) + Math.pow(bounds[2][0], 2));
        float sizey = (float) Math.sqrt(Math.pow(bounds[0][1], 2)
                + Math.pow(bounds[1][1], 2) + Math.pow(bounds[2][1], 2));
        logger.debug("effective bounds: " + sizex + ", " + sizey);
        if (guidePlane == null) {
            guidePlane = new GuidePlane(parent.getRootTransform(), sizex, sizey);
        }
        // guidePlane.setJusin(new
        // Point3f((float)jusin[0],(float)jusin[1],(float)jusin[2]));

        JPanel ret = new JPanel();
        ret.setLayout(new BoxLayout(ret, BoxLayout.X_AXIS));

        JPanel normalPanel = new JPanel();
        normalPanel.setLayout(new BoxLayout(normalPanel, BoxLayout.X_AXIS));
        normalPanel.setBorder(new TitledBorder("normal vector of plane"));
        JPanel tnx = new JPanel();
        tnx.setBorder(new TitledBorder("x"));
        textNormalX = new JTextField(10);
        textNormalX.setText("0.0");
        tnx.add(textNormalX);

        JPanel tny = new JPanel();
        tny.setBorder(new TitledBorder("y"));
        textNormalY = new JTextField(10);
        textNormalY.setText("0.0");
        tny.add(textNormalY);

        JPanel tnz = new JPanel();
        tnz.setBorder(new TitledBorder("z"));
        textNormalZ = new JTextField(10);
        tnz.add(textNormalZ);
        textNormalZ.setText("1.0");

        normalPanel.add(tnx);
        normalPanel.add(tny);
        normalPanel.add(tnz);

        JPanel rotPanel = new JPanel();
        rotPanel.setLayout(new BoxLayout(rotPanel, BoxLayout.Y_AXIS));
        rotPanel.setBorder(new TitledBorder("rotation (degrees)"));

        RotListener rlistener = new RotListener();
        vsliderRotX = new ValueSlider(-180, 180);
        vsliderRotX.setTitleForBorder("around axis1");
        vsliderRotX.setOrientation(ValueSlider.HORIZONTAL);
        vsliderRotX.addValueSliderListener(rlistener);
        vsliderRotY = new ValueSlider(-180, 180);
        vsliderRotY.setTitleForBorder("around axis2");
        vsliderRotY.setOrientation(ValueSlider.HORIZONTAL);
        vsliderRotY.addValueSliderListener(rlistener);

        planeSelector = new JComboBox(planes);
        initRot = new JButton("init rotation");
        JPanel pplane = new JPanel();
        pplane.setBorder(new TitledBorder(""));
        // pplane.add(planeSelector);
        pplane.add(initRot);

        //
        // JPanel rty = new JPanel();
        // rty.setBorder(new TitledBorder("around y-axis"));
        // textRotY = new JTextField(5);
        // textRotY.setText("0");
        // sliderRotY = new JSlider(-180,180,0);
        // sliderRotY.setMajorTickSpacing(90);
        // sliderRotY.setMinorTickSpacing(30);
        // sliderRotY.setPaintTicks(true);
        // sliderRotY.setPaintLabels(true);
        // rty.add(sliderRotY);
        //
        // JPanel rtz = new JPanel();
        // rtz.setBorder(new TitledBorder("around z-axis"));
        // textRotZ = new JTextField(5);
        // sliderRotZ = new JSlider(-180,180,0);
        // sliderRotZ.setMajorTickSpacing(90);
        // sliderRotZ.setMinorTickSpacing(30);
        // sliderRotZ.setPaintTicks(true);
        // sliderRotZ.setPaintLabels(true);
        // rtz.add(sliderRotZ);
        // textRotZ.setText("0");

        rotPanel.add(pplane);
        rotPanel.add(vsliderRotX);
        rotPanel.add(vsliderRotY);

        centerPanel = new JPanel();
        centerPanel.setLayout(new BoxLayout(centerPanel, BoxLayout.Y_AXIS));
        centerPanel.setBorder(new TitledBorder("origin"));

        for (int i = 0; i < 3; i++) {
            logger.debug("cell : " + bounds[i][0] + " " + bounds[i][1] + " "
                    + bounds[i][2]);
        }
        // centerPanel.add(cnx);
        // centerPanel.add(cny);
        // centerPanel.add(cnz);

        JPanel norpanel = new JPanel();
        norpanel.setLayout(new BorderLayout());
        norpanel.add(normalPanel, BorderLayout.NORTH);
        norpanel.add(rotPanel, BorderLayout.CENTER);

        JTabbedPane tp = new JTabbedPane();
        tp.addTab("direction", norpanel);
        tp.addTab("origin", centerPanel);
        ret.add(tp);
        // ret.add(norpanel);
        // ret.add(centerPanel);

        float boundmax1 = bounds[0][0] + bounds[0][1] + bounds[0][2]
                - origin[0];
        float boundmax2 = bounds[1][0] + bounds[1][1] + bounds[1][2]
                - origin[1];
        float boundmax3 = bounds[2][0] + bounds[2][1] + bounds[2][2]
                - origin[2];
        sliderCenterX = new ValueSlider(0, boundmax1 * (float) lenmax);
        sliderCenterX.setTitleForBorder("axis 1");
        sliderCenterX.setOrientation(ValueSlider.HORIZONTAL);
        sliderCenterX.addValueSliderListener(this);
        sliderCenterY = new ValueSlider(0, boundmax2 * (float) lenmax);
        sliderCenterY.setTitleForBorder("axis 2");
        sliderCenterY.setOrientation(ValueSlider.HORIZONTAL);
        sliderCenterY.addValueSliderListener(this);
        sliderCenterZ = new ValueSlider(0, boundmax3 * (float) lenmax);
        sliderCenterZ.setOrientation(ValueSlider.HORIZONTAL);
        sliderCenterZ.setTitleForBorder("axis 3");
        sliderCenterZ.addValueSliderListener(this);
        JPanel xpanel = new JPanel();
        xpanel.add(sliderCenterX);
        JPanel ypanel = new JPanel();
        ypanel.add(sliderCenterY);
        JPanel zpanel = new JPanel();
        zpanel.add(sliderCenterZ);
        centerPanel.add(xpanel);
        centerPanel.add(ypanel);
        centerPanel.add(zpanel);

        CaretListener clist = new CaretListener() {
            public void caretUpdate(CaretEvent e) {
                updateGuidePlaneFromNormal();
            }
        };

        textNormalX.addCaretListener(clist);
        textNormalY.addCaretListener(clist);
        textNormalZ.addCaretListener(clist);

        initRot.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                vsliderRotX.setValue(0);
                vsliderRotY.setValue(0);
                prevx = 0;
                prevy = 0;
                textNormalX.setText("0.0");
                textNormalY.setText("0.0");
                textNormalZ.setText("1.0");
                updateGuidePlaneFromNormal();
                recreateContour();
                removeGuidePlane();
            }
        });

        return ret;
    }

    private ColorMapPanel colorBarPanel;

    private JPanel createAppearancePanel() {
        JPanel ret = new JPanel();
        ret.setLayout(new BoxLayout(ret, BoxLayout.Y_AXIS));

        JPanel pminmax = new JPanel();
        pminmax.setLayout(new BoxLayout(pminmax, BoxLayout.X_AXIS));
        textMin = new JTextField(10);
        textMax = new JTextField(10);
        float minv = parent.getAssociatedVolumetricData()[0].getMinVal();
        float maxv = parent.getAssociatedVolumetricData()[0].getMaxVal();
        setTitle(getTitle() + " min: " + formexp.format(minv) + ", max: "
                + formexp.format(maxv));
        // if ( minv < 0 ) {
        // minv = (float) 1e-10;
        // }

        textMin.addCaretListener(new CaretListener() {
            public void caretUpdate(CaretEvent ce) {
                float min;
                try {
                    min = Float.parseFloat(textMin.getText().trim());
                    colorBarPanel.setMinVal(min);
                } catch (Exception exc) {
                }
            }
        });

        textMax.addCaretListener(new CaretListener() {
            public void caretUpdate(CaretEvent ce) {
                float max;
                try {
                    max = Float.parseFloat(textMax.getText().trim());
                    colorBarPanel.setMaxVal(max);
                } catch (Exception exc) {
                }
            }
        });

        JPanel pmin = new JPanel();
        pmin.setBorder(new TitledBorder("min"));
        pmin.add(textMin);

        JPanel pmax = new JPanel();
        pmax.setBorder(new TitledBorder("max"));
        pmax.add(textMax);

        pminmax.add(pmin);
        pminmax.add(pmax);

        JPanel ptrans = new JPanel();
        // ptrans.setBorder(new TitledBorder("transparency"));
        ptrans.setLayout(new BoxLayout(ptrans, BoxLayout.X_AXIS));
        transp = new TransparencySelector();
        ptrans.add(transp);
        transp.addChangeListener(new ChangeListener() {
            public void stateChanged(ChangeEvent arg0) {
                if (ontheFly.isSelected())
                    recreateContour();
            }
        });

        // pminmax.add(ptrans);

        // attrPanel.add(pminmax);

        colorBarPanel = new ColorMapPanel(parent);
        colorBarPanel.setLayout(new BoxLayout(colorBarPanel, BoxLayout.Y_AXIS));
        colorBarPanel.addColorBarChangeListener(this);

        ret.add(pminmax);
        ret.add(ptrans);
        ret.add(colorBarPanel);

        textMin.setText(String.valueOf(minv));
        textMax.setText(String.valueOf(maxv));

        textMin.addCaretListener(new CaretListener() {
            public void caretUpdate(CaretEvent arg0) {
                if (!ontheFly.isSelected())
                    return;
                recreateContour();
            }
        });

        textMax.addCaretListener(new CaretListener() {
            public void caretUpdate(CaretEvent arg0) {
                if (!ontheFly.isSelected())
                    return;
                recreateContour();
            }
        });

        return ret;
    }

    private Point3f getPos() {
        Point3f center = new Point3f(0.0f, 0.0f, 0.0f);
        Point3f center_tmp = new Point3f();
        center_tmp.x = sliderCenterX.getValue()
                / (bounds[0][0] + bounds[0][1] + bounds[0][2]);
        center_tmp.y = sliderCenterY.getValue()
                / (bounds[1][0] + bounds[1][1] + bounds[1][2]);
        center_tmp.z = sliderCenterZ.getValue()
                / (bounds[2][0] + bounds[2][1] + bounds[2][2]);

        center.x = (center_tmp.x * bounds[0][0] + center_tmp.y * bounds[1][0]
                + center_tmp.z * bounds[2][0] - (float) (jusin[0]))
                / (float) lenmax;
        center.y = (center_tmp.x * bounds[0][1] + center_tmp.y * bounds[1][1]
                + center_tmp.z * bounds[2][1] - (float) (jusin[1]))
                / (float) lenmax;
        center.z = (center_tmp.x * bounds[0][2] + center_tmp.y * bounds[1][2]
                + center_tmp.z * bounds[2][2] - (float) (jusin[2]))
                / (float) lenmax;
        return center;
    }

    private Point3f getPos(Point3f pos) {
        Point3f ret = new Point3f();
        Point3f tmppos = new Point3f(pos);
        tmppos.x = tmppos.x / (bounds[0][0] + bounds[0][1] + bounds[0][2]);
        tmppos.y = tmppos.y / (bounds[1][0] + bounds[1][1] + bounds[1][2]);
        tmppos.z = tmppos.z / (bounds[2][0] + bounds[2][1] + bounds[2][2]);
        ret.x = (tmppos.x * bounds[0][0] + tmppos.y * bounds[1][0] + tmppos.z
                * bounds[2][0] - (float) (jusin[0]))
                / (float) lenmax;
        ret.y = (tmppos.x * bounds[0][1] + tmppos.y * bounds[1][1] + tmppos.z
                * bounds[2][1] - (float) (jusin[1]))
                / (float) lenmax;
        ret.z = (tmppos.x * bounds[0][2] + tmppos.y * bounds[1][2] + tmppos.z
                * bounds[2][2] - (float) (jusin[2]))
                / (float) lenmax;
        return ret;
    }

    private Point3f getPosRealCoords(Point3f j3dPos) {
        Point3f ret = new Point3f();
        double[][] tmpbound = new double[3][3];
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                tmpbound[i][j] = (double) bounds[j][i];
            }
        }
        double[][] tmppos = new double[3][1];
        tmppos[0][0] = ((double) j3dPos.x) * lenmax + jusin[0];
        tmppos[1][0] = ((double) j3dPos.y) * lenmax + jusin[1];
        tmppos[2][0] = ((double) j3dPos.z) * lenmax + jusin[2];
        Matrix A = new Matrix(tmpbound);
        Matrix P = new Matrix(tmppos);
        Matrix B = A.solve(P);
        ret.x = (float) B.get(0, 0)
                * (bounds[0][0] + bounds[0][1] + bounds[0][2]);
        ret.y = (float) B.get(1, 0)
                * (bounds[1][0] + bounds[1][1] + bounds[1][2]);
        ret.z = (float) B.get(2, 0)
                * (bounds[2][0] + bounds[2][1] + bounds[2][2]);
        return ret;
    }

    private Point3f getNorm() {
        String x = textNormalX.getText();
        String y = textNormalY.getText();
        String z = textNormalZ.getText();
        try {
            return new Point3f(Float.parseFloat(x.trim()), Float.parseFloat(y
                    .trim()), Float.parseFloat(z.trim()));
        } catch (Exception exc) {
            return null;
        }
    }

    private void setContourParameters(Contour contour) {
        // Point3f center = getPos();

        Point3f center = guidePlane.getOrigin();
        Point3f normal = guidePlane.getNormalVector();
        // try{
        // normal.x = Float.parseFloat(textNormalX.getText().trim());
        // normal.y = Float.parseFloat(textNormalY.getText().trim());
        // normal.z = Float.parseFloat(textNormalZ.getText().trim());
        // } catch(Exception exc) {
        // logger.error("invalid normal and/or origin; using defaults");
        // }

        //
        // double rotx = 0;
        // double roty = 0;
        // double rotz = 0;
        // rotx = ((double) sliderRotX.getValue()) * (Math.PI/180.0);
        // roty = ((double) sliderRotY.getValue()) * (Math.PI/180.0);
        // rotz = ((double) sliderRotZ.getValue()) * (Math.PI/180.0);
        // if ( rotx != 0 || roty != 0 || rotz != 0 ) {
        // Transform3D t3d = new Transform3D();
        // if ( rotx != 0 ) t3d.rotX(rotx);
        // if ( roty != 0 ) t3d.rotY(roty);
        // if ( rotz != 0 ) t3d.rotZ(rotz);
        // Point3f tmp = new Point3f(0,0,1f);
        // t3d.transform(tmp);
        // normal = tmp;
        // }

        Float min = null;
        Float max = null;
        try {
            min = new Float(textMin.getText().trim());
            max = new Float(textMax.getText().trim());
        } catch (NumberFormatException nfe) {
            return;
        }

        float trans = transp.getTransparency();

        int transmode = TransparencyAttributes.BLENDED;

        ColorMap map = colorBarPanel.getSelectedColorMap();
        boolean b = colorBarPanel.isInvert();

        contour.setNormalVector(normal);
        contour.setMinMax(min, max);
        contour.setOrigin(center);
        contour.setTransparency(trans);
        contour.setTransparencyMode(transmode);
        contour.setColorMap(map);
        contour.setInvert(b);
    }

    class MyComboBoxModel extends DefaultComboBoxModel {
        public void setSelectedItem(Object anObject) {
            logger.debug("set selected item : " + anObject);
            super.setSelectedItem(anObject);
        }
    }

    private void recreateContour() {
        // if ( comboContour.getItemCount() == 0 ) {
        try {
            setCOMAndLenMax();
            if (comboContourModel.getSize() == 0) {
                drawContour();
                return;
            }
            // }
            Contour contour = (Contour) comboContourModel.getSelectedItem();
            if (contour == null) {
                logger.error("contour is null!!!!");
            }
            setContourParameters(contour);
            contour.recreateContour();
        } catch (Exception exc) {
            exc.printStackTrace();
        }
    }

    private void drawContour() {
        setCOMAndLenMax();
        int count = comboContour.getItemCount();
        Contour contour = null;
        contour = new Contour(parent,
                (VolumetricData) dataCombo.getSelectedItem(), lenmax, jusin);
        if (parent instanceof FBZ) {
            contour.setClippingPlane(((FBZ) parent).getFBZ().getPlanes());
        }
        contour.setID(count + 1);
        contour.setChargeID(dataCombo.getSelectedIndex());

        setContourParameters(contour);
        setCOMAndLenMax();
        contour.createContour();

        TransformGroup tg = parent.getRootTransform();
        tg.addChild(contour);

        comboContourModel.addElement(contour);
        updateGUI();
        // comboContour.addItem(contour);
        // comboContour.setSelectedItem(contour);
    }

    private void removeContour() {
        if (comboContour.getSelectedIndex() < 0) {
            return;
        }
        int oldind = comboContour.getSelectedIndex();
        try {
            Object obj = comboContour.getSelectedItem();
            comboContourModel.removeElementAt(oldind);
            if (obj != null) {
                ((BranchGroup) obj).detach();
            }
            for (int i = 0; i < comboContourModel.getSize(); i++) {
                Object ob = comboContourModel.getElementAt(i);
                ((Contour) ob).setID(i + 1);
            }
        } catch (Exception exc) {
            exc.printStackTrace();
        }
        if (oldind == 0) {
            comboContour.setSelectedIndex(0);
        } else {
            comboContour.setSelectedIndex(oldind - 1);
        }
    }

    private DecimalFormat decform = new DecimalFormat("0.000");

    private void updateGUI() {
        if (comboContourModel.getSize() <= 0) {
            return;
        }
        dontShow = true;
        setCOMAndLenMax();
        Contour contour = (Contour) comboContour.getSelectedItem();
        if (contour == null) {
            return;
        }
        Point3f origin = contour.getOrigin();
        Point3f normal = contour.getNormalVector();
        textNormalX.setText(decform.format(normal.x));
        textNormalY.setText(decform.format(normal.y));
        textNormalZ.setText(decform.format(normal.z));

        float trans = contour.getTransparency();
        transp.setTransparency(trans);

        textMin.setText(String.valueOf(contour.getMinVal()));
        textMax.setText(String.valueOf(contour.getMaxVal()));

        Point3f p = getPosRealCoords(contour.getOrigin());

        sliderCenterX.setValue(p.x);
        sliderCenterY.setValue(p.y);
        sliderCenterZ.setValue(p.z);
        dontShow = false;

    }

    public void colorMapChanged(ColorMapChangeEvent event) {
        ColorMap colorMap = event.getColorMap();
        if (colorMap == null) {
            return;
        }
        boolean invert = event.invert();

        Object obj = comboContour.getSelectedItem();
        if (obj != null) {
            Contour cont = (Contour) obj;
            cont.setColorMap(colorMap);
            logger.debug("invert: " + invert);
            cont.setInvert(invert);
            cont.recreateContour();
        }
    }

    public void valueSliderValueChanged() {
        updateGuidePlane();
        if (ontheFly.isSelected())
            recreateContour();
    }

    private void updateGuidePlaneFromNormal() {
        Point3f no = getNorm();
        if (no == null) {
            return;
        }
        guidePlane.setNormalVector(no);
        guidePlane.setOrigin(getPos());
        guidePlane.updateGuidePlane();
    }

    private void updateGuidePlane() {
        if (guidePlane == null) {
            guidePlane = new GuidePlane(parent.getRootTransform());
        }

        logger.debug("updating guide plane...");
        // guidePlane.setNormalVector(getNorm());
        guidePlane.setOrigin(getPos());
        guidePlane.setTransformRot(transRot());
        // guidePlane.updateGuidePlane();
    }

    private double prevx = 0;

    private double prevy = 0;

    private Transform3D transRot() {
        double rotx = 0;
        double roty = 0;
        rotx = (((double) vsliderRotX.getValue()) - prevx) * (Math.PI / 180.0);
        roty = (((double) vsliderRotY.getValue()) - prevy) * (Math.PI / 180.0);
        if (rotx == 0 && roty == 0) {
            return new Transform3D();
        }
        prevx = (double) vsliderRotX.getValue();
        prevy = (double) vsliderRotY.getValue();

        Transform3D tx = new Transform3D();
        Transform3D ty = new Transform3D();

        if (planeSelector.getSelectedIndex() == 0) {
            if (rotx != 0) {
                tx.rotX(rotx);
            }
            if (roty != 0) {
                ty.rotY(roty);
            }
        } else if (planeSelector.getSelectedIndex() == 1) {
            if (rotx != 0) {
                tx.rotX(rotx);
            }
            if (roty != 0) {
                ty.rotZ(roty);
            }
        } else if (planeSelector.getSelectedIndex() == 2) {
            if (rotx != 0) {
                tx.rotY(rotx);
            }
            if (roty != 0) {
                ty.rotZ(roty);
            }
        }
        tx.mul(ty);
        return tx;
    }

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

    private void doSurfaces(ConfigDataUpdateEvent e) {
        TransformGroup tg = parent.getRootTransform();
        TransformGroup gtg = guidePlane.getTransform();
        Point3f origCenter = guidePlane.getOrigin();
        Vector rorigs = new Vector();
        Vector rnorms = new Vector();
        if (e.getType() == ConfigDataUpdateEvent.CELL_CHANGED) { // pre-process
            // for (int i = 0; i < comboContourModel.getSize(); i++) {
            // Contour cont = (Contour) comboContourModel.getElementAt(i);
            // logger.debug("orig normal: " + cont.getNormalVector());
            // cont
            // .setVolumetricData(parent.getAssociatedVolumetricData()[cont
            // .getChargeID()]);
            // Point3f orig = cont.getOrigin();
            // Point3f rorig = getPosRealCoords(orig);
            // rorigs.addElement(rorig);
            // rnorms.addElement(new Point3f(cont.getNormalVector()));
            // logger.debug("rorig: " + rorig);
            // }

            this.bounds = ((ChaseTransformGroup) parent.getRootTransform())
                    .getEffectiveBounds();
            logger.debug("bounds0: " + bounds[0][0] + ", " + bounds[0][1]
                    + ", " + bounds[0][2]);
            logger.debug("bounds1: " + bounds[1][0] + ", " + bounds[1][1]
                    + ", " + bounds[1][2]);
            logger.debug("bounds2: " + bounds[2][0] + ", " + bounds[2][1]
                    + ", " + bounds[2][2]);
            setCOMAndLenMax();
            updateCenterPanel();
        }
        //
        // for (int i = 0; i < comboContourModel.getSize(); i++) {
        // Contour cont = (Contour) comboContourModel.getElementAt(i);
        // cont.detach();
        // if (e.getType() == ConfigDataUpdateEvent.CELL_CHANGED) {
        // Point3f neworig = ((Point3f) rorigs.get(i));
        // logger.debug("new orig: " + neworig);
        // cont.setOrigin(neworig);
        // cont.setCOM(jusin);
        // cont.setLenMax(lenmax);
        // cont.setNormalVector((Point3f) rnorms.get(i));
        // cont.recreateContour();
        // logger.debug("new normal: " + cont.getNormalVector());
        // }
        // tg.addChild(cont);
        // }

        guidePlane.removeGuidePlane();
        float sizex = bounds[0][0] + bounds[1][0] + bounds[2][0];
        float sizey = bounds[0][1] + bounds[1][1] + bounds[2][1];
        guidePlane = new GuidePlane(parent.getRootTransform(), sizex, sizey);
        Transform3D currtrans = new Transform3D();
        gtg.getTransform(currtrans);
        guidePlane.setTransform(currtrans);
        guidePlane.setOrigin(origCenter);
        guidePlane.removeGuidePlane();
    }

    public boolean needsUpdate() {
        return true;
    }

    public void configDataUpdate() {
    }

    class RotListener implements ValueSliderListener {
        private ValueSlider vslider;

        RotListener() {
        }

        public void valueSliderValueChanged() {
            Transform3D tx = transRot();
            if (guidePlane == null) {
                guidePlane = new GuidePlane(parent.getRootTransform());
            }

            guidePlane.setTransformRot(tx);
            Point3f foo = guidePlane.getNormalVector();

            CaretListener[] clis = textNormalX.getCaretListeners();
            for (int i = 0; i < clis.length; i++) {
                textNormalX.removeCaretListener(clis[i]);
                textNormalY.removeCaretListener(clis[i]);
                textNormalZ.removeCaretListener(clis[i]);
            }
            textNormalX.setText(String.valueOf(foo.x));
            textNormalY.setText(String.valueOf(foo.y));
            textNormalZ.setText(String.valueOf(foo.z));
            for (int i = 0; i < clis.length; i++) {
                textNormalX.addCaretListener(clis[i]);
                textNormalY.addCaretListener(clis[i]);
                textNormalZ.addCaretListener(clis[i]);
            }

            if (ontheFly.isSelected())
                recreateContour();
        }
    }

    private boolean dontShow = false;

    class GuidePlane extends Plane {
        private TransformGroup transformGroup;

        private TransformGroup tg;

        private BranchGroup guidePlane;

        private float trans = 0.7f;
        private TransparencyAttributes tattr;

        private boolean visible = false;

        private Logger logger = Logger.getLogger(GuidePlane.class.getName());

        private Point3f axis1 = new Point3f(1, 0, 0);

        private Point3f axis2 = new Point3f(0, 1, 0);

        private Point3f[] initpoints = new Point3f[] { new Point3f(1f, 1f, 0f),
                new Point3f(1f, -1f, 0f), new Point3f(-1f, -1f, 0f),
                new Point3f(-1f, 1f, 0f) };

        private Color3f color = new Color3f(0.9f, 0.9f, 0.9f);

        GuidePlane(TransformGroup transformGroup) {
            super();
            this.transformGroup = transformGroup;
            guidePlane = new BranchGroup();
            guidePlane.setCapability(BranchGroup.ALLOW_DETACH);
            init();
        }

        GuidePlane(TransformGroup transformGroup, float sizex, float sizey) {
            initpoints = new Point3f[] { new Point3f(sizex, sizey, 0f),
                    new Point3f(sizex, -sizey, 0f),
                    new Point3f(-sizex, -sizey, 0f),
                    new Point3f(-sizex, sizey, 0f) };
            this.transformGroup = transformGroup;
            guidePlane = new BranchGroup();
            guidePlane.setCapability(BranchGroup.ALLOW_DETACH);
            init();
        }

        private Point3f jusin = new Point3f();

        void setJusin(Point3f jusin) {
            this.jusin = jusin;
        }

        private void init() {
            for (int i = 0; i < initpoints.length; i++)
                logger.debug("initpoints: " + initpoints[i]);

            QuadArray qarray = new QuadArray(initpoints.length,
                    QuadArray.COORDINATES | QuadArray.COLOR_3);
            qarray.setCoordinates(0, initpoints);
            qarray.setColors(0, new Color3f[] { color, color, color, color });
            Shape3D s3d = new Shape3D();
            try {
                qarray.setCapability(QuadArray.ALLOW_INTERSECT);
            } catch (RestrictedAccessException rae) {
            }
            s3d.setGeometry(qarray);

            Appearance app = new Appearance();
            tattr = new TransparencyAttributes();
            tattr.setCapability(TransparencyAttributes.ALLOW_VALUE_WRITE);
            tattr.setTransparency(trans);
            tattr.setTransparencyMode(TransparencyAttributes.BLENDED);
            app.setTransparencyAttributes(tattr);

            PolygonAttributes pattr = new PolygonAttributes();
            pattr.setCullFace(PolygonAttributes.CULL_NONE);
            pattr.setPolygonMode(PolygonAttributes.POLYGON_FILL);
            pattr.setBackFaceNormalFlip(true);
            app.setPolygonAttributes(pattr);

            s3d.setAppearance(app);
            tg = new TransformGroup();
            tg.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
            tg.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
            tg.addChild(s3d);
            // CylinderCreator creator = new CylinderCreator();
            // BranchGroup nor1 = creator.create(new Point3d(normal),new
            // Point3d(),0.01f);
            // tg.addChild(nor1);
            guidePlane.addChild(tg);
        }

        void setTransparency(float tran) {
            tattr.setTransparency(tran);
        }

        void resetTransparency() {
            tattr.setTransparency(this.trans);
        }

        private Transform3D transform = new Transform3D();

        private Transform3D transformRotX = new Transform3D();

        private Transform3D transformRotY = new Transform3D();

        private Transform3D transformRotZ = new Transform3D();

        public Point3f getNormalVector() {
            Transform3D currtrans = new Transform3D();
            tg.getTransform(currtrans);
            Vector3f vnormal = new Vector3f(0, 0, 1);
            currtrans.transform(vnormal);
            normal = new Point3f(vnormal);
            return normal;
        }

        void updateGuidePlane() {
            if (!visible && !dontShow) {
                transformGroup.addChild(guidePlane);
                visible = true;
            }
            logger.debug("axis1 & 2 before transformation: " + axis1 + " "
                    + axis2);
            Vector3f unit = new Vector3f(normal);
            // calculate vectors for rotation matrix
            // rotate object in any orientation, onto Y axis (exception handled
            // below)
            // (see page 418 of _Computer Graphics_ by Hearn and Baker)
            Vector3f uX = new Vector3f();
            Vector3f uY = new Vector3f();
            Vector3f uZ = new Vector3f();
            float magX;
            Transform3D rotateFix = new Transform3D();
            uZ = new Vector3f(unit);
            uX.cross(new Vector3f(0, 1, 0), unit);
            magX = uX.length();
            Transform3D rotateMatrix = null;

            // magX == 0 if object's axis is parallel to Z axis
            if (magX != 0) {
                uX.z = uX.z / magX;
                uX.x = uX.x / magX;
                uX.y = uX.y / magX;
                uY.cross(uZ, uX);

                // create the rotation matrix
                rotateMatrix = new Transform3D(
                        new Matrix4d(uX.x, uX.y, uX.z, 0, uY.x, uY.y, uY.z, 0,
                                uZ.x, uZ.y, uZ.z, 0, 0, 0, 0, 1));
                try {
                    rotateMatrix.invert();
                } catch (Exception exc) {
                    exc.printStackTrace();
                }
            } else {
                rotateMatrix = new Transform3D();
                rotateMatrix.rotX(Math.PI / 2.0);
            }

            rotateMatrix.transform(axis1);
            rotateMatrix.transform(axis2);

            rotateMatrix.setTranslation(new Vector3f(origin));
            tg.setTransform(rotateMatrix);
            logger.debug("axis1 & 2 after transformation: " + axis1 + " "
                    + axis2);
        }

        Point3f getAxis1() {
            return axis1;
        }

        Point3f getAxis2() {
            return axis2;
        }

        TransformGroup getTransform() {
            return tg;
        }

        /**
         * Transform3Dを直接セットする.
         * 
         * @param t3d
         *            セットしたいTransform3D
         */
        void setTransform(Transform3D t3d) {
            tg.setTransform(t3d);
        }

        /**
         * Transform3Dを直接セットする. 回転のみの場合これを使う.
         * 
         * @param t3d
         *            セットしたいトランスフォーム
         */
        void setTransformRot(Transform3D t3d) {
            if (!visible) {
                transformGroup.addChild(guidePlane);
                visible = true;
            }
            // tg.setTransform(t3d);
            // t3d.transform(normal);
            Transform3D currtrans = new Transform3D();
            tg.getTransform(currtrans);
            Matrix4d mat = new Matrix4d();
            currtrans.get(mat);

            // Translate to origin
            currtrans.setTranslation(new Vector3d(0.0, 0.0, 0.0));
            currtrans.mul(currtrans, t3d);

            // Set old translation back
            // Vector3d translation = new Vector3d(mat.m03, mat.m13, mat.m23);
            // t3d.setTranslation(translation);
            // currtrans.setTranslation(translation);
            Point3f point = new Point3f(jusin);
            point.add(origin);
            currtrans.setTranslation(new Vector3d(point));

            // Update xform
            tg.setTransform(currtrans);
            // tg.setTransform(t3d);
            // normal = new Point3f(0,0,1);
            currtrans.transform(normal);
        }

        /**
         * このGuidePlaneをシーンから削除
         */
        void removeGuidePlane() {
            guidePlane.detach();
            visible = false;
        }

        /**
         * GuidePlaneが可視か否かを返す.
         * 
         * @return 可視ならばtrue.
         */
        boolean isVisible() {
            return visible;
        }

    }
}
