import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.awt.geom.*;
import java.net.*;
import javax.swing.*;
import java.util.Enumeration;
import java.util.Hashtable;

import visual.dynamic.described.Sprite;
import visual.statik.sampled.ImageFactory;

/**
 * A Stage that uses full-screen exclusive mode
 *
 * @version 1.0
 * @author  Prof. David Bernstein, James Madison University
 *
 */
public class FullScreenStage implements ActionListener, KeyListener
{
    private boolean          shouldRestart;
    private BufferStrategy   bufferStrategy;
    private Graphics         g;
    private Hashtable        sprites;
    private int              frameTime, height, restartTime, time, width;
    private JFrame           frame;
    private Rectangle        bounds;
    private TexturePaint     texture;
    private Timer            timer;



    /**
     * Explicit Value Constructor
     *
     * @param frameTime    Milliseconds between exitFrame events
     */
    public FullScreenStage(int frameTime)
    {
       BufferedImage         image;
       GraphicsConfiguration graphicsConfiguration;
       GraphicsDevice        graphicsDevice;
       GraphicsEnvironment   graphicsEnvironment;
       Rectangle2D.Float     r;
       
       
       
       sprites        = new Hashtable();
       
       this.frameTime = frameTime;
       time           = -frameTime;
       shouldRestart  = false;
       restartTime    = -1;
       
       
       
       // Configure graphics-related objects
       graphicsEnvironment =
          GraphicsEnvironment.getLocalGraphicsEnvironment();
       graphicsDevice =
          graphicsEnvironment.getDefaultScreenDevice();
       graphicsConfiguration = graphicsDevice.getDefaultConfiguration();
       
       // Configure the JFrame
       frame = new JFrame(graphicsConfiguration);
       frame.setUndecorated(true);
       frame.setIgnoreRepaint(true);
       frame.setResizable(false);
       frame.addKeyListener(this);
       
       // Set full-screen exclusive mode (if supported)
       if (graphicsDevice.isFullScreenSupported())
          graphicsDevice.setFullScreenWindow(frame);
       
       
       // Configure the BufferStrategy
       frame.createBufferStrategy(2);
       bufferStrategy = frame.getBufferStrategy();
       
       
       // Get the rendering engine
       g = bufferStrategy.getDrawGraphics();
       
       // Get the bounds 
       bounds = frame.getBounds();
       
       image = ImageFactory.createBufferedImage("sky.jpeg");
       r = new Rectangle2D.Float(0.0f,0.0f,	
                                 (float)(image.getWidth()), 
                                 (float)(image.getHeight()));
       texture = new TexturePaint(image, r);
       
       // Create a Timer in a separate Thread
       timer = new Timer(frameTime, this);
    }




    /**
     * Part of being an ActionListener (for the Timer)
     *
     * @param ae   The ActionEvent to handle
     */
    public void actionPerformed(ActionEvent ae)
    {	
       if (ae.getSource() == timer)
       {
          time += frameTime;
          if ((shouldRestart) && (time >= restartTime)) time = 0;
          fireExitFrame();
          render();  // Instead of repaint()
       }
    }



    /**
     * Add a MouseMotionListener
     */
    public void addMouseMotionListener(MouseMotionListener mml)
    {
       frame.addMouseMotionListener(mml);
    }
    



    /**
     * Add a Sprite to the Stage
     *
     * @param s   The Sprite to add
     */
    public void addSprite(Sprite s)
    {
	sprites.put(s, s);
    }



    /**
     * Draw (i.e., render) the Stage
     *
     * @param g  The Graphics context to paint on
     */
    public void draw(Graphics g)
    {
	Enumeration e;
	Graphics2D  g2;
	Sprite      s;

	g2 = (Graphics2D)g;

	// Paint the background
	g2.setPaint(texture);
	g2.fillRect(0,0,bounds.width,bounds.height);

	// Have each Sprite render itself
	//
	e = sprites.elements();

	while (e.hasMoreElements()) {

	    s = (Sprite)e.nextElement();
	    s.render(g2);
	}
    }




    /**
     * Fire an exitFrame event to every Sprite
     */
    public void fireExitFrame()
    {
	Enumeration       e;
	Sprite s;


	e = sprites.elements();

	while (e.hasMoreElements()) {

	    s = (Sprite)e.nextElement();
	    s.exitFrame(time);
	}
    }



    /**
     * Handle key Pressedevents (required by KeyListener)
     *
     * @param evt   The KeyEvent
     */
    public void keyPressed(KeyEvent evt)
    {
	if (evt.getKeyCode() == KeyEvent.VK_Q) stop(); 
    }


    /**
     * Handle keyReleased events (required by KeyListener)
     *
     * @param evt   The KeyEvent
     */
    public void keyReleased(KeyEvent evt)
    {
    }


    /**
     * Handle keyTyped events (required by KeyListener)
     *
     * @param evt   The KeyEvent
     */
    public void keyTyped(KeyEvent evt)
    {
    }



    /**
     * Render the stage (in lieu of repaint)
     */
    public void render()
    {
	draw(g);
        bufferStrategy.show();
    }



    /**
     * Remove a Sprite from the Stage
     *
     * @param s   The Sprite to add
     */
    public void removeSprite(Sprite s)
    {
	sprites.remove(s);
    }



    /**
     * Start the Stage
     */
    public void start()
    {
	timer.start();
    }
    
    

    /**
     * Stop the Stage
     */
    public void stop()
    {
	timer.stop();
        g.dispose();
	System.exit(0);
    }
}
