/*
 * Copyright 2010-2015 Institut Pasteur.
 * 
 * This file is part of Icy.
 * 
 * Icy is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Icy is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Icy. If not, see <http://www.gnu.org/licenses/>.
 */
package icy.gui.lut;

import icy.canvas.IcyCanvas3D;
import icy.file.xml.XMLPersistentHelper;
import icy.gui.component.button.IcyButton;
import icy.gui.component.button.IcyToggleButton;
import icy.gui.component.renderer.ColormapComboBoxRenderer;
import icy.gui.dialog.OpenDialog;
import icy.gui.dialog.SaveDialog;
import icy.gui.util.ComponentUtil;
import icy.gui.util.GuiUtil;
import icy.gui.viewer.Viewer;
import icy.image.colormap.IcyColorMap;
import icy.image.colormap.IcyColorMap.IcyColorMapType;
import icy.image.colormap.IcyColorMapEvent;
import icy.image.colormap.IcyColorMapListener;
import icy.image.lut.LUT;
import icy.image.lut.LUT.LUTChannel;
import icy.resource.ResourceUtil;
import icy.resource.icon.IcyIcon;
import icy.sequence.Sequence;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.ButtonGroup;
import javax.swing.JComboBox;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.SwingConstants;

/**
 * @author stephane
 */
public class ColormapPanel extends JPanel implements IcyColorMapListener
{
    /**
     * 
     */
    private static final long serialVersionUID = -4042084504553770641L;

    private static final String DEFAULT_COLORMAP_DIR = IcyColorMap.DEFAULT_COLORMAP_DIR;
    private static final String DEFAULT_COLORMAP_NAME = "colormap.xml";

    /**
     * gui
     */
    private final ColormapViewer colormapViewer;
    final IcyToggleButton rgbBtn;
    final IcyToggleButton grayBtn;
    final IcyToggleButton alphaBtn;
    final ButtonGroup colormapTypeBtnGrp;
    final JComboBox colormapComboBox;

    /**
     * associated Viewer & LUTBand
     */
    public final Viewer viewer;
    public final LUTChannel lutChannel;

    /**
     * cached
     */
    final IcyColorMap colormap;

    private boolean modifyingColormap;

