package plugins.kernel.canvas;

import icy.gui.component.IcyTextField;
import icy.gui.component.IcyTextField.TextChangeListener;
import icy.gui.component.NumberTextField;
import icy.gui.component.button.ColorChooserButton;
import icy.gui.component.button.ColorChooserButton.ColorChangeListener;
import icy.gui.component.button.IcyToggleButton;
import icy.resource.ResourceUtil;
import icy.resource.icon.IcyIcon;
import icy.vtk.VtkImageVolume;
import icy.vtk.VtkImageVolume.VtkVolumeBlendType;

import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.util.EventListener;

import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.JCheckBox;

public class VtkSettingPanel extends JPanel implements ActionListener, TextChangeListener, ColorChangeListener

{
    /**
     * 
     */
    private static final long serialVersionUID = 6433369095311474470L;

    protected static final Image ICON_GPU = ResourceUtil.getAlphaIconAsImage("gpu.png");
    protected static final Image ICON_SHADING = ResourceUtil.getColorIconAsImage("shading.png");

    public static final String PROPERTY_BG_COLOR = "renderBGColor";
    public static final String PROPERTY_MAPPER = "volumeMapper";
    public static final String PROPERTY_BLENDING = "volumeBlending";
    public static final String PROPERTY_SAMPLE = "volumeSample";
    public static final String PROPERTY_INTERPOLATION = "volumeInterpolation";
    public static final String PROPERTY_SHADING = "shading";
    public static final String PROPERTY_AMBIENT = "volumeAmbient";
    public static final String PROPERTY_DIFFUSE = "volumeDiffuse";
    public static final String PROPERTY_SPECULAR = "volumeSpecular";

    /**
     * GUI
     */
    private ColorChooserButton bgColorButton;
    private JCheckBox gpuMapperCheckBox;
    private JComboBox volumeBlendingComboBox;
    private JComboBox volumeSampleComboBox;
    private JComboBox volumeInterpolationComboBox;
    private IcyToggleButton shadingButton;
    private NumberTextField volumeAmbientField;
    private NumberTextField volumeSpecularField;
    private NumberTextField volumeDiffuseField;

    /**
     * Create the panel.
     */
    public VtkSettingPanel()
    {
        super();

        initialize();

        updateState();

        bgColorButton.addColorChangeListener(this);
        gpuMapperCheckBox.addActionListener(this);
        volumeBlendingComboBox.addActionListener(this);
        volumeInterpolationComboBox.addActionListener(this);
        volumeSampleComboBox.addActionListener(this);
        shadingButton.addActionListener(this);

        volumeAmbientField.addTextChangeListener(this);
        volumeDiffuseField.addTextChangeListener(this);
        volumeSpecularField.addTextChangeListener(this);
    }

