
import java.awt.*;
import javax.swing.*;
import javax.swing.border.Border;
import java.awt.event.ActionListener;

/**
 * <code>AddressBar</code> is a Swing GUI component written for {@link Azure},
 * providing a <code>JTextField</code> for inputting URLs and a status label
 * that can be updated by it's own thread (the <code>AddressBar</code>) as the
 * download of the source progresses. The status changes as follows: "Ready"
 * --> The thread is started. --> "Loading" --> "Loading." --> "Loading.." -->
 * "Loading..." --> "Loading" --> "Loading." --> And so on, repeated until the
 * thread is told that the download is complete. --> "Setting" --> The page is
 * hopefully loaded into view by the GUI in this time. --> "Ready". The
 * <code>AddressBar</code> needs to be provided with the <code>ActionListener
 * </code> for the URL input field.
 *
 * @author Pekka Ryynänen
 * @see Azure
 */
public class AddressBar extends JPanel implements Runnable {

    private JTextField urlTextField; // for entering sources
    private JLabel status;           // shows the changing status
    private Thread loadingThread;    // used for updating the status
    private boolean loading;         // controls the thread

    /**
     * Constructs an <code>AddressBar</code> containing two significant
     * components: a text field for URL input and a label showing download
     * status. The Swing GUI components use static colors from {@link Azure}
     * class. The thread created for changing the download status so
     * that the event-dispatching thread remains responsive is set
     * to daemon status so there's no need to stop it.
     *
     * @param urlListener the <code>ActionListener</code> for any text entered
     *        into the <code>JTextField</code> in the <code>AddressBar</code>
     * @see <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/awt/event/ActionListener.html">ActionListener</a>
     */
    public AddressBar(ActionListener urlListener) {
	super(new BorderLayout());
	((BorderLayout)getLayout()).setHgap(5);
	setBackground(Azure.azure);
	Border matte = BorderFactory.createMatteBorder(2,5,2,5,Azure.azure);
	Border matte2 = BorderFactory.createMatteBorder(2,10,2,2,Azure.azure);
	Border line = BorderFactory.createLineBorder(Azure.darkBlue);
	Border compound = BorderFactory.createCompoundBorder(line, matte2);
	setBorder(BorderFactory.createCompoundBorder(matte, compound));

	JLabel urlLabel = new JLabel("URL:");
	add(urlLabel, BorderLayout.WEST);

	urlTextField = new JTextField();
	urlTextField.setToolTipText("Write your URL here and press Enter.");
	urlTextField.addActionListener(urlListener);
	add(urlTextField, BorderLayout.CENTER);

	status = new JLabel("Status: Ready");
	status.setPreferredSize(new Dimension(120,10));
	status.setToolTipText("Page download status.");
	add(status, BorderLayout.EAST);

	loadingThread = new Thread(this);
	loadingThread.setDaemon(true);
    }

    /**
     * Changes the address shown by the <code>JTextField</code> in the
     * <code>AddressBar</code>. If the browsed page is changed by some other
     * means than writing a new address into the text field, should the
     * <code>AddressBar</code> be updated to show the correct address.
     *
     * @param newURL the string inserted into the text field
     */
    public void updateURL(String newURL) {
	urlTextField.setText(newURL);
    }

    /**
     * Changes the status text in the <code>AddressBar</code> according to
     * the pattern provided in the description of this class. A convenience
     * method called by the <code>run</code> method of this class.
     */
    private void updateStatus() {
	String loadStatus = status.getText();
	if (loadStatus.equals("Status: Ready") || 
	    loadStatus.equals("Status: Loading..."))
	    loadStatus = "Status: Loading";
	else if (loadStatus.equals("Status: Loading"))
	    loadStatus = "Status: Loading.";
	else if (loadStatus.equals("Status: Loading."))
	    loadStatus = "Status: Loading..";
	else if (loadStatus.equals("Status: Loading.."))
	    loadStatus = "Status: Loading...";
	status.setText(loadStatus);
    }

    /**
     * Puts the thread that updates the download status to work or
     * sets it (back) to standby. When <code>setLoadStatus</code> is called
     * for the first time with parameter <code>true</code>, the method starts
     * the thread. Calling this method with parameter <code>false</code> puts
     * the thread to standby until <code>setLoadStatus(true)</code> is
     * called again. This method is <code>synchronized</code> so that it can
     * notify the thread on standby.
     *
     * @param on <code>true</code> to set the changing of download status on,
     *           <code>false</code> to revert the status back to "Ready"
     */
    public synchronized void setLoadStatus(boolean on) {
	loading = on;
	if (on) {
	    if (!loadingThread.isAlive())
		loadingThread.start();
	    else
		notify();
	}
    }

    /**
     * Updates the download status as described in the class description.
     * When the thread is told that download is complete by calling
     * <code>setLoadStatus(false)</code>, this method enters the
     * <code>synchronized</code> part, setting the status text
     * back to "Ready" and starting to wait for another go.
     */
    public void run() {
	while (true) {
	    while (loading) {
		updateStatus();
		try {
		    Thread.sleep(500);
		} catch(InterruptedException e) {}
	    }
	    synchronized(this) {
		status.setText("Status: Setting");
		try {
		    Thread.sleep(1000);
		} catch (InterruptedException e) {}
		status.setText("Status: Ready");
		try {
		    wait();
		} catch(InterruptedException e) {}
	    }
	}
    }

}

