package org.greenstone.gatherer.feedback;

import java.awt.image.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import javax.swing.event.*;
import java.io.*;
import java.util.ResourceBundle;
import java.text.MessageFormat;
import javax.swing.text.NumberFormatter;
import java.text.NumberFormat;
import java.beans.*;
import java.util.ArrayList;

/**
 * This is the class to generate a animation type from a list of ImageIcon.
 * It will display a JDialog window and then user can specify the frames
 * per second rate that the user want to use to view the list of the ImageIcon.
 * @author Veronica Liesaputra
 */
public class Movie extends JDialog
    implements ActionListener,ChangeListener,PropertyChangeListener
{
    /**
     * This variable will hold the resource of the words that is stored in Messages.properties file.
     * The calling using messages.getString(someString) will caused someString to be translated
     * into some other string that is hold in that file.usually it will caused it to be translated
     * to the language that the user use or choose to have as stated in Locale.
     */
    private static ResourceBundle messages;

    /**
     * This is the label that we use to display the ImageIcon.
     */
    private JLabel picture;

    /**
     * This is a textField where user can set how many frames per second they want to view the
     * ImageIcon.If user enter value that is bigger than the FPS_MAX then it will beep and user
     * will need to enter new valid value or it will stay as the previous valid value.
     * If user enter 0 then it will stop the animation.
     */
    private JFormattedTextField textField;

    /**
     * This is a slider where user can set how many frames per second they want to view the
     * ImageIcon.If user slide to 0 then animation stop.
     */
    private JSlider framesPerSecond;
    
    /**
     * This is the minimum frame per second value.
     */
    private int FPS_MIN = 0;

    /**
     * This is the maximum frame per second value. The value is the twice the amount of 
     * ImageIcon in the list.
     */
    private int FPS_MAX;

    /**
     * This is the initial frame per second value to view the ImageIcon.
     */
    private int FPS_INIT = 1;

    /**
     * This is the ImageIcon index that are currently showing in the label.
     */
    private int frameNumber;

    /**
     * This is the total number of ImageIcon in the list.
     */
    private int NUM_FRAMES;

    /**
     * This hold the list of the ImageIcon that are to be displayed.
     */
    private ArrayList images;

    /**
     * This is telling how long is the delay for each frame to be refreshed.
     */
    private int delay;

    /**
     * This is the timer that generate the animation of the list of ImageIcon.
     * The animation will be paused twice per cycle by restarting the timer.
     * If the the prame per second chosen by the user is 0 then the timer
     * will be stop.
     */
    private Timer timer;

    /**
     * This the flag to say when the animation should stop.
     * If frozen = true then the animation should stop otherwise the animation
     * should keep running.
     */
    private boolean frozen = false;

    /**
     * This will sho a modal-dialog and set up the value for the animation to run properly 
     * according to the list of ImageIcon passed in.
     * @param msg  hold the resource of the words that is stored in Messages.properties file.
     * @param img  hold the list of ImageIcon to be displayed.
     */
    public Movie (ResourceBundle msg,ArrayList img)
    {
	super();

	images = img;
	NUM_FRAMES = img.size(); 
	FPS_MAX = img.size() * 2;

	messages = msg; 
	setTitle(messages.getString("GenerateMovie"));
	setModal(true);
	setBackground(new Color (176,208,176));
    }

    /**
     * This method will seeting up the content pane of the dialog window.
     * It also setting up the timer and the delay.
     * @return the content pane of the window.
     */
    public JPanel create_UI()
    {
	JPanel cont;
	cont = new JPanel();
	cont.setOpaque(true);
	cont.setBackground(new Color (176,208,176));

	JButton cls;
	cls = new JButton(messages.getString("Close"));
	cls.addActionListener(new ActionListener ()
	    {
		public void actionPerformed (ActionEvent e)
		{
		    dispose();
		    if (timer != null)
			timer.stop();
		}
	    });
	cls.setDefaultCapable(true);
	cls.setActionCommand(messages.getString("Close"));
	cls.setBackground(new Color(176,208,176));


	if (images.size() <= 0)
	    {
		JLabel empty_lbl;
		empty_lbl = new JLabel ("No Image to be displayed   ");
		cont.add(empty_lbl);
		cont.add(cls);
		return cont;
	    }

	cont.setLayout(new BoxLayout(cont, BoxLayout.PAGE_AXIS));

	delay = 1000 / FPS_INIT;

	JLabel sliderLabel;
	sliderLabel = new JLabel(messages.getString("FramesPerSecond") + ": ", JLabel.CENTER);
	sliderLabel.setBackground(new Color(176,208,176));
        sliderLabel.setAlignmentX(Component.CENTER_ALIGNMENT);

        NumberFormat numberFormat;
	numberFormat = NumberFormat.getIntegerInstance();
        NumberFormatter formatter;
	formatter = new NumberFormatter(numberFormat);
        formatter.setMinimum(Integer.valueOf(FPS_MIN));
        formatter.setMaximum(Integer.valueOf(FPS_MAX));

        textField = new JFormattedTextField(formatter);
	textField.setBackground(new Color(224,240,224));
        textField.setValue(Integer.valueOf(FPS_INIT));
        textField.setColumns(5);
        textField.addPropertyChangeListener(this);

        textField.getInputMap().put(KeyStroke.getKeyStroke(
                                        KeyEvent.VK_ENTER, 0),
                                        "check");
        textField.getActionMap().put("check", new AbstractAction() 
	    {
		public void actionPerformed(ActionEvent e) 
		{
		    if (!textField.isEditValid()) 
			{ 
			    Toolkit.getDefaultToolkit().beep();
			    textField.selectAll();
			} 
		    else
			{ 
			    try 
				{                  
				    textField.commitEdit();
				} 
			    catch (java.text.ParseException exc) { }
			}
		}
	    });

        framesPerSecond = new JSlider(JSlider.HORIZONTAL,
                                      FPS_MIN, FPS_MAX, FPS_INIT);
        framesPerSecond.addChangeListener(this);
	framesPerSecond.setBackground(new Color(176,208,176));
	framesPerSecond.setMajorTickSpacing(10);
        framesPerSecond.setMinorTickSpacing(1);
        framesPerSecond.setPaintTicks(true);
        framesPerSecond.setPaintLabels(true);
        framesPerSecond.setBorder(BorderFactory.createEmptyBorder(0,0,10,0));
	
        picture = new JLabel();
	Dimension dim;
	int iw,ih;
	dim = Toolkit.getDefaultToolkit().getScreenSize();
	iw = (int) (dim.width - 30);
	ih = (int) (dim.height * 0.75);
	picture.setPreferredSize(new Dimension(iw,ih));
        picture.setHorizontalAlignment(JLabel.CENTER);
        picture.setAlignmentX(Component.CENTER_ALIGNMENT);
        picture.setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createLoweredBevelBorder(),
                BorderFactory.createEmptyBorder(10,10,10,10)));
	picture.setBackground(new Color(176,208,176));
        updatePicture(0);

	
        JPanel labelAndTextField;
	labelAndTextField = new JPanel();
        labelAndTextField.add(sliderLabel);
        labelAndTextField.add(textField);
	labelAndTextField.add(cls);
	labelAndTextField.setBackground(new Color(176,208,176));

	cont.add(picture);
	cont.add(framesPerSecond);
        cont.add(labelAndTextField);
       
        cont.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));

        timer = new Timer(delay, this);
        timer.setInitialDelay(delay * 7); 
        timer.setCoalesce(true);
	timer.start();
	
	return cont;
    }

    /**
     * This method will tell what should happen when the slider's arrow
     * change its position.
     * <br>If user adjusting the frame per second value using the slider
     * then it will set the Textfield to have the value that slider point 
     * to at the moment.If the value is 0 then it will stop the animation
     * otherwise it will keep animate with the new frame per second value.
     * @param e is the slider when the slider's arrow change its position.
     */
    public void stateChanged(ChangeEvent e) 
    {
        JSlider source;
	source = (JSlider)e.getSource();
        int fps;
	fps = (int)source.getValue();
        if (!source.getValueIsAdjusting()) 
	    { 
		textField.setValue(Integer.valueOf(fps));
		if (fps == 0) 
		    {
			if (!frozen) 
			    stopAnimation();
		    } 
		else 
		    {
			delay = 1000 / fps;
			timer.setDelay(delay);
			timer.setInitialDelay(delay * 10);
			if (frozen) 
			    startAnimation();
		    }
	    } 
	else 
	    { 
		textField.setText(String.valueOf(fps));
	    }
    }
   
    /**
     * This method will set the slider value when the user adjusting
     * the frame per second value using the textfield.
     * <br>If user adjusting the frame per second value using text field, the
     * slider's arrow position will be changed into that value.
     * @param e the text field
     */
    public void propertyChange(PropertyChangeEvent e) 
    {
        if ("value".equals(e.getPropertyName())) 
	    {
		Number value;
		value = (Number)e.getNewValue();
		if (framesPerSecond != null && value != null) 
		    {
			framesPerSecond.setValue(value.intValue());
		    }
	    }
    }

    /**
     * This method will start the timer.
     */
    public void startAnimation() 
    {
        timer.start();
        frozen = false;
    }

    /**
     * This method will stop the timer.
     */
    public void stopAnimation() 
    {
        timer.stop();
        frozen = true;
    }

    /**
     * This method will tell what should happen when the timer is start.
     * When the timer start it will increase the framenumber and will
     * update the label with the new image icon.
     * If the timer already display all the image icon in the list, it 
     * will restart the timer and reanimate the image icon again.
     * @param e fires when the timer start.
     */
    public void actionPerformed(ActionEvent e) 
    {
        if (frameNumber == (NUM_FRAMES - 1))
            frameNumber = 0;
        else 
            frameNumber++;
        
        updatePicture(frameNumber);

        if ((frameNumber == (NUM_FRAMES - 1)) ||
	    (frameNumber==(NUM_FRAMES/2 - 1)))
            timer.restart();
    }

    /**
     * This will update the label with the new ImageIcon.
     * If the ImageIcon is null then the label will display an error message
     * instead of the picture.
     * @param frameNum this is the index of the ImageIcon to be displayed. 
     */
    protected void updatePicture(int frameNum) 
    {
        if (images.get(frameNumber) != null) 
	    {
		picture.setIcon((ImageIcon) images.get(frameNumber));
	    } 
	else 
	    {
		picture.setText(messages.getString("image")+" #"
				+ frameNumber + " " +
				messages.getString("notfound"));
	    }
    }
}