    protected void initialize()
    {
        setBorder(new EmptyBorder(2, 0, 2, 0));
        GridBagLayout gridBagLayout = new GridBagLayout();
        gridBagLayout.columnWidths = new int[] {0, 0, 0, 0, 0};
        gridBagLayout.rowHeights = new int[] {0, 0, 0, 0, 0, 0};
        gridBagLayout.columnWeights = new double[] {0.0, 1.0, 1.0, 1.0, Double.MIN_VALUE};
        gridBagLayout.rowWeights = new double[] {0.0, 0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE};
        setLayout(gridBagLayout);

        final JLabel lblBackground = new JLabel("Background color ");
        lblBackground.setToolTipText("Change background color");
        GridBagConstraints gbc_lblBackground = new GridBagConstraints();
        gbc_lblBackground.anchor = GridBagConstraints.WEST;
        gbc_lblBackground.insets = new Insets(0, 0, 5, 5);
        gbc_lblBackground.gridx = 0;
        gbc_lblBackground.gridy = 0;
        add(lblBackground, gbc_lblBackground);

        bgColorButton = new ColorChooserButton();
        bgColorButton.setToolTipText("Change background color");
        GridBagConstraints gbc_bgColorButton = new GridBagConstraints();
        gbc_bgColorButton.anchor = GridBagConstraints.WEST;
        gbc_bgColorButton.insets = new Insets(0, 0, 5, 5);
        gbc_bgColorButton.gridx = 1;
        gbc_bgColorButton.gridy = 0;
        add(bgColorButton, gbc_bgColorButton);

        gpuMapperCheckBox = new JCheckBox("New check box");
        gpuMapperCheckBox.setFocusable(false);
        gpuMapperCheckBox.setIconTextGap(8);
        gpuMapperCheckBox.setText("GPU rendering");
        gpuMapperCheckBox.setToolTipText("Enable GPU volume rendering");
        GridBagConstraints gbc_gpuMapperCheckBox = new GridBagConstraints();
        gbc_gpuMapperCheckBox.anchor = GridBagConstraints.EAST;
        gbc_gpuMapperCheckBox.gridwidth = 2;
        gbc_gpuMapperCheckBox.insets = new Insets(0, 0, 5, 0);
        gbc_gpuMapperCheckBox.gridx = 2;
        gbc_gpuMapperCheckBox.gridy = 0;
        add(gpuMapperCheckBox, gbc_gpuMapperCheckBox);

        final JLabel lblInterpolation = new JLabel("Interpolation  ");
        lblInterpolation.setToolTipText("Select volume rendering interpolation method");
        GridBagConstraints gbc_lblInterpolation = new GridBagConstraints();
        gbc_lblInterpolation.anchor = GridBagConstraints.WEST;
        gbc_lblInterpolation.insets = new Insets(0, 0, 5, 5);
        gbc_lblInterpolation.gridx = 0;
        gbc_lblInterpolation.gridy = 1;
        add(lblInterpolation, gbc_lblInterpolation);

        volumeInterpolationComboBox = new JComboBox();
        volumeInterpolationComboBox.setToolTipText("Select volume rendering interpolation method");
        volumeInterpolationComboBox.setMaximumRowCount(7);
        volumeInterpolationComboBox.setModel(new DefaultComboBoxModel(new String[] {"Nearest (Fast)", "Linear",
                "Cubic (Slow)"}));
        volumeInterpolationComboBox.setSelectedIndex(1);
        GridBagConstraints gbc_volumeInterpolationComboBox = new GridBagConstraints();
        gbc_volumeInterpolationComboBox.fill = GridBagConstraints.HORIZONTAL;
        gbc_volumeInterpolationComboBox.gridwidth = 3;
        gbc_volumeInterpolationComboBox.insets = new Insets(0, 0, 5, 0);
        gbc_volumeInterpolationComboBox.gridx = 1;
        gbc_volumeInterpolationComboBox.gridy = 1;
        add(volumeInterpolationComboBox, gbc_volumeInterpolationComboBox);

        JLabel lblBlending = new JLabel("Blending");
        lblBlending.setToolTipText("Select volume rendering blending method");
        GridBagConstraints gbc_lblBlending = new GridBagConstraints();
        gbc_lblBlending.anchor = GridBagConstraints.WEST;
        gbc_lblBlending.insets = new Insets(0, 0, 5, 5);
        gbc_lblBlending.gridx = 0;
        gbc_lblBlending.gridy = 2;
        add(lblBlending, gbc_lblBlending);

        volumeBlendingComboBox = new JComboBox();
        volumeBlendingComboBox.setToolTipText("Select volume rendering blending method");
        volumeBlendingComboBox.setModel(new DefaultComboBoxModel(VtkVolumeBlendType.values()));
        GridBagConstraints gbc_volumeBlendingComboBox = new GridBagConstraints();
        gbc_volumeBlendingComboBox.fill = GridBagConstraints.HORIZONTAL;
        gbc_volumeBlendingComboBox.gridwidth = 3;
        gbc_volumeBlendingComboBox.insets = new Insets(0, 0, 5, 0);
        gbc_volumeBlendingComboBox.gridx = 1;
        gbc_volumeBlendingComboBox.gridy = 2;
        add(volumeBlendingComboBox, gbc_volumeBlendingComboBox);

        final JLabel lblSample = new JLabel("Sample");
        lblSample.setToolTipText("Set volume sample resolution (raycaster mapper only)");
        GridBagConstraints gbc_lblSample = new GridBagConstraints();
        gbc_lblSample.anchor = GridBagConstraints.WEST;
        gbc_lblSample.insets = new Insets(0, 0, 5, 5);
        gbc_lblSample.gridx = 0;
        gbc_lblSample.gridy = 3;
        add(lblSample, gbc_lblSample);

        volumeSampleComboBox = new JComboBox();
        volumeSampleComboBox
                .setToolTipText("Use low value for fine (but slow) rendering and high value for fast (but coarse) rendering");
        volumeSampleComboBox.setMaximumRowCount(11);
        volumeSampleComboBox.setModel(new DefaultComboBoxModel(new String[] {"Auto", "1 (Slow)", "2", "3", "4", "5",
                "6", "7", "8", "9", "10 (Fast)"}));
        volumeSampleComboBox.setSelectedIndex(0);
        GridBagConstraints gbc_volumeSampleComboBox = new GridBagConstraints();
        gbc_volumeSampleComboBox.fill = GridBagConstraints.HORIZONTAL;
        gbc_volumeSampleComboBox.gridwidth = 3;
        gbc_volumeSampleComboBox.insets = new Insets(0, 0, 5, 0);
        gbc_volumeSampleComboBox.gridx = 1;
        gbc_volumeSampleComboBox.gridy = 3;
        add(volumeSampleComboBox, gbc_volumeSampleComboBox);

        shadingButton = new IcyToggleButton(new IcyIcon(ICON_SHADING, false));
        shadingButton.setIconTextGap(8);
        shadingButton.setText("Shading");
        shadingButton.setFocusable(false);
        shadingButton.setToolTipText("Enable volume shading");
        GridBagConstraints gbc_shadingBtn = new GridBagConstraints();
        gbc_shadingBtn.anchor = GridBagConstraints.WEST;
        gbc_shadingBtn.insets = new Insets(0, 0, 0, 5);
        gbc_shadingBtn.gridx = 0;
        gbc_shadingBtn.gridy = 4;
        add(shadingButton, gbc_shadingBtn);

        volumeAmbientField = new NumberTextField();
        volumeAmbientField.setToolTipText("Ambient lighting coefficient");
        GridBagConstraints gbc_volumeAmbientField = new GridBagConstraints();
        gbc_volumeAmbientField.fill = GridBagConstraints.HORIZONTAL;
        gbc_volumeAmbientField.insets = new Insets(0, 0, 0, 5);
        gbc_volumeAmbientField.gridx = 1;
        gbc_volumeAmbientField.gridy = 4;
        add(volumeAmbientField, gbc_volumeAmbientField);
        volumeAmbientField.setColumns(3);

        volumeDiffuseField = new NumberTextField();
        volumeDiffuseField.setToolTipText("Diffuse lighting coefficient");
        GridBagConstraints gbc_volumeDiffuseField = new GridBagConstraints();
        gbc_volumeDiffuseField.insets = new Insets(0, 0, 0, 5);
        gbc_volumeDiffuseField.fill = GridBagConstraints.HORIZONTAL;
        gbc_volumeDiffuseField.gridx = 2;
        gbc_volumeDiffuseField.gridy = 4;
        add(volumeDiffuseField, gbc_volumeDiffuseField);
        volumeDiffuseField.setColumns(3);

        volumeSpecularField = new NumberTextField();
        volumeSpecularField.setToolTipText("Specular lighting coefficient");
        GridBagConstraints gbc_volumeSpecularField = new GridBagConstraints();
        gbc_volumeSpecularField.fill = GridBagConstraints.HORIZONTAL;
        gbc_volumeSpecularField.gridx = 3;
        gbc_volumeSpecularField.gridy = 4;
        add(volumeSpecularField, gbc_volumeSpecularField);
        volumeSpecularField.setColumns(3);
    }

