/*
 * 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.viewer;

import icy.gui.component.IcySlider;
import icy.gui.component.button.IcyToggleButton;
import icy.gui.util.ComponentUtil;
import icy.gui.util.GuiUtil;
import icy.resource.ResourceUtil;
import icy.resource.icon.IcyIcon;
import icy.system.thread.ThreadUtil;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingConstants;
import javax.swing.Timer;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

/**
 * @author Stephane
 */
public class TNavigationPanel extends JPanel
{
    /**
     * 
     */
    private static final long serialVersionUID = 9123780562399386045L;

    private static final int DEFAULT_FRAME_RATE = 15;

    final JSlider slider;
    final JLabel leftLabel;
    final JLabel rightLabel;

    final IcyToggleButton play;
    final IcyToggleButton loop;
    final JSpinner frameRate;

    final Timer timer;

    public TNavigationPanel()
    {
        super(true);

        slider = new IcySlider(SwingConstants.HORIZONTAL);
        slider.setFocusable(false);
        slider.setMaximum(0);
        slider.setMinimum(0);
        slider.setToolTipText("Move cursor to navigate in T dimension");
        slider.addChangeListener(new ChangeListener()
        {
            @Override
            public void stateChanged(ChangeEvent e)
            {
                rightLabel.setText(Integer.toString(slider.getMaximum()));
                leftLabel.setText(Integer.toString(slider.getValue()));
                validate();
            }
        });

        ComponentUtil.setFixedHeight(slider, 22);

        timer = new Timer(1000 / DEFAULT_FRAME_RATE, new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                // only if slider is not adjusting T position
                if (!slider.getValueIsAdjusting())
                {
                    final int oldT = getTPosition();

                    incTPosition();

                    // end reached ?
                    if (oldT == getTPosition())
                    {
                        // loop mode --> reset
                        if (isRepeat())
                            setTPosition(0);
                        else
                        {
                            // end play
                            stopPlay();
                            // and reset position
                            setTPosition(0);
                        }
                    }
                }
            }
        });

        play = new IcyToggleButton(new IcyIcon(ResourceUtil.ICON_PLAY));
        play.setFlat(true);
        play.setToolTipText("play");
        play.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                if (isPlaying())
                    stopPlay();
                else
                    startPlay();
            }
        });

        loop = new IcyToggleButton(new IcyIcon(ResourceUtil.ICON_ARROW_RIGHT, 16));
        loop.setFlat(true);
        loop.setToolTipText("Enable loop playback");
        loop.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                setRepeat(!isRepeat());
            }
        });

        frameRate = new JSpinner(new SpinnerNumberModel(DEFAULT_FRAME_RATE, 1, 60, 1));
        frameRate.setFocusable(false);
        // no manual edition and edition focus
        final JTextField tf = ((JSpinner.DefaultEditor) frameRate.getEditor()).getTextField();
        tf.setEditable(false);
        tf.setFocusable(false);
        frameRate.setToolTipText("Change playback frame rate");
        frameRate.addChangeListener(new ChangeListener()
        {
            @Override
            public void stateChanged(ChangeEvent e)
            {
                final int f = ((Integer) frameRate.getValue()).intValue();
                // adjust timer delay
                setTimerDelay(1000 / f);
            }
        });
        ComponentUtil.setFixedSize(frameRate, new Dimension(50, 22));

        leftLabel = new JLabel("0");
        leftLabel.setToolTipText("T position");
        rightLabel = new JLabel("0");
        rightLabel.setToolTipText("T sequence size");

        final JPanel leftPanel = GuiUtil.createLineBoxPanel(Box.createHorizontalStrut(8), GuiUtil.createBoldLabel("T"),
                Box.createHorizontalStrut(10), leftLabel);

        rightLabel.addComponentListener(new ComponentAdapter()
        {
            @Override
            public void componentResized(ComponentEvent e)
            {
                // so left label is adjusted to right label size
                ComponentUtil.setFixedWidth(leftLabel, rightLabel.getWidth());
                leftPanel.revalidate();
            }
        });

        final JPanel rightPanel = new JPanel();
        rightPanel.setLayout(new BoxLayout(rightPanel, BoxLayout.LINE_AXIS));

        final JLabel frameRateLabel = new JLabel("FPS");
        frameRateLabel.setToolTipText("Frames Per Second");

        rightPanel.add(rightLabel);
        rightPanel.add(Box.createHorizontalStrut(12));
        rightPanel.add(play);
        rightPanel.add(loop);
        rightPanel.add(Box.createHorizontalStrut(8));
        rightPanel.add(frameRate);
        rightPanel.add(Box.createHorizontalStrut(4));
        rightPanel.add(frameRateLabel);
        rightPanel.add(Box.createHorizontalStrut(4));

        // setBorder(BorderFactory.createLineBorder(BorderFactory.createTitledBorder("").getTitleColor()));
        setBorder(BorderFactory.createTitledBorder("").getBorder());
        setLayout(new BorderLayout());

        add(leftPanel, BorderLayout.WEST);
        add(slider, BorderLayout.CENTER);
        add(rightPanel, BorderLayout.EAST);

        validate();
    }

    protected void incTPosition()
    {
        setTPosition(getTPosition() + 1);
    }

    protected void decTPosition()
    {
        setTPosition(Math.max(0, getTPosition() - 1));
    }

    protected void setTimerDelay(int delay)
    {
        timer.setDelay(delay);
    }

    protected int getTPosition()
    {
        return slider.getValue();
    }

    protected void setTPosition(final int t)
    {
        // we want to be sure the T position is changed in EDT
        ThreadUtil.invokeNow(new Runnable()
        {
            @Override
            public void run()
            {
                slider.setValue(t);
            }
        });
    }

    /**
     * Returns the frame rate (given in frame per second) for play command.
     */
    public int getFrameRate()
    {
        return ((Integer) frameRate.getValue()).intValue();
    }

    /**
     * Sets the frame rate (given in frame per second) for play command.
     */
    public void setFrameRate(int fps)
    {
        frameRate.setValue(Integer.valueOf(fps));
    }

    /**
     * Returns true if <code>repeat</code> is enabled for play command.
     */
    public boolean isRepeat()
    {
        return loop.getToolTipText().startsWith("Disable");
    }

    /**
     * Set <code>repeat</code> mode for play command.
     */
    public void setRepeat(boolean value)
    {
        if (value)
        {
            loop.setIcon(new IcyIcon(ResourceUtil.ICON_RELOAD, 16));
            loop.setSelected(true);
            loop.setToolTipText("Disable loop playback");
        }
        else
        {
            loop.setIcon(new IcyIcon(ResourceUtil.ICON_ARROW_RIGHT, 16));
            loop.setSelected(false);
            loop.setToolTipText("Enable loop playback");
        }
    }

    /**
     * Returns true if currently playing.
     */
    public boolean isPlaying()
    {
        return timer.isRunning();
    }

    /**
     * Start sequence play.
     * 
     * @see #stopPlay()
     * @see #setRepeat(boolean)
     */
    public void startPlay()
    {
        timer.start();
        play.setIcon(new IcyIcon(ResourceUtil.ICON_PAUSE));
        play.setSelected(true);
        play.setToolTipText("pause");
    }

    /**
     * Stop sequence play.
     * 
     * @see #startPlay()
     */
    public void stopPlay()
    {
        timer.stop();
        play.setIcon(new IcyIcon(ResourceUtil.ICON_PLAY));
        play.setSelected(false);
        play.setToolTipText("play");
    }

    /**
     * @see icy.gui.component.IcySlider#setPaintLabels(boolean)
     */
    public void setPaintLabels(boolean b)
    {
        slider.setPaintLabels(b);
    }

    /**
     * @see icy.gui.component.IcySlider#setPaintTicks(boolean)
     */
    public void setPaintTicks(boolean b)
    {
        slider.setPaintTicks(b);
    }

    /**
     * @see javax.swing.JSlider#addChangeListener(javax.swing.event.ChangeListener)
     */
    public void addChangeListener(ChangeListener l)
    {
        slider.addChangeListener(l);
    }

    /**
     * @see javax.swing.JSlider#removeChangeListener(javax.swing.event.ChangeListener)
     */
    public void removeChangeListener(ChangeListener l)
    {
        slider.removeChangeListener(l);
    }

    /**
     * Remove all change listener
     */
    public void removeAllChangeListener()
    {
        for (ChangeListener l : slider.getListeners(ChangeListener.class))
            slider.removeChangeListener(l);
    }

    /**
     * @see javax.swing.JSlider#getValue()
     */
    public int getValue()
    {
        return slider.getValue();
    }

    /**
     * @param n
     * @see javax.swing.JSlider#setValue(int)
     */
    public void setValue(int n)
    {
        slider.setValue(n);
    }

    /**
     * @see javax.swing.JSlider#getMinimum()
     */
    public int getMinimum()
    {
        return slider.getMinimum();
    }

    /**
     * @see javax.swing.JSlider#setMinimum(int)
     */
    public void setMinimum(int minimum)
    {
        slider.setMinimum(minimum);
    }

    /**
     * @see javax.swing.JSlider#getMaximum()
     */
    public int getMaximum()
    {
        return slider.getMaximum();
    }

    /**
     * @see javax.swing.JSlider#setMaximum(int)
     */
    public void setMaximum(int maximum)
    {
        slider.setMaximum(maximum);
    }

    /**
     * @see javax.swing.JSlider#getPaintTicks()
     */
    public boolean getPaintTicks()
    {
        return slider.getPaintTicks();
    }

    /**
     * @see javax.swing.JSlider#getPaintTrack()
     */
    public boolean getPaintTrack()
    {
        return slider.getPaintTrack();
    }

    /**
     * @see javax.swing.JSlider#setPaintTrack(boolean)
     */
    public void setPaintTrack(boolean b)
    {
        slider.setPaintTrack(b);
    }

    /**
     * @see javax.swing.JSlider#getPaintLabels()
     */
    public boolean getPaintLabels()
    {
        return slider.getPaintLabels();
    }
}