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. To compile or run this plugin you
must first install QuickTime for Java by selecting "QuickTIme for Java" in the "Custom"
install option of the QuickTime installer. Note: 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;

	public void run(String arg) {
		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();		
			if (IJ.altKeyDown()) {
				kWidth = 320; kHeight = 240;
				myQTCanvas = new QTCanvas(QTCanvas.kPerformanceResize, 0.5f, 0.5f);
			} else {
				kWidth = 640; kHeight = 480;
				myQTCanvas = new QTCanvas(QTCanvas.kPerformanceResize, 1f, 1f);
			}
			myQTCanvas.setMaximumSize (new Dimension (640, 480));
			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{
			
			mGrabber = new SequenceGrabber();
			mVideo = new SGVideoChannel(mGrabber);
			//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();
		}
	}

	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);
	}

	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) {}
}