    protected void updateState()
    {
        // if (isBoundingBoxVisible())
        // {
        // boundingBoxGridCheckBox.setEnabled(true);
        // boundingBoxRulerCheckBox.setEnabled(true);
        // }
        // else
        // {
        // boundingBoxGridCheckBox.setEnabled(false);
        // boundingBoxRulerCheckBox.setEnabled(false);
        // }

        // switch (getVolumeMapperType())
        // {
        // case RAYCAST_CPU_FIXEDPOINT:
        // case RAYCAST_GPU_OPENGL:
        // volumeSampleComboBox.setEnabled(true);
        // volumeBlendingComboBox.setEnabled(true);
        // break;
        // case TEXTURE2D_OPENGL:
        // case TEXTURE3D_OPENGL:
        // volumeSampleComboBox.setEnabled(false);
        // volumeBlendingComboBox.setEnabled(false);
        // break;
        // }
    }

    public Color getBackgroundColor()
    {
        return bgColorButton.getColor();
    }

    public void setBackgroundColor(Color value)
    {
        bgColorButton.setColor(value);
    }

    public boolean getGPURendering()
    {
        return gpuMapperCheckBox.isSelected();
    }

    public void setGPURendering(boolean value)
    {
        gpuMapperCheckBox.setSelected(value);
    }

    public int getVolumeInterpolation()
    {
        return volumeInterpolationComboBox.getSelectedIndex();
    }

    public void setVolumeInterpolation(int value)
    {
        volumeInterpolationComboBox.setSelectedIndex(value);
    }

    public VtkVolumeBlendType getVolumeBlendingMode()
    {
        if (volumeBlendingComboBox.getSelectedIndex() == -1)
            return null;

        return (VtkVolumeBlendType) volumeBlendingComboBox.getSelectedItem();
    }