    public ColormapPanel(final Viewer viewer, final LUTChannel lutChannel)
    {
        super();

        this.viewer = viewer;
        this.lutChannel = lutChannel;
        modifyingColormap = false;

        colormap = lutChannel.getColorMap();
        colormap.setName("Custom");

        colormapViewer = new ColormapViewer(lutChannel);

        // colormap type
        rgbBtn = new IcyToggleButton(new IcyIcon(ResourceUtil.ICON_RGB_COLOR, false));
        rgbBtn.setToolTipText("Set colormap type to Color");
        rgbBtn.setFocusPainted(false);
        ComponentUtil.setFixedWidth(rgbBtn, 26);
        grayBtn = new IcyToggleButton(new IcyIcon(ResourceUtil.ICON_GRAY_COLOR, false));
        grayBtn.setToolTipText("Set colormap type to Gray");
        grayBtn.setFocusPainted(false);
        ComponentUtil.setFixedWidth(grayBtn, 26);
        alphaBtn = new IcyToggleButton(new IcyIcon(ResourceUtil.ICON_ALPHA_COLOR, false));
        alphaBtn.setToolTipText("Set colormap type to Alpha (transparency)");
        alphaBtn.setFocusPainted(false);
        ComponentUtil.setFixedWidth(alphaBtn, 26);

        colormapTypeBtnGrp = new ButtonGroup();

        colormapTypeBtnGrp.add(rgbBtn);
        colormapTypeBtnGrp.add(grayBtn);
        colormapTypeBtnGrp.add(alphaBtn);

        // select item according to current colormap type
        updateColormapType(colormap.getType());

        rgbBtn.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                colormap.setType(IcyColorMapType.RGB);
            }
        });
        grayBtn.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                colormap.setType(IcyColorMapType.GRAY);
            }
        });
        alphaBtn.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                colormap.setType(IcyColorMapType.ALPHA);
            }
        });

        // alpha checkbox
        // final JCheckBox alphaEnabled = new JCheckBox("alpha", null, true);
        // alphaEnabled.setToolTipText("Enable alpha control");
        // alphaEnabled.addItemListener(new ItemListener()
        // {
        // @Override
        // public void itemStateChanged(ItemEvent e)
        // {
        // // set alpha
        // colormapViewer.setAlphaEnabled(e.getStateChange() == ItemEvent.SELECTED);
        // }
        // });

        final IcyColorMap defaultColormap;
        // retrieve the default sequence LUT
        final LUT defaultLUT = viewer.getSequence().getDefaultLUT();

        // compatible colormap (should always be the case)
        if (defaultLUT.isCompatible(lutChannel.getLut()))
        {
            defaultColormap = defaultLUT.getLutChannel(lutChannel.getChannel()).getColorMap();
            defaultColormap.setName("Default");
        }
        else
        {
            // asynchronous sequence change, get a default colormap from current one
            defaultColormap = new IcyColorMap("Default");
            // copy from current colormap
            defaultColormap.copyFrom(colormap);
        }

        // copy it in the sequence user colormap
        updateSequenceColormap();

        // get colormap list
        final List<IcyColorMap> colormaps = IcyColorMap.getAllColorMaps(true, true);

        // remove the default colormap if already present in the list
        colormaps.remove(defaultColormap);
        // and set it at position 0
        colormaps.add(0, defaultColormap);

        // this is the user customized colormap
        colormaps.add(colormap);

        // build colormap selector
        colormapComboBox = new JComboBox(colormaps.toArray());
        colormapComboBox.setRenderer(new ColormapComboBoxRenderer(colormapComboBox));
        // limit size
        ComponentUtil.setFixedWidth(colormapComboBox, 96);
        colormapComboBox.setToolTipText("Select colormap model");
        // don't want focusable here
        colormapComboBox.setFocusable(false);
        // set to current colormap
        colormapComboBox.setSelectedItem(colormap);

        colormapComboBox.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                setColorMap((IcyColorMap) colormapComboBox.getSelectedItem());
            }
        });

        // load button
        final IcyButton loadButton = new IcyButton(new IcyIcon(ResourceUtil.ICON_OPEN));
        loadButton.setFlat(true);
        loadButton.setToolTipText("Load colormap from file");

        // action to load colormap
        loadButton.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                final String filename = OpenDialog.chooseFile("Load colormap...", DEFAULT_COLORMAP_DIR,
                        DEFAULT_COLORMAP_NAME);

                if (filename != null)
                {
                    final IcyColorMap map = new IcyColorMap();

                    if (XMLPersistentHelper.loadFromXML(map, filename))
                        setColorMap(map);
                }
            }
        });

        // save button
        final IcyButton saveButton = new IcyButton(new IcyIcon(ResourceUtil.ICON_SAVE));
        saveButton.setFlat(true);
        saveButton.setToolTipText("Save colormap to file");

        // action to save colormap
        saveButton.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                final String filename = SaveDialog.chooseFile("Save colormap...", DEFAULT_COLORMAP_DIR,
                        DEFAULT_COLORMAP_NAME);

                if (filename != null)
                    XMLPersistentHelper.saveToXML(colormap, filename);
            }
        });

        // set up GUI
        final JPanel bottomPanel = new JPanel();
        bottomPanel.setLayout(new BorderLayout());
        bottomPanel.setBorder(BorderFactory.createEmptyBorder(2, 0, 0, 0));

        bottomPanel.add(GuiUtil.createLineBoxPanel(rgbBtn, grayBtn, alphaBtn), BorderLayout.WEST);
        bottomPanel.add(Box.createGlue(), BorderLayout.CENTER);
        bottomPanel.add(
                GuiUtil.createLineBoxPanel(colormapComboBox, new JSeparator(SwingConstants.VERTICAL), loadButton,
                        Box.createHorizontalStrut(2), saveButton), BorderLayout.EAST);
        // bottomPanel.add(Box.createHorizontalStrut(6));
        // bottomPanel.add(alphaEnabled);

        bottomPanel.validate();

        setLayout(new BorderLayout());

        add(colormapViewer, BorderLayout.CENTER);
        add(bottomPanel, BorderLayout.SOUTH);

        validate();
    }

    @Override
    public void addNotify()
    {
        super.addNotify();

        // listen colormap changes
        colormap.addListener(this);
    }

    @Override
    public void removeNotify()
    {
        colormap.removeListener(this);

        super.removeNotify();
    }

    /**
     * @return the colormapViewer
     */
    public ColormapViewer getColormapViewer()
    {
        return colormapViewer;
    }

    private void updateColormapType(IcyColorMapType type)
    {
        switch (type)
        {
            case RGB:
                colormapTypeBtnGrp.setSelected(rgbBtn.getModel(), true);
                break;
            case GRAY:
                colormapTypeBtnGrp.setSelected(grayBtn.getModel(), true);
                break;
            case ALPHA:
                colormapTypeBtnGrp.setSelected(alphaBtn.getModel(), true);
                break;
        }
    }

    /**
     * Set the colormap.
     */
    public void setColorMap(IcyColorMap src)
    {
        if (colormap == src)
            return;

        modifyingColormap = true;
        try
        {
            if (viewer.getCanvas() instanceof IcyCanvas3D)
            {
                // copy alpha component only if we have specific alpha info
                // copyAlpha = !src.alpha.isAllSame();
                colormap.beginUpdate();
                try
                {
                    colormap.copyFrom(src, false);
                    colormap.setAlphaToLinear3D();
                }
                finally
                {
                    colormap.endUpdate();
                }
            }
            else
                colormap.copyFrom(src, true);
        }
        finally
        {
            modifyingColormap = false;
        }

        colormapComboBox.setSelectedItem(colormap);
    }

    void updateSequenceColormap()
    {
        // set the current colormap in the sequence
        final Sequence seq = viewer.getSequence();

        if (seq != null)
        {
            final int ch = lutChannel.getChannel();

            if (ch < seq.getSizeC())
                seq.setColormap(ch, colormap, true);
        }
    }

    /**
     * @deprecated Use {@link #setColorMap(IcyColorMap)} instead.
     */
    @Deprecated
    public void copyColorMap(IcyColorMap src)
    {
        setColorMap(src);
    }

    @Override
    public void colorMapChanged(IcyColorMapEvent e)
    {
        switch (e.getType())
        {
            case TYPE_CHANGED:
                // colormap type has changed ? --> update combo state
                updateColormapType(e.getColormap().getType());
                break;

            case MAP_CHANGED:
                // colormap has been manually changed, set name to custom
                if (!modifyingColormap)
                    colormap.setName("Custom");
                // update the combo box colormap representation
                colormapComboBox.setSelectedItem(colormap);
                colormapComboBox.repaint();
                break;
        }

        updateSequenceColormap();
    }
}