/*
 * QuickTime for Java SDK Sample Code

   Usage subject to restrictions in SDK License Agreement
 * Copyright: © 1996-1999 Apple Computer, Inc.

 */
import java.awt.*;
import java.awt.event.*;
import java.io.IOException;
import java.io.File;

import quicktime.*;
import quicktime.qd.*;
import quicktime.std.*;
import quicktime.io.*;
import quicktime.std.image.*;
import quicktime.util.*;
import quicktime.std.movies.*;
import quicktime.std.movies.media.*;
import quicktime.app.actions.*;
import quicktime.app.anim.*;
import quicktime.app.audio.*;
import quicktime.app.display.*;
import quicktime.app.image.*;
import quicktime.app.QTFactory;

import ds.actions.*;

public final class DraggingSprites extends Frame implements StdQTConstants {
	static final int 
				kWidth = 400,
				kHeight = 300;
	
	// MIDI note numbers for drum kit instrument
	static final int
		kSnareDrum = 40,
		kClosedHiHat = 44,
		kOpenHiHat = 46,
		kCrashCymbal = 49,
		kRideCymbal = 51;

	public static void main (String[] args) {
		try {
			QTSession.open();
			new DraggingSprites().show();
		} catch (Exception e) {
			e.printStackTrace();
			QTSession.close();
		}
	}
	
//____________________ INSTANCE VARIABLES	
	private Compositor myCompositor;
	private QTCanvas canv;
	
	private boolean performance = true;
	
//____________________ INSTANCE METHODS	
	DraggingSprites () throws QTException, IOException {
		super ("Sprite App");

		canv = new QTCanvas (QTCanvas.kInitialSize, 0.5F, 0.5F);
		add ("Center", canv);
		Dimension size = new Dimension(kWidth, kHeight);
		QDGraphics gw = new QDGraphics (new QDRect(size));
		myCompositor = new Compositor (gw, QDColor.red, 20, 1);	
		
		File matchFile = QTFactory.findAbsolutePath ("Ships.mov");
		OpenMovieFile movieFile = OpenMovieFile.asRead(new QTFile(matchFile));
		Movie mov = Movie.fromFile (movieFile);
		Track shipTrack = mov.getIndTrack (1);
		ImageDataSequence is = ImageUtil.createSequence (shipTrack);
		
		createSprites (myCompositor, ImageUtil.makeTransparent (is, QDColor.blue), size);
		myCompositor.getTimer().setRate (2);

		canv.setClient (myCompositor, true);

		addWindowListener(new WindowAdapter () {
			public void windowClosing (WindowEvent e) {
				QTSession.close();
				dispose();
			}

			public void windowClosed (WindowEvent e) {
				System.exit(0);
			}
		});
		pack();
	}
				
