/*
!=======================================================================
!
!  PROGRAM  PHASE-Viewer  (PHASE-Viewer 2014.01 ver.3.3.0)
!
!  Created on 2005/11/10, 16:25
!  AUTHOR(S): KOGA, Junichiro
!  File : IsosurfacePanel.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.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.media.j3d.Node;
import javax.media.j3d.TransformGroup;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JColorChooser;
import javax.swing.JComboBox;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.JToggleButton;
import javax.swing.border.TitledBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.vecmath.Color3f;

import org.apache.log4j.Logger;

import ciss.phase_viewer.acviewer.BaseJ3DPanel;
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.MainPanel;
import ciss.phase_viewer.acviewer.colormap.BaseColorMap;
import ciss.phase_viewer.acviewer.colormap.ColorBar;
import ciss.phase_viewer.acviewer.colormap.ColorMap;
import ciss.phase_viewer.acviewer.fbz.FBZ;
import ciss.phase_viewer.acviewer.scenegraphelements.Isosurface;
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;

/**
 * 等値面表示設定画面
 * 
 * @author
 */
public class IsosurfacePanel extends InternalFrameChase implements ConfigData {
    private Logger logger = Logger.getLogger(IsosurfacePanel.class.getName());
    private BaseJ3DPanel parent;

    private Color defaultColor = new Color((int) (0.9f * 255.f),
            (int) (0.9f * 255.f), (int) (0.5f * 255.f));

    /** Creates a new instance of IsosurfacePanel */
    public IsosurfacePanel(BaseJ3DPanel parent) {
        super("Isosurface panel", new Dimension(550, 430));
        this.parent = parent;
        parent.addDisposeOnExit(this);
        if (parent instanceof MainPanel) {
            ((CoordsViewerInterface) parent).getCD().register(this);
        }
        init();
        setVisible(true);
    }

    private JComboBox isosurfaces;
    private JButton colorChooser;
    private JTextField textTrans;
    private JComboBox normalSelector;
    private JComboBox colorMaps;
    private JComboBox colorScheme;
    private JTextField minText;
    private JTextField maxText;
    private float minval;
    private float maxval;
    private JCheckBox invert;
    private JToggleButton colorbar;

    private VolumetricData density;
    private TransformGroup transformGroup;

    private JComboBox dataSelector;
    private VolumetricData[] densities;
    private TitledBorder titledBorder;
    private JPanel pall;

    private TransparencySelector transparencySelector;
    private ValueSlider valueSlider;

    private JCheckBox ontheFly;

