import com.sun.j3d.utils.image.*;
import java.awt.print.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.io.*;
import java.net.*;
import javax.media.j3d.*;
import javax.vecmath.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import com.sun.image.codec.jpeg.*;
import javax.swing.*;
import java.util.*;

public class CaptureCanvas3D extends Canvas3D{
    boolean useIOThread = true;
    
    Vector captureRequests = new Vector();
    private int captureCount = 0;
    boolean capturing = false;
    File destFolder;
    String filePrefix;
    IOThread ioThread = new IOThread();
    
    public CaptureCanvas3D(GraphicsConfiguration gc) {
        super( gc );
        ioThread.start();
    }
    public void startCapturing( File destFolder, String filePrefix ){
        this.destFolder = destFolder;
        this.filePrefix = filePrefix;
        capturing = true;
    }
    public void stopCapturing(){
        capturing = false;
    }
    public void captureOne( final File f ){
        ImageHandler request = new ImageHandler(){
            public void handleImage( BufferedImage img ){
                System.out.println( "Writing JPEG to " + f.getAbsolutePath() );
                if( useIOThread ){
                    ioThread.requestWriteToFile( img, f );
                }else{
                    writeToFile( img, f );
                }
            }
        };
        synchronized( captureRequests ){
            captureRequests.add( request );
        }
        repaint();
    }
    public void writeToFile( BufferedImage image, File file ){
        try{
            FileOutputStream out = new FileOutputStream( file );
            JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder( out );
            JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam( image );
            param.setQuality( 1.0f, false );
            encoder.setJPEGEncodeParam( param );
            encoder.encode( image );
            out.close();
        }catch ( IOException e ){
            System.out.println("I/O exception!");
        }
    }    
    BufferedImage createBufferedImage(){
        GraphicsContext3D  ctx = getGraphicsContext3D();
        int w = getWidth();
        int h = getHeight();
        BufferedImage bi = new BufferedImage( w, h, BufferedImage.TYPE_INT_RGB );
        ImageComponent2D im = new ImageComponent2D( ImageComponent.FORMAT_RGB, bi );
        Raster ras = new Raster( new Point3f( -1.0f, -1.0f, -1.0f ), Raster.RASTER_COLOR, 0, 0, w, h, im, null );
        ctx.readRaster( ras );
        return ras.getImage().getImage();
    }
    public void postSwap(){
        if( capturing ){
            captureOne( new File( destFolder, filePrefix + captureCount + ".jpg" ) );
            captureCount++;
        }
        ImageHandler[] requests;
        synchronized( captureRequests ){
            requests = (ImageHandler[])captureRequests.toArray( new ImageHandler[captureRequests.size()] );
            captureRequests.clear();
        }
        if( requests.length > 0 ){
            BufferedImage img = createBufferedImage();
            for( int i = 0; i < requests.length; ++i ){
                requests[i].handleImage( img );
            }
        }
    }
    interface ImageHandler{
        public void handleImage( BufferedImage image );
    }
    class IOThread extends Thread{
        private Vector images = new Vector();
        private Vector files = new Vector();
        private Object lock = new Object();
        
        public void run(){
            BufferedImage[] bis;
            File[] fs;
            while( true ){
                synchronized( lock ){
                    while( images.size() == 0 ){
                        try{
                            lock.wait();
                        }catch( InterruptedException e ){
                        }
                    }
                    bis = (BufferedImage[])images.toArray( new BufferedImage[images.size()] );
                    fs = (File[])files.toArray( new File[files.size()] );
                }
                for( int i = 0; i < bis.length; ++i ){
                    writeToFile( bis[i], fs[i] );
                }
            }
        }
        public void requestWriteToFile( BufferedImage image, File file ){
            synchronized( lock ){
                images.add( image );
                files.add( file );
                lock.notify();
            }
        }
    }
}
