import ij.*;
import ij.plugin.PlugIn;
import ij.gui.*;
import ij.process.*;
import ij.util.*;
import ij.text.*;

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;

import quicktime.qd.*;
import quicktime.*;
import quicktime.util.*;


import quicktime.io.*;
import quicktime.std.StdQTConstants;
import quicktime.std.sg.*;

import quicktime.app.sg.SGDrawer;
import quicktime.std.movies.*;
import quicktime.std.movies.media.UserData;
import quicktime.app.display.QTCanvas;
import quicktime.app.image.QTImageProducer;
import quicktime.app.image.ImagePresenter;

/**
Captures a single video frame using QuickTime for Java. This plugin is not likely to run on Windows due
to lack of QuickTime video capture drivers.

To compile this plugin on Mac OS X, you need to add /System/Library/Java/Extensions/QTJava.zip to
the classpath as described in the "Adding a JAR File" section of the OS X installation notes at

    http://rsb.info.nih.gov/ij/docs/install/osx.html

To compile or run this plugin on Mac OS 9 you may need to first install QuickTime for Java by 
selecting "QuickTIme for Java" in the "Custom" install option of the QuickTime installer. Also on the 
Mac OS 9, you may need to allocate more memory to ImageJ to avoid memFullErr exceptions.
*/

public class QT_Capture extends ImagePlus implements PlugIn {

    static boolean grabGrayscale = false;
    boolean error;
    boolean grab;

    public void run(String arg) {
        String options = Macro.getOptions();
        if (options!=null && options.indexOf("grab")!=-1)
            arg = "grab";
        grab = arg.equals("grab");
        try {
            Class qts = Class.forName("quicktime.QTSession");
        } catch (Exception e) {
            IJ.error("Video capture requires QuickTime for Java, available\n"
                +"as a custom install with QuickTime 4.0 or later.");
            return;
        }
        SGCapture sg = new SGCapture("Live Video", this);
        sg.pack();
        sg.setVisible(true);
        sg.toFront();
        IJ.register(QT_Capture.class);
     }

    void closing(Image img) {
        error = (img==null);
        if (!error) {
            setImage(img);
            changes = true;
            setTitle("Captured Video");
            if (grabGrayscale) {
                ImageConverter ic = new ImageConverter(this);
                ic.convertToGray8();
            }
        }
    }

    void closed() {
        if (!error) show();
    }

}

class SGCapture extends Frame implements WindowListener, ActionListener, KeyListener, StdQTConstants, Errors {  

    private int kWidth;
    private int kHeight;
    
    private QTCanvas        myQTCanvas;
    private SGVideoChannel  mVideo;
    private SGDrawer        mDrawable;
    private SequenceGrabber mGrabber;
    private QTFile          mFile;
    private Movie           mMovie;
    private QT_Capture      thePlugIn;

    private Button grabButton, settingsButton, optionsButton, cancelButton;

    private static UserData settings;

    SGCapture(String title, QT_Capture thePlugIn) {
        super(title);
        this.thePlugIn = thePlugIn;
        try {       
            QTSession.open();       
            mGrabber = new SequenceGrabber();
            mVideo = new SGVideoChannel(mGrabber);
            QDRect bounds = mVideo.getSrcVideoBounds();
            if (IJ.altKeyDown()) {
                kWidth = bounds.getWidth()/2; kHeight = bounds.getHeight()/2;
                myQTCanvas = new QTCanvas(QTCanvas.kPerformanceResize, 0.5f, 0.5f);
            } else {
                kWidth = bounds.getWidth(); kHeight = bounds.getHeight();
                myQTCanvas = new QTCanvas(QTCanvas.kPerformanceResize, 1f, 1f);
            }
            Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
            if (kHeight>(screen.height-40)) { // iSight camera claims to 1600x1200!
                kWidth =640;
                kHeight = 480;
            }
            myQTCanvas.setMaximumSize (new Dimension (bounds.getWidth(), bounds.getHeight()));
            myQTCanvas.setPreferredSize (new Dimension (kWidth, kHeight));
            myQTCanvas.setSize (new Dimension (kWidth, kHeight));
            myQTCanvas.addKeyListener(this);

            setLayout (new BorderLayout());
            add ("Center", myQTCanvas); 

            Panel buttons = new Panel();
            //buttons.setLayout(new FlowLayout(FlowLayout.RIGHT));
            grabButton = new Button(" Grab");
            buttons.add(grabButton);
            grabButton.addActionListener(this);
            settingsButton = new Button(" QT Settings ");
            buttons.add(settingsButton);
            settingsButton.addActionListener(this);
            optionsButton = new Button(" Options ");
            buttons.add(optionsButton);
            optionsButton.addActionListener(this);
            cancelButton = new Button(" Cancel ");
            buttons.add(cancelButton);
            cancelButton.addActionListener(this);
            add("South", buttons);
            addWindowListener(this);
            addKeyListener(this);
        } catch (Exception ee) {
            printStackTrace(ee);
            QTSession.close();
        }
        IJ.register(SGCapture.class);
    }
    