    public void setVolumeBlendingMode(VtkVolumeBlendType value)
    {
        volumeBlendingComboBox.setSelectedItem(value);
    }

    public int getVolumeSample()
    {
        return volumeSampleComboBox.getSelectedIndex();
    }

    public void setVolumeSample(int value)
    {
        volumeSampleComboBox.setSelectedIndex(value);
    }

    public double getVolumeAmbient()
    {
        return volumeAmbientField.getNumericValue();
    }

    public void setVolumeAmbient(double value)
    {
        volumeAmbientField.setNumericValue(value);
    }

    public double getVolumeDiffuse()
    {
        return volumeDiffuseField.getNumericValue();
    }

    public void setVolumeDiffuse(double value)
    {
        volumeDiffuseField.setNumericValue(value);
    }

    public double getVolumeSpecular()
    {
        return volumeSpecularField.getNumericValue();
    }

    public void setVolumeSpecular(double value)
    {
        volumeSpecularField.setNumericValue(value);
    }

    /**
     * @see VtkImageVolume#getShade()
     */
    public boolean getVolumeShading()
    {
        return shadingButton.isSelected();
    }

    /**
     * @see VtkImageVolume#setShade(boolean)
     */
    public void setVolumeShading(boolean value)
    {
        if (shadingButton.isSelected() != value)
            shadingButton.doClick();
    }

    /**
     * Add a SettingChange listener
     */
    public void addSettingChangeListener(SettingChangeListener listener)
    {
        listenerList.add(SettingChangeListener.class, listener);
    }

    /**
     * Remove a SettingChange listener
     */
    public void removeSettingChangeListener(SettingChangeListener listener)
    {
        listenerList.remove(SettingChangeListener.class, listener);
    }

    public void fireSettingChange(Object source, String propertyName, Object oldValue, Object newValue)
    {
        if ((oldValue != null) && (newValue != null) && oldValue.equals(newValue))
            return;

        final PropertyChangeEvent event = new PropertyChangeEvent(source, propertyName, oldValue, newValue);
        final SettingChangeListener[] listeners = getListeners(SettingChangeListener.class);

        for (int i = 0; i < listeners.length; i++)
            listeners[i].settingChange(event);
    }

    @Override
    public void colorChanged(ColorChooserButton source)
    {
        if (source == bgColorButton)
            fireSettingChange(source, PROPERTY_BG_COLOR, null, source.getColor());
    }

    @Override
    public void actionPerformed(ActionEvent e)
    {
        final Object source = e.getSource();

        if (source == gpuMapperCheckBox)
            fireSettingChange(source, PROPERTY_MAPPER, Boolean.valueOf(!gpuMapperCheckBox.isSelected()),
                    Boolean.valueOf(gpuMapperCheckBox.isSelected()));
        else if (source == volumeBlendingComboBox)
            fireSettingChange(source, PROPERTY_BLENDING, null, volumeBlendingComboBox.getSelectedItem());
        else if (source == volumeSampleComboBox)
            fireSettingChange(source, PROPERTY_SAMPLE, Integer.valueOf(-1),
                    Integer.valueOf(volumeSampleComboBox.getSelectedIndex()));
        else if (source == volumeInterpolationComboBox)
            fireSettingChange(source, PROPERTY_INTERPOLATION, Integer.valueOf(-1),
                    Integer.valueOf(volumeInterpolationComboBox.getSelectedIndex()));
        else if (source == shadingButton)
            fireSettingChange(source, PROPERTY_SHADING, Integer.valueOf(-1),
                    Boolean.valueOf(shadingButton.isSelected()));

        updateState();
    }

    @Override
    public void textChanged(IcyTextField source, boolean validate)
    {
        if (!validate)
            return;

        if (source == volumeAmbientField)
            fireSettingChange(source, PROPERTY_AMBIENT, Double.valueOf(-1d),
                    Double.valueOf(volumeAmbientField.getNumericValue()));
        else if (source == volumeDiffuseField)
            fireSettingChange(source, PROPERTY_DIFFUSE, Double.valueOf(-1d),
                    Double.valueOf(volumeDiffuseField.getNumericValue()));
        else if (source == volumeSpecularField)
            fireSettingChange(source, PROPERTY_SPECULAR, Double.valueOf(-1d),
                    Double.valueOf(volumeSpecularField.getNumericValue()));

        updateState();
    }

    public static interface SettingChangeListener extends EventListener
    {
        public void settingChange(PropertyChangeEvent evt);
    }
}