package toy;

import java.applet.*;
import java.awt.*;
import java.awt.event.*;

import toy.image.*;

/**
 * ToySplash is a very special component that it does practically everything for
 * the splash window or splash applet.  It just needs to be placed in the
 * correct window.
 *
 * @author Brian Tsang
 * @version 7.0
 */

public class ToySplash extends Component
                       implements Runnable, WindowListener
{
    /**
     * The version and build number of Visual X-TOY (for debugging purposes.
     */
    private static final String BUILD_STRING = "7.0.32";
    /**
     * The date when this program was last modified (for debugging purposes.
     */
    private static final String DATE_STRING  = "9/13/01";

    /**
     * The width of the splash image.
     */
    public static final int WIDTH           = 341;
    /**
     * The height of the splash image.
     */
    public static final int HEIGHT          = 256;

    /**
     * The ToyFrame that was launched.
     * @serial
     */
    private ToyFrame frame;

    /**
     * The ToyImageManager that will load the images.  This is static because
     * the browser might not launch different applets in different VMs.  Thus
     * we save time by having the images already loaded.
     */
    private static ToyImageManager imageManager;

    /**
     * The ToyParameterManager that will load the starting preferences and
     * example programs.  This is static because the browser might not launch
     * different applets in different VMs.  Thus we save time by having the
     * parameters and example programs already loaded.
     */
    private static ToyParameterManager parameterManager;

    /**
     * The current brightness of the "Click To Start" text.  Ranges from 0 to
     * 255.
     * @serial
     */
    private int colorCtr;

    /**
     * The splash applet (if this is an applet).
     * @serial
     */
    private Applet parentApplet;
    /**
     * The splash window (if this is an application).
     * @serial
     */
    private Window parentWindow;

    /**
     * The buffered screen.
     * @serial
     */
    private Image offscreenImage;
    /**
     * The graphics object attached to the buffered screen.
     * @serial
     */
    private Graphics offscreen;

    /**
     * The animation thread.
     * @serial
     */
    private Thread runner;

    /**
     * Constructs a new ToySplash object attached to a window.
     * @param newParentWindow the window that this ToySplash will be attached
     * to.
     */
    public ToySplash(Window newParentWindow)
    {
        System.out.println("Visual X-TOY");
        System.out.println("Originally Coded by Brian Tsang");
        System.out.println("Build " + BUILD_STRING + " (" + DATE_STRING + ")");

        parentWindow = newParentWindow;

        setBackground(new Color(64, 80, 96));

        //get the parameters and example programs
        if (parameterManager == null)
            parameterManager = new ToyParameterManager();

        //get the images
        if (imageManager == null)
            imageManager = new ToyImageManager(parentWindow);

        //Our animation thread
        runner = new Thread(this);
        runner.start();

        enableEvents(AWTEvent.MOUSE_EVENT_MASK);
    }

    /**
     * Constructs a new ToySplash object attached to a applet.
     * @param newParentApplet the applet that this ToySpash will be attached to.
     */
    public ToySplash(Applet newParentApplet)
    {
        System.out.println("Visual X-TOY");
        System.out.println("Originally Coded by Brian Tsang");
        System.out.println("Build " + BUILD_STRING + " (" + DATE_STRING + ")");

        parentApplet = newParentApplet;

        setBackground(new Color(64, 80, 96));

        //get the parameters and example programs
        if (parameterManager == null)
            parameterManager = new ToyParameterManager(parentApplet);

        //get the images, but don't bother if we already have an imageManger
        //it's static
        if (imageManager == null)
            imageManager = new ToyImageManager(parentApplet);

        //Our animation thread
        runner = new Thread(this);
        runner.start();

        enableEvents(AWTEvent.MOUSE_EVENT_MASK);
    }

    /**
     * Make sure the thread stops while we
     */
    public void finalize()
    {
        //toss out the thread
        runner = null;
    }

    /**
     * Implement the Runnable interface to animate the splash screen.
     */
    public void run()
    {
        Thread thisThread = Thread.currentThread();
        colorCtr = 255;   //"Click to Start" should start at black
        int direction = -7; //and should be getting brighter in steps of 7
        boolean cursorChanged = false;

        repaint();

        while (runner == thisThread && frame == null)
        {
            //Ccall the repaint function
            repaint();

            //increment the color
            colorCtr += direction;

            //don't let the color change if we're loading
            if (!(imageManager.isReady() && parameterManager.isReady()))
                colorCtr = 255;

            //Reverse the direction if the color reaches one of the RGB bounds
            if (colorCtr <= 10)
                direction = 7;

            if (colorCtr >= 245)
                direction = -7;

            //when our images are ready, we can set the cursor to the hand
            //(or launch the frame if we're an application)
            if (!cursorChanged && imageManager.isReady() && parameterManager.isReady())
            {
                if (parentWindow == null)
                {
                    cursorChanged = true;
                    setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
                }
                else
                    launchAttempt();
            }

            try
            {
                //pause for a number of milliseconds
                if (imageManager.isReady() && parameterManager.isReady())
                    //pause longer if the color is closer to white
                    Thread.sleep(50 + colorCtr * colorCtr / 500);
                else
                    //pause for only 100 milliseconds
                    Thread.sleep(100);
            }
            catch (Exception e)
            {
                //whatever
            }
        }

        runner = null;
        colorCtr = 255;
        repaint();
    }

    /**
     * Repaint has been simplified to speed the animation process.
     */
    public void repaint()
    {
        paint(getGraphics());
    }

    /**
     * Update has been simplified to speed the animation process.
     * @param g the graphics object on which to paint.
     */
    public void update(Graphics g)
    {
        paint(g);
    }

    /**
     * Paint the logo and text onto the buffer and then to g
     * @param g the graphics object on which to paint.
     */
    public void paint(Graphics g)
    {
        if (g == null)
            return;

        if (offscreen == null)
        {
            //initialize the g buffer
            offscreenImage = createImage(getBounds().width, getBounds().height);
            if (offscreenImage == null)
                return;

            offscreen = offscreenImage.getGraphics();
            offscreen.setFont(new Font("SansSerif", Font.PLAIN, 12));
        }

        offscreen.clearRect(0, 0, getBounds().width, getBounds().height);

        //put the logo on the buffer
        offscreen.drawImage(
            imageManager.getImage(ToyImageManager.SPLASH_BACKGROUND),
            0,
            0,
            this
            );

        //put the "Click to launch" on the buffer (we set the color back in start()
        offscreen.setColor(new Color(colorCtr, colorCtr, colorCtr));

        if (imageManager.isReady() && parameterManager.isReady())
        {
            if (frame != null)
                offscreen.drawString("Frame Launched", 244, 251);
            else {
                if (parentWindow == null)
                    offscreen.drawString("Click to Launch", 248, 251);
                else
                    offscreen.drawString("Launching...", 248, 251);
            }
        }
        else
            offscreen.drawString(
                "Loading... " + imageManager.getPercentDone() + "%",
                248,
                251
                );

        //put the buffer on the g
        g.drawImage(offscreenImage, 0, 0, this);
    }

    /**
     * Attempt to launch a frame if it does not already exist.  If a frame
     * already exists, just bring it to the front.
     */
    public void launchAttempt()
    {
        if (imageManager.isReady() && parameterManager.isReady())
        {
            //If there's already a frame, just bring it to the front
            if (frame != null && frame.isShowing())
            {
                frame.requestFocus();
                frame.toFront();
            }
            else {
                //If not, make a new one
                frame = new ToyFrame(
                    parentApplet,
                    parameterManager,
                    imageManager
                    );

                frame.addWindowListener(this);
                frame.show();

                if (parentWindow != null)
                    parentWindow.hide();
            }
        }
    }

    /**
     * Listen to Mouse Events for the MouseEvent.MOUSE_CLICKED event, attempt
     * to launch if that event is processed.
     * @see #launchAttempt()
     */
    public void processMouseEvent(MouseEvent e)
    {
        if (e.getID() == MouseEvent.MOUSE_CLICKED)
            launchAttempt();
    }

    /**
     * Implement the WindowListener interface to be notified when the ToyFrame
     * closes.  This particualr function does nothing.
     * @see #windowClosed(java.awt.event.WindowEvent)
     */
    public void windowOpened(WindowEvent e)
    {

    }

    /**
     * Implement the WindowListener interface to be notified when the ToyFrame
     * closes.  If the ToyFrame is closed, then restart the animation if this is
     * an applet, and if this is an application, terminate this process.
     */
    public void windowClosed(WindowEvent e)
    {
        if (parentWindow == null)
        {
            frame = null;
            if (runner == null)
            {
                runner = new Thread(this);
                runner.start();
            }
        }
        else
            System.exit(0);
    }

    /**
     * Implement the WindowListener interface to be notified when the ToyFrame
     * closes.  This particualr function does nothing.
     * @see #windowClosed(java.awt.event.WindowEvent)
     */
    public void windowClosing(WindowEvent e)
    {

    }

    /**
     * Implement the WindowListener interface to be notified when the ToyFrame
     * closes.  This particualr function does nothing.
     * @see #windowClosed(java.awt.event.WindowEvent)
     */
    public void windowActivated(WindowEvent e)
    {

    }

    /**
     * Implement the WindowListener interface to be notified when the ToyFrame
     * closes.  This particualr function does nothing.
     * @see #windowClosed(java.awt.event.WindowEvent)
     */
    public void windowDeactivated(WindowEvent e)
    {

    }

    /**
     * Implement the WindowListener interface to be notified when the ToyFrame
     * closes.  This particualr function does nothing.
     * @see #windowClosed(java.awt.event.WindowEvent)
     */
    public void windowIconified(WindowEvent e)
    {

    }

    /**
     * Implement the WindowListener interface to be notified when the ToyFrame
     * closes.  This particualr function does nothing.
     * @see #windowClosed(java.awt.event.WindowEvent)
     */
    public void windowDeiconified(WindowEvent e)
    {

    }

}
