package net.sf.fmj.ejmf.toolkit.gui.controls;

import java.awt.BorderLayout;
import java.awt.Component;

import javax.media.Control;
import javax.media.Controller;
import javax.media.ControllerErrorEvent;
import javax.media.ControllerEvent;
import javax.media.ControllerListener;
import javax.media.StartEvent;
import javax.media.StopEvent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.EtchedBorder;
import javax.swing.border.TitledBorder;

import net.sf.fmj.ejmf.toolkit.util.SourcedTimer;
import net.sf.fmj.ejmf.toolkit.util.SourcedTimerEvent;
import net.sf.fmj.ejmf.toolkit.util.SourcedTimerListener;
import net.sf.fmj.ejmf.toolkit.util.TimeSource;

/**
* A read-only Control that displays media time.
* <p>
* Creates a SourcedTimer to generate ticks that trigger
* Control to look at media time, convert it to a string
* and display it.
* <p>
* Registers as SourcedTimerListener and ControllerListener.
* As ControllerListener, TimerDisplayControl listens for
* start/restart and stop events to correctly turn SourcedTimer
* on and off.
*
* @see net.sf.fmj.ejmf.toolkit.util.SourcedTimer
* @see net.sf.fmj.ejmf.toolkit.util.SourcedTimerListener
* @see net.sf.fmj.ejmf.toolkit.util.TimeSource
*/

public class TimeDisplayControl implements Control, 
		SourcedTimerListener, 
		TimeSource,
		ControllerListener {

    private JLabel 		timerField = new JLabel("0:00:00", JLabel.RIGHT);
    private JPanel		controlComponent = new JPanel();

    private String		timeVal;

    private SourcedTimer	timer;
    private Controller		controller;
    private long		divisor;

	/** 
	* Create a TimeDisplayControl for displaying the
	* current media time of the Controller passed as an
	* argument.
	* @param controller The Controller with which to associate 
	* control.
	*/
    public TimeDisplayControl(Controller controller) {
	this.controller = controller;

	// Create a timer and listener for 'ticks'
	timer = new SourcedTimer(this);
	timer.addSourcedTimerListener(this);

	// SourcedTimer units divided by divisor
	// equals seconds.
	divisor = timer.getConversionDivisor();

	controller.addControllerListener(this);

	setUpControlComponent();

	// Initialize time label
        timeVal = convertTime(getTime(), divisor);
	loadTime();
    }

	/** 
	* Create a TimeDisplayControl for displaying the
	* current media time of the Controller passed as an
	* argument.
	* <p>
	* The <code>timer</code> is used as the SourcedTimer.
	* This form of the constructor allows reuse of an existing
	* SourcedTimer.
	*
	* @param controller	javax.media.Controller
	* @param timer		ejmf.toolkit.util.SourcedTimer
	*	
	* @see net.sf.fmj.ejmf.toolkit.util.SourcedTimer
	*/
    public TimeDisplayControl(Controller controller, SourcedTimer timer) {
	this.controller = controller;

	// Create a timer and listener for 'ticks'
	this.timer = timer;
	timer.addSourcedTimerListener(this);

	// SourcedTimer units divided by divisor
	// equals seconds.
	divisor = timer.getConversionDivisor();

	controller.addControllerListener(this);

	setUpControlComponent();

	// Initialize time label
        timeVal = convertTime(getTime(), divisor);
	loadTime();
    }

	/** 
	* Return the control component.
	*
	* @return java.awt.Component
	*/
    public Component getControlComponent() {
	return controlComponent;
    }

	/**
	* Build border and title the control component.
	*/
    private void setUpControlComponent() {
        JLabel timeLabel = new JLabel("Media Time:", JLabel.RIGHT);
        JPanel mainPanel = new JPanel();
        int GAP = 10;

        Border emptyBorder  = new EmptyBorder(GAP,GAP,GAP,GAP);

        Border etchedBorder = new CompoundBorder(
            new EtchedBorder(), emptyBorder);

        Border titledBorder = new TitledBorder(
            etchedBorder, "Time Display Control");

        mainPanel.setBorder(titledBorder);
        mainPanel.setLayout( new BorderLayout(GAP, GAP) );
        mainPanel.add(timeLabel, BorderLayout.CENTER);
        mainPanel.add(timerField, BorderLayout.EAST);

        controlComponent.add(mainPanel);
    }


    	/**
 	* Input value expected in nanoseconds and converted	
	* to a String in h:mm:ss format.
        *
	* @param longTime	time in unit such that <code>longTime/d</code>
	* 			results in seconds.
	* @param		value such that <code>longTime/d</code> 
	* 			results in seconds.
	*
	* @return java.awt.String representation of converted time
     	*/
    private String convertTime(long longTime, long d) {
        int hr = 0;
        int min = 0;
        long sec = longTime / d;

        min = (int) (sec / 60);

        if (min > 0) {
    	    if (min > 60) {
    	        hr = min / 60;
    	        min = min % 60;
    	    }
    	    sec = sec % 60;
    	}

        String t = hr + ":";
	if (min < 10) {
	  t = t + 0 + min + ":";
	} else {
	  t = t + min + ":";
	}

        if (sec < 10) {
	  t = t + 0 + sec;
	} else {
	  t = t + sec;
	}
        return t;
     }

    
    // All but writing to text field is done before
    // call to loadTime. We don't want to do any conversion
    // on event thread.
	/*
	* Display the current media time.
	*/

    private void loadTime() {
	timerField.setText(timeVal);
    }

	/* 
	* Write time to GUI. Convenience class for	
	* execution on AWT thread.
	*/
    class LoadTimeThread implements Runnable {
	public void run() {
	    loadTime();
	}
    }

    //////////// SourcedTimerListener interface  ////////////

	/**
	* Respond to 'tick' from SourcedTimer.
	* <p>
	* @param e	a SourcedTimerEvent containing TimeSource
	* 		that generated tick.	
	*/
    public void timerUpdate(SourcedTimerEvent e) {
	timeVal = convertTime(getTime(), divisor);
	SwingUtilities.invokeLater(new LoadTimeThread());
    }

    ///////// ControllerListener Interface /////////////

	/**
	* Listen for start/stop events and start/stop 
	* SourceTimer in response.
	* @param e A controller event used to determine start/stop	
	* state of timer.
	*/
    public void controllerUpdate(ControllerEvent e) {
	if (e instanceof StopEvent ||
	    e instanceof ControllerErrorEvent) {
	    timer.stop();
	} else if (e instanceof StartEvent) {
	    timer.start();
	}
    }

    ////////// TimeSource Interface //////////////

	/**
	* Report media time from associated Controller.	
	* <p>
	* @return media time in nanoseconds
	*/
    public long getTime() {
    	return controller.getMediaNanoseconds();
    }

	/**
	* Report value such that <code>getTime()/number</code> equals seconds.
	* <p>
	* @return a long such that <code>getTime()/number</code> equals
	* 			seconds.
	*/
    public long getConversionDivisor() {
        return TimeSource.NANOS_PER_SEC;
    }

}