	void createSprites (Compositor myCompositor, ImageDataSequence seq, Dimension size) throws QTException, IOException {
		// This is the instrument that will be used to play all of the notes
		// of the different actions -> we use a polyphony of 10
		// which is a reasonable estimate of the maximum number of notes
		// that will be sounding at a given time
		final NoteChannelControl nc = new NoteChannelControl (StdQTConstants.kFirstDrumkit, 10);

		Matrix matrix = new Matrix();
		matrix.setTx(size.width / 4);
		matrix.setTy(size.height / 2);
		TwoDSprite s1 = new TwoDSprite(seq, matrix, true, 1);
		myCompositor.addMember (s1);
		
        // paint a sprite and set a hot spot region for it
		// the green region is the hot spot
		// we use a 16 bit BigEndian pixel format as we have no blending on this sprite
		matrix = new Matrix();
		matrix.setTx(20);
		matrix.setTy(20);
		QDRect r40 = new QDRect (40, 40);
		QDRect r8 = new QDRect (1, 1, 8, 8);
		QDGraphics y = new QDGraphics (QDConstants.k16BE555PixelFormat, r40);
		y.setBackColor (QDColor.yellow);
		y.eraseRect (null);
		y.setForeColor (QDColor.green);
		y.paintRect (r8);
		EncodedImage ei = RawEncodedImage.fromPixMap (y.getPixMap());
		ImageDescription id = new ImageDescription (y.getPixMap());
		ImageDataSequence idsy = new ImageDataSequence (id);
		idsy.addMember (ei);
		if ((QTSession.isCurrentOS(QTSession.kWin32) && QTSession.getQTMajorVersion() == 3) == false)	//doesn't work on QT3.0.2 on Win
			idsy = ImageUtil.makeTransparent (idsy, QDColor.black, new QDGraphics (QDConstants.k16BE555PixelFormat, r40), new Region (r8));
		TwoDSprite s2 = new TwoDSprite(idsy, matrix, true, 10);
		myCompositor.addMember (s2);

		matrix = new Matrix();
		matrix.setTx(size.width / 2);
		matrix.setTy(size.height / 2);
		TwoDSprite s3 = new TwoDSprite(seq, 15, matrix, true, 10);
		myCompositor.addMember (s3);
		ImageSequencer is3 = new ImageSequencer (seq);
		is3.setCurrentFrame (15);	//keep in sync with sprite's current image
		is3.setLooping (ImageSequencer.kLoopForwards);

		SimpleActionList al = new SimpleActionList();
		ImageSequencer is = new ImageSequencer (seq);
		is.setLooping (ImageSequencer.kLoopForwards);
		al.addMember (new NextImageAction (15, 1, is, s1));
		BounceAction b1 = new BounceAction (20, 1, myCompositor, s1, 1, 2);
		b1.setActionable (new Actionable () {
			public void trigger () {
				try {
					nc.playNoteFor (kCrashCymbal, 127, 20);
				} catch (QTException e) {
					e.printStackTrace();
				}
			}
		});
		al.addMember (b1);
					// 360 degrees at a scale of 20
		al.addMember (new RotateAction (360, 20, myCompositor, s3));
					// 360/ (1/2)number of images changes at a scale of 20
		al.addMember (new NextImageAction ((360 / seq.size() / 2), 20, is3, s3));
		al.addMember (new BounceAction (1, 10, myCompositor, s3, 1, 1)); // a slow drift
		myCompositor.addController(al);

	//all mouse downs with NO modifiers pressed will drag all sprites
		Dragger dragger = new Dragger (MouseResponder.kNoModifiersMask);
		dragger.setActionable (new Actionable () {
			public void trigger () {
				try {
					nc.playNoteFor (kRideCymbal, 127, 80);
				} catch (QTException e) {
					e.printStackTrace();
				}
			}
		});
		SWController ct = new SWController (dragger, true);
		myCompositor.addController (ct);
		
	//only shift key down when mouse pressed will allow sprite number 2 & 3 to be scaled when dragged
		MouseController controller = new SWController (new Scaler (400, InputEvent.SHIFT_MASK), false);
		controller.addMember (s3);
		controller.addMember (s2);
		myCompositor.addController (controller);

	//only option/alt key will allow sprite number 3 to be skewed when dragged
		controller = new SWController (new Skewer (200, 200, InputEvent.ALT_MASK), false);
		controller.addMember (s2);
		myCompositor.addController (controller);
		
		// this responder responds to rollover type events on sprites when NO modifiers are pressed
		final GenericResponder gr = GenericResponder.asRolloverListener (MouseResponder.kNoModifiersMask, MouseResponder.kModifiersExactMatch);
		// add a listener to play notes when rolled over 
		// add sets the graphics mode to blend using lightGray
		gr.addQTMouseMotionListener (new QTMouseMotionAdapter () {
			GenericResponder g = gr;
			GraphicsMode savedGM;
			GraphicsMode setGM = new GraphicsMode (QDConstants.blend, QDColor.lightGray);
			
			public void mouseDragged (MouseEvent event) {
				try {
					nc.playNoteFor (kClosedHiHat, 127, 80);
				} catch (QTException e) {
					e.printStackTrace();
				}
			}

			public void mouseMoved (MouseEvent event) {
				try {
					nc.playNoteFor (kClosedHiHat, 127, 80);
				} catch (QTException e) {
					e.printStackTrace();
				}
			}

			public void mouseEnteredTarget (MouseEvent event) {
				try {
					if (g.getTarget() instanceof TwoDSprite) {	// just for sanity
						TwoDSprite sprite = (TwoDSprite)g.getTarget();
						savedGM = sprite.getGraphicsMode();
						sprite.setGraphicsMode (setGM);
					}
					nc.playNoteFor (kSnareDrum, 127, 80);
				} catch (QTException e) {
					throw new QTRuntimeException (e);
				}
			}

			public void mouseExitedTarget (MouseEvent event) {
				try {
					if (g.getTarget() instanceof TwoDSprite) {	// just for sanity
						TwoDSprite sprite = (TwoDSprite)g.getTarget();
						sprite.setGraphicsMode (savedGM);
					}
					nc.playNoteFor (kOpenHiHat, 127, 80);
				} catch (QTException e) {
					throw new QTRuntimeException (e);
				}
			}
		});
		SWController ctr = new SWController (gr, true);
		myCompositor.addController (ctr);
	}
}
