/**
 *  Licensed under GPL. For more information, see
 *    http://jaxodraw.sourceforge.net/license.html
 *  or the LICENSE file in the jaxodraw distribution.
 */
package net.sf.jaxodraw.gui.launch;

import java.awt.AWTEvent;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dialog;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.Toolkit;
import java.awt.event.AWTEventListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;

import javax.swing.Timer;


/** The splash window displayed on startup.
 * @since 2.0
 */
public class JaxoSplashWindow extends Component implements AWTEventListener {
    private static final long serialVersionUID = 7526471155622776147L;
    private final Object lock = new Object(); // Component lock is used by AWT
    private Frame frame; // null if disposed
    private final Image image;
    private boolean painted;
    private final String windowTitle;

    /**
     * Constructor.
     *
     * @param title The title of the SplashWindow frame.
     */
    public JaxoSplashWindow(final String title) {
        super();
        this.windowTitle = title;
        setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));

        // avoid JaxoUtils to reduce class-loading at this point
        image = Toolkit.getDefaultToolkit().getImage(
            Thread.currentThread().getContextClassLoader().getResource(
                "resources/icons/start.png"));
    }

    /** Load/show the splash screen (unless image loading failed,
     * then the window is not shown).
     */
    public void start() {
        synchronized (lock) {
            if (frame != null) {
                return;
            }

            final MediaTracker t = new MediaTracker(this);

            t.addImage(image, 0);

            try {
                t.waitForAll();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }

            final int width = image.getWidth(this);
            final int height = image.getHeight(this);

            if ((width > 0) && (height > 0)) {
                frame = new Frame(windowTitle);
                frame.setUndecorated(true);
                // IconImage may yet be visible in the task bar
                // avoid JaxoUtils to reduce class-loading at this point
                frame.setIconImage(Toolkit.getDefaultToolkit().getImage(
                        Thread.currentThread().getContextClassLoader().getResource(
                        "resources/icons/frame.png")));

                frame.setAlwaysOnTop(true);

                frame.add(this);

                frame.setSize(width, height);
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);

                frame.getToolkit().addAWTEventListener(this,
                    AWTEvent.COMPONENT_EVENT_MASK);
            }
        }
    }

    /**
     * Dispose the splash screen in 'remaining >= 0' milliseconds.
     * @param remaining The time to wait.
     */
    public final void dispose(final int remaining) {
        synchronized (lock) {
            if (frame != null) {
                if (remaining > 0) {
                    final Frame f = frame;
                    final Timer t =
                        new Timer(remaining,
                            new ActionListener() {
                                public void actionPerformed(final ActionEvent e) {
                                    f.dispose();
                                }
                            });

                    t.setRepeats(false);
                    t.start();
                } else {
                    frame.dispose();
                }

                frame.getToolkit().removeAWTEventListener(this);
                frame = null;
                painted = false;

                lock.notifyAll();
            }
        }
    }

    /** Move the splash Window to front. */
    public void moveToFront() {
        synchronized (lock) {
            if (frame != null) {
                frame.toFront();
            }
        }
    }

    /**
     * Paint the splash image.
     *
     * @param g The graphics contect to paint to.
     */
    @Override
    public final void paint(final Graphics g) {
        g.drawImage(image, 0, 0, null);

        synchronized (lock) {
            painted = true;
            lock.notifyAll();
        }
    }

    /**
     * Wait for 'remaining > 0' milliseconds until the image has been
     * painted or the splash window disposed. You cannot wait forever -
     * there is no guarantee that the frame, even if visible, will ever
     * be painted.
     * @param wait The time to wait.
     * @throws java.lang.InterruptedException InterruptedException
     */
    public void waitUntilPainted(final long wait) throws InterruptedException {
        synchronized (lock) {
            if (painted || (frame == null)) {
                return;
            }

            long remaining = wait;
            long old = System.currentTimeMillis();

            while (remaining > 0) {
                lock.wait(remaining);

                if (painted || (frame == null)) {
                    return;
                }

                final long now = System.currentTimeMillis();

                remaining -= (now - old);

                old = now;
            }
        }
    }

    /**
     * Used internally.
     * @param e An AWT event.
     */
    public void eventDispatched(final AWTEvent e) {
        // Use Dialog, not Window; showing the main Frame
        // is normal, Dialogs are exceptional.
        if (e.getSource() instanceof Dialog
                && (e.getID() == ComponentEvent.COMPONENT_SHOWN)) {
            synchronized (lock) {
                if (frame != null) {
                    frame.setAlwaysOnTop(false);
                    ((Dialog) e.getSource()).toFront();
                }
            }
        }
    }
}