    private void init() {

        densities = parent.getAssociatedVolumetricData();
        density = densities[0];
        transformGroup = parent.getRootTransform();

        Container container = getContentPane();
        String title = null;
        if (density != null) {
            title = density.toString();
        }
        container.setLayout(new BoxLayout(container, BoxLayout.Y_AXIS));

        pall = new JPanel();
        pall.setLayout(new BoxLayout(pall, BoxLayout.Y_AXIS));
        if (title != null) {
            titledBorder = new TitledBorder(title);
            pall.setBorder(titledBorder);
        }

        JPanel datapanel = new JPanel();
        datapanel.setLayout(new BoxLayout(datapanel, BoxLayout.X_AXIS));
        JPanel selepanel = new JPanel();
        selepanel.setBorder(new TitledBorder("select data"));
        dataSelector = new JComboBox();
        selepanel.add(dataSelector);
        datapanel.add(selepanel);

        // panel.setLayout(new BoxLayout(panel,BoxLayout.X_AXIS));
        float mid = density.getMidVal();
        minval = density.getMinVal();
        maxval = density.getMaxVal();

        valueSlider = new ValueSlider(minval, maxval,
                density.getInterpolationScheme());

        if (parent instanceof FBZ) {
            valueSlider.setValue(((FBZ) parent).getFBZData().fermiEnergy);
        } else {
            valueSlider.setValue(density.getMidVal());
        }

        datapanel.add(valueSlider);

        ontheFly = new JCheckBox("render on the fly");
        // datapanel.add(ontheFly);

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

        GridBagConstraints gc = new GridBagConstraints();
        gc.weightx = 1;
        gc.weighty = 0;
        gc.fill = GridBagConstraints.HORIZONTAL;
        gc.anchor = GridBagConstraints.CENTER;

        Node[] nds = ((ChaseTransformGroup) transformGroup)
                .getChildren(Isosurface.class);
        if (nds != null && nds.length != 0) {
            isosurfaces = new JComboBox(nds);
        } else {
            isosurfaces = new JComboBox();
        }

        JPanel psurfs = new JPanel();
        psurfs.setLayout(new GridBagLayout());
        psurfs.add(isosurfaces, gc);
        psurfs.setBorder(new TitledBorder("select isosurface"));

        transparencySelector = new TransparencySelector();

        colorChooser = new JButton("color");
        colorChooser.setBackground(defaultColor);

        normalSelector = new JComboBox(new String[] { "built-in", "Java3D" });
        JPanel pns = new JPanel();
        pns.setLayout(new GridBagLayout());
        pns.setBorder(new TitledBorder("normal generator"));
        pns.add(normalSelector, gc);

        props.add(psurfs);
        props.add(transparencySelector);
        // props.add(ptrans);
        // props.add(colorChooser);
        props.add(pns);

        JPanel pcolor = new JPanel();
        pcolor.setLayout(new BoxLayout(pcolor, BoxLayout.X_AXIS));
        JPanel pcscheme = new JPanel();
        pcscheme.setLayout(new GridBagLayout());
        pcscheme.setBorder(new TitledBorder("coloring scheme"));
        colorScheme = new JComboBox(new String[] { "by color", "by value" });
        pcscheme.add(colorScheme, gc);
        ColorMap[] cmaps = BaseColorMap.getSupportedColorMaps();
        if (cmaps == null || cmaps.length == 0) {
            logger.error("corrupted colormap file!!!");
        }

        JPanel pcmapall = new JPanel();
        pcmapall.setLayout(new BoxLayout(pcmapall, BoxLayout.Y_AXIS));
        colorMaps = new JComboBox(cmaps);
        JPanel pcmaps = new JPanel();
        pcmaps.setBorder(new TitledBorder("color map"));
        pcmaps.add(colorMaps);
        invert = new JCheckBox("invert");
        colorbar = new JToggleButton("colorbar");
        pcmaps.add(invert);
        pcmaps.add(colorbar);

        pcmapall.add(pcmaps);
        JPanel pcmaptext = new JPanel();
        pcmaptext.setLayout(new BoxLayout(pcmaptext, BoxLayout.X_AXIS));
        JPanel minpanel = new JPanel();
        minpanel.setBorder(new TitledBorder("min"));
        JPanel maxpanel = new JPanel();
        maxpanel.setBorder(new TitledBorder("max"));
        float tmpmin = minval;
        float tmpmax = maxval;
        // if ( tmpmin <= 0.0 ) {
        // tmpmin = (float) 1e-10;
        // }
        // if ( tmpmax <= 0.0 ) {
        // tmpmax = (float) 1e-10;
        // }
        minText = new JTextField(7);
        minText.setText(String.valueOf(tmpmin));
        maxText = new JTextField(7);
        maxText.setText(String.valueOf(tmpmax));

        minpanel.add(minText);
        maxpanel.add(maxText);
        pcmaptext.add(minpanel);
        pcmaptext.add(maxpanel);
        pcmapall.add(pcmaptext);

        pcolor.add(pcscheme);
        pcolor.add(pcmapall);
        pcolor.add(colorChooser);

        pall.add(datapanel);
        pall.add(props);
        pall.add(pcolor);

        JButton apply = new JButton("apply");
        JButton add = new JButton("add");
        JButton remove = new JButton("remove");

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

        container.add(pall);

        if (densities != null) {
            for (int i = 0; i < densities.length; i++) {
                dataSelector.addItem(densities[i]);
            }
            dataSelector.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    boolean valchanged = true;
                    try {
                        if (density.getMidVal() == valueSlider.getValue()) {
                            valchanged = false;
                        }
                    } catch (Exception exc) {
                    }
                    density = (VolumetricData) dataSelector.getSelectedItem();
                    if (titledBorder != null) {
                        titledBorder.setTitle(density.toString());
                        pall.repaint();
                    }
                    valueSlider.setMinMax(density.getMinVal(),
                            density.getMaxVal());
                    if (parent instanceof MainPanel && !valchanged) {
                        float mid = density.getMidVal();
                        valueSlider.setValue(mid);
                    }
                }
            });
        }

        isosurfaces.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                Object obj = isosurfaces.getSelectedItem();
                if (obj != null) {
                    valueSlider.setValue(((Isosurface) obj).getValue());
                }
                updateGUI();
            }
        });

        valueSlider.addValueSliderListener(new ValueSliderListener() {
            public void valueSliderValueChanged() {
                if (!ontheFly.isSelected())
                    return;
                applyChanges(false);
                if (colorScheme.getSelectedIndex() == 1) {
                    updateAllIsoSurfaces();
                    updateColorBar();
                }
            }
        });

        ontheFly.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                if (!ontheFly.isSelected())
                    return;
                applyChanges(false);
                if (colorScheme.getSelectedIndex() == 1) {
                    updateAllIsoSurfaces();
                    updateColorBar();
                }
            }
        });

        colorScheme.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                if (!ontheFly.isSelected())
                    return;
                applyChanges(false);
                if (colorScheme.getSelectedIndex() == 1) {
                    updateAllIsoSurfaces();
                    updateColorBar();
                }
            }
        });

        transparencySelector.addChangeListener(new ChangeListener() {
            public void stateChanged(ChangeEvent arg0) {
                if (!ontheFly.isSelected())
                    return;
                applyChanges(false);
                if (colorScheme.getSelectedIndex() == 1) {
                    updateAllIsoSurfaces();
                    updateColorBar();
                }
            }
        });

        apply.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                applyChanges();
                if (colorScheme.getSelectedIndex() == 1) {
                    updateAllIsoSurfaces();
                    updateColorBar();
                }
            }
        });

        add.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                addIsosurface();
            }
        });

        remove.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                removeIsosurface();
            }
        });

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

        colorChooser.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                JColorChooser colorchooser = new JColorChooser();
                Color col = colorChooser.getBackground();
                Color color = JColorChooser.showDialog(parent, "Choose", col);
                if (color != null) {
                    colorChooser.setBackground(color);
                }
            }
        });

        colorbar.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                if (colorbar.isSelected()) {
                    drawColorBar();
                } else {
                    removeColorBar();
                }
            }
        });

        invert.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                updateColorBar();
                updateAllIsoSurfaces();
            }
        });

        colorMaps.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                updateColorBar();
                updateAllIsoSurfaces();
            }
        });

        colorScheme.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                if (colorScheme.getSelectedIndex() == 0) {
                    colorMaps.setEnabled(false);
                    minText.setEnabled(false);
                    maxText.setEnabled(false);
                    colorbar.setSelected(false);
                    colorbar.setEnabled(false);
                    removeColorBar();
                    colorChooser.setEnabled(true);
                } else if (colorScheme.getSelectedIndex() == 1) {
                    colorMaps.setEnabled(true);
                    minText.setEnabled(true);
                    maxText.setEnabled(true);
                    colorbar.setEnabled(true);
                    colorChooser.setEnabled(false);
                }
            }
        });

        colorScheme.setSelectedIndex(0);

        if (density != null
                && density.getMode() == VolumetricData.MOLECULAR_ORBITAL) {
            colorScheme.setSelectedIndex(1);
        }

    }

    private float[] getMinMaxVal() {
        float tmpmin = minval;
        float tmpmax = maxval;
        try {
            tmpmin = Float.parseFloat(minText.getText().trim());
            tmpmax = Float.parseFloat(maxText.getText().trim());
        } catch (Exception exc) {
        }
        return new float[] { tmpmin, tmpmax };
    }

    private ColorBar bar;

    private void drawColorBar() {
        removeColorBar();
        ColorMap map = (ColorMap) colorMaps.getSelectedItem();
        boolean b = invert.isSelected();
        try {
            float[] minmax = getMinMaxVal();
            bar = new ColorBar(map, b, new Float(minmax[0]), new Float(
                    minmax[1]), parent);
            bar.setInterpolationMode(parent.getAssociatedVolumetricData()[0]
                    .getInterpolationScheme());
            bar.create();
        } catch (Exception exc) {
            exc.printStackTrace();
        }
        parent.getRootBranch().addChild(bar);
        logger.debug("colorbar added.");
    }

    private void updateColorBar() {
        if (bar != null) {
            ColorMap map = (ColorMap) colorMaps.getSelectedItem();
            boolean b = invert.isSelected();
            float[] minmax = getMinMaxVal();
            bar.recreate(map, b, new Float(minmax[0]), new Float(minmax[1]));
        }
    }

    private void removeColorBar() {
        if (bar != null) {
            bar.detach();
        }
    }

    private void updateAllIsoSurfaces() {
        int n = isosurfaces.getItemCount();
        for (int i = 0; i < n; i++) {
            Isosurface surf = (Isosurface) isosurfaces.getItemAt(i);
            Color3f c3f = getColor(surf.getValue());
            surf.setColor(c3f);
            surf.createIsoSurface();
        }
        updateGUI();
    }

    private Color3f getColor(float value) {
        if (colorScheme.getSelectedIndex() == 0) {
            Color c = colorChooser.getBackground();
            return new Color3f(((float) c.getRed() / 255.f),
                    ((float) c.getGreen() / 255.f),
                    ((float) c.getBlue() / 255.f));
        } else if (colorScheme.getSelectedIndex() == 1) {
            double tmpmin = (double) minval;
            double tmpmax = (double) maxval;
            try {
                tmpmin = Double.parseDouble(minText.getText().trim());
                tmpmax = Double.parseDouble(maxText.getText().trim());
            } catch (Exception exc) {
            }
            double d = BaseColorMap.getLogScaledValue(tmpmin, tmpmax, value);
            ColorMap map = (ColorMap) colorMaps.getSelectedItem();
            boolean b = invert.isSelected();
            return map.getColor3f((float) d, b);
        }

        /* ここにはこない */
        return null;
    }

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

    private void applyChanges(boolean gui_mo) {
        Object obj = isosurfaces.getSelectedItem();
        if (obj == null) {
            addIsosurface();
            return;
        }
        Isosurface surf = (Isosurface) obj;
        updateIsoSurface(surf);
        if (gui_mo)
            updateGUI();
    }

    private void updateIsoSurface(Isosurface surf) {
        float val = valueSlider.getValue();

        Color3f c3f = getColor(val);
        surf.setColor(c3f);

        float trans = transparencySelector.getTransparency();

        surf.setValue(val);
        surf.setTransparency(trans);
        surf.setNormalGenerator(normalSelector.getSelectedIndex());
        surf.createIsoSurface();
    }

    private void addIsosurface() {
        try {
            float val = valueSlider.getValue();

            if (density == null) {
                logger.info("no volumetric data allocated.");
                return;
            }

            Color3f c3f = getColor(val);

            float trans = transparencySelector.getTransparency();

            Isosurface surf = new Isosurface(parent, density, c3f, val, trans,
                    isosurfaces.getItemCount());
            surf.setChargeID(dataSelector.getSelectedIndex());

            if (parent instanceof FBZ) {
                surf.setClippingPlane(((FBZ) parent).getFBZ().getPlanes());
            }

            surf.setNormalGenerator(normalSelector.getSelectedIndex());
            surf.createIsoSurface();

            isosurfaces.addItem(surf);
            isosurfaces.setSelectedItem(surf);

            transformGroup.addChild(surf);

        } catch (Exception exc) {
            exc.printStackTrace();
        }
        updateGUI();
    }

    private void removeIsosurface() {
        Object obj = isosurfaces.getSelectedItem();
        if (obj == null) {
            logger.info("no isosurface selected");
            return;
        }
        try {
            Isosurface surf = (Isosurface) obj;
            surf.detach();
            isosurfaces.removeItem(surf);
            updateGUI();
        } catch (Exception exc) {
            exc.printStackTrace();
        }
    }

    private void updateGUI() {
        for (int i = 0; i < isosurfaces.getItemCount(); i++) {
            Isosurface surf = (Isosurface) isosurfaces.getItemAt(i);
            surf.setID(i);
        }
        Object o = isosurfaces.getSelectedItem();
        if (o != null) {
            Color3f c = ((Isosurface) o).getColor();
            colorChooser.setBackground(new Color(c.x, c.y, c.z));
            transparencySelector.setTransparency(((Isosurface) o)
                    .getTransparency());
            valueSlider.setValue(((Isosurface) o).getValue());
            normalSelector.setSelectedIndex(((Isosurface) o)
                    .getNormalGenerator());
        }
    }

    public void configDataUpdate(boolean rescaleOnUpdate,
            ConfigDataUpdateEvent e) {
        transformGroup = parent.getRootTransform();
        densities = parent.getAssociatedVolumetricData();
        getContentPane().removeAll();
        init();
        updateGUI();
    }

    public boolean needsUpdate() {
        return true;
    }

    public void configDataUpdate() {
    }

}