    public void windowOpened(WindowEvent e) {
        try{
            
            //if (settings!=null)
            //  mVideo.setSettings(settings);
            mVideo.setBounds (new QDRect(kWidth, kHeight));
            mVideo.setUsage (seqGrabPreview); // seqGrabRecord
    
            mDrawable = new SGDrawer(mVideo);           
            myQTCanvas.setClient(mDrawable,true);           

            //mGrabber.setDataOutput (mFile, seqGrabPreview | seqGrabRecord | seqGrabPlayDuringRecord);
            mGrabber.prepare(true,true);
            mGrabber.startPreview();
        } catch (Exception ee) {
            printStackTrace(ee);
            QTSession.close();
        }
        if (thePlugIn.grab)
            grabFrame();
    }

    void shutDown() {
        myQTCanvas.removeClient();
        QTSession.close();
        setVisible(false);
        dispose();
    }

    void printStackTrace(Exception e) {
        CharArrayWriter caw = new CharArrayWriter();
        PrintWriter pw = new PrintWriter(caw);
        e.printStackTrace(pw);
        String s = caw.toString();
        if (IJ.isMacintosh())
            s = Tools.fixNewLines(s);
        new TextWindow("Exception", s, 400, 300);
        Macro.abort();
    }

    void grabFrame () {
        ImageProducer producer = null;
        Pict pict = null;
        ImagePresenter ip = null;
        try {
            mGrabber.pause(seqGrabPause);
            pict = mGrabber.grabPict(new QDRect(kWidth, kHeight), 0, grabPictOffScreen);
            ip = ImagePresenter.fromPict(pict);
            QDRect rect = ip.getDisplayBounds();
            Dimension d = new Dimension(rect.getWidth(), rect.getHeight());
            producer = new QTImageProducer(ip, d);
            //producer = new QTImageProducer(mDrawable, new Dimension(kWidth, kHeight));
            Image img = createImage(producer);
            thePlugIn.closing(img);
        } catch (Exception ee) {
            printStackTrace(ee);
            thePlugIn.closing(null);
            shutDown();
        }
        shutDown();
    }

    public void windowClosing (WindowEvent e) {
        grabFrame();
    }

    public void windowClosed (WindowEvent e) { 
        thePlugIn.closed();
    }
    
    public void actionPerformed(ActionEvent e) {
        if (e.getSource()==grabButton)
            grabFrame();
        else if (e.getSource()==settingsButton) {
            try {
                mVideo.settingsDialog();
                //settings = mVideo.getSettings();
            } catch (Exception ee) {
                //printStackTrace(ee);
            }
        } else if (e.getSource()==optionsButton)
            showOptionsDialog();
        else if (e.getSource()==cancelButton) {
            shutDown();
            thePlugIn.closing(null);
        }
    }

    public void keyPressed(KeyEvent e) {
        grabFrame();
    }

    void showOptionsDialog() {
        GenericDialog gd = new GenericDialog("Options", this);
        gd.addCheckbox("Capture 8-bit Grayscale", thePlugIn.grabGrayscale);
        gd.showDialog();
        if (gd.wasCanceled())
            return;
        thePlugIn.grabGrayscale = gd.getNextBoolean();
    }

    public void keyReleased(KeyEvent e) {}
    public void keyTyped(KeyEvent e) {}
    public void windowIconified (WindowEvent e) {}
    public void windowDeiconified (WindowEvent e) {}
    public void windowActivated (WindowEvent e) {}
    public void windowDeactivated (WindowEvent e) {}
}
