/*
 * @(#)CaptureCanvas3d.java
 */

import java.awt.*;
import java.awt.image.BufferedImage;
import javax.media.j3d.*;


/**
 * Extends the Canvas3D by providing the functionality to capture the screen
 * contents and dump the contents to disc as a jpeg. <br><br>
 *
 * The screen is captured by overloading the postSwap() method. When a flag is
 * set, the contents of the frame buffer are read and enoded into jpeg.
 *
 * @author  Sean Sylvis
 */
public class CaptureCanvas3d extends Canvas3D
{
    // Members
    protected boolean mCaptureScreen;
    protected int mFrameNum;

    protected int mWidth;
    protected int mHeight;
    protected Raster mRaster;

    
    /**
     * Creates a default CaptureCanvas3d object.
     */
    public CaptureCanvas3d()
    {
        // Set graphics configuration
        super(createGraphicsConfig());

        mCaptureScreen = false;
        mFrameNum = 0;

        // Set raster
        mRaster = new Raster();
        setRaster(Viewer3d.kDefaultWidth,
                  Viewer3d.kDefaultHeight);
    }


    /**
     * Captures one canvas screenshot and writes a jpeg file to disc.
     */
    public void captureCanvas()
    {
        mCaptureScreen = true;
        super.repaint();
    }


    /**
     * Called by the Java 3D rendering loop after clearing the canvas and 
     * before any rendering has been done for this frame.
     */
    public void preRender()
    {
        super.getGraphicsContext3D().clear();
    }


    /**
     * Called by the Java3D rendering engine after writing to the frame buffer.
     */
    public void postSwap()
    {
        // If either flag is true, capture canvas
        if (!mCaptureScreen)
            return;

        validateDimension();

        // Dump frame buffer into raster
        GraphicsContext3D vContext = super.getGraphicsContext3D();
        vContext.readRaster(mRaster);

	    // Get buffered image
	    BufferedImage vBufferedImage = mRaster.getImage().getImage();

        String vFilename = "screen" + mFrameNum;
        writeImage(vFilename, vBufferedImage);

	    mCaptureScreen = false;

        mFrameNum++;
    }
        

    /**
     * Writes the parameter image to disc using the parameter filename. <br><br>
     *
     * The image is encoder differently depending upon if the image is going to
     * be used as a movie frame or not. If the parameter flag is set, the image
     * is encoded into a png image file. Otherwise, a jpeg file is generated.
     *
     * @param pFilename  the file name
     * @param pImage  the image
     */
    public void writeImage(String pFilename,
                           BufferedImage pImage)
    {
        try
        {
            FileOutputStream vFile = 
                new FileOutputStream(pFilename + ".jpg");

            // Set parameters
            JPEGEncodeParam vParam = 
                JPEGCodec.getDefaultJPEGEncodeParam(pImage);
            vParam.setQuality(1.0f, false);

            JPEGImageEncoder vEncoder = 
                JPEGCodec.createJPEGEncoder(vFile, vParam);
            vEncoder.encode(pImage);
            
            vFile.close();
        }

        catch (Exception e)
        {
            e.printStackTrace();
            
            System.out.println("Could not write screen");
        }
    }


    /**
     * Creates the raster object used to store the frame buffer.
     *
     * @param pWidth  the window width
     * @param pHeight  the window height
     */
    protected void setRaster(int pWidth, int pHeight)
    {
        // Set width and height
        mWidth = pWidth;
        mHeight = pHeight;

        BufferedImage vBufferedImage = 
            new BufferedImage(mWidth, mHeight, BufferedImage.TYPE_INT_RGB);

        // Create image component
        ImageComponent2D vImage = new ImageComponent2D(ImageComponent.FORMAT_RGB,
                                                       vBufferedImage);

        // Set raster image
        mRaster.setSize(mWidth, mHeight);
        mRaster.setImage(vImage);
    }


    /**
     * Checks to see if the application window has changed size. If so, then a
     * new raster object is created for capturing a screenshot.
     */
    protected void validateDimension()
    {
        int vWidth = super.getWidth();
        int vHeight = super.getHeight();
        
        if (vWidth != mWidth || vHeight != mHeight)
            setRaster(vWidth, vHeight);
    }
        

    /**
     * Creates a graphics template that Canvas3D uses to create a canvas and
     * returns the configuration.
     */
    public static GraphicsConfiguration createGraphicsConfig()
    {
        // Create the template
        GraphicsConfigTemplate3D vGraphicsTemplate = new GraphicsConfigTemplate3D();
        
        vGraphicsTemplate.setStereo(GraphicsConfigTemplate3D.PREFERRED);
        vGraphicsTemplate.setSceneAntialiasing(GraphicsConfigTemplate3D.PREFERRED);

        // Then find a configuration that fits
        GraphicsDevice vDevice = 
            GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();

        return vDevice.getBestConfiguration(vGraphicsTemplate);
    }

}

