// Copyright (c) 1996 Stephen B Kinzler.  All rights reserved.
//
// Redistribution and use, with or without modification, are permitted
// provided that the following conditions are met:
// 1. Redistributions must retain the above copyright notice, this list of
//    conditions and the following disclaimer.
// 2. All advertising materials mentioning features or use of this software
//    must display the following acknowledgement:
//     This product includes software developed by the Stephen B Kinzler.
// 3. The name Stephen B Kinzler may not be used to endorse or promote
//    products derived from this software without specific prior written
//    permission.
//
// THIS SOFTWARE IS PROVIDED BY STEPHEN B KINZLER ``AS IS'' AND ANY EXPRESS
// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED.  IN NO EVENT SHALL STEPHEN B KINZLER BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
// IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

import java.applet.*;
import java.awt.*;
import java.net.*;

///////////////////////////////////////////////////////////////////////////////
// ClipControl // Declaration /////////////////////////////////////////////////

public class ClipControl extends Applet implements Runnable {

	int width  = 260, height = 40;
	String url = "http://www.cs.indiana.edu/hyplan/kinzler/fun/"
		   + "shr_sounds/index.html";

	public String getAppletInfo() {
		return
"ClipControl - a AudioClip control applet with play/loop/stop/load buttons\n"
+ "Steve Kinzler, kinzler@cs.indiana.edu, May 96\n" + url
+ "\nVersion 1.1.0";
	}

	String	  clipname;
	URL	  clipurl;
	boolean	  backload = false, backplay = false;

	AudioClip clip = null;
	Button	  play = null, loop = null, stop = null;
	Checkbox  load = null;
	Event	  pend = null;
	Thread	  thrd = null;
	boolean	  loading = false;

	String pinfo[][] = {
	    {"clip",      "URL",     "audio clip"},
	    {"bgcolor",   "color",   "background color"},
	    {"fgcolor",   "color",   "foreground color"},
	    {"playlabel", "string",  "play button label [Play]"},
	    {"looplabel", "string",  "loop button label [Loop]"},
	    {"stoplabel", "string",  "stop button label [Stop]"},
	    {"loadlabel", "string",  "load button label [Load]"},
	    {"noload",    "boolean", "don't show load button [false]"},
	    {"preload",   "boolean", "load clip at start [false]"},
	    {"preplay",   "boolean", "play clip at start [false]"},
	    {"preloop",   "boolean", "loop clip at start [false]"},
	    {"backload",  "boolean", "keep loading on applet stop [false]"},
	    {"backplay",  "boolean", "keep playing on applet stop [false]"}
	};

	public String[][] getParameterInfo() {
		return pinfo;
	}

// ClipControl // Initialization //////////////////////////////////////////////

	public synchronized void init() {
		Dimension d = size();
		if (d.width  > 0) width  = d.width;
		if (d.height > 0) height = d.height;
		resize(width, height);

		String s = getParameter("BGCOLOR");
		if (s != null && s.startsWith("#")) {
			int i = Integer.parseInt(s.substring(1), 16);
			setBackground(new Color(i));
		}
		s = getParameter("FGCOLOR");
		if (s != null && s.startsWith("#")) {
			int i = Integer.parseInt(s.substring(1), 16);
			setForeground(new Color(i));
		}

		s = clipname = getParameter("CLIP");
		try { clipurl = (s != null) ? new URL(getDocumentBase(), s)
					    : null; }
		catch (MalformedURLException e) { clipurl = null; }
		if (clipurl == null) return;

		s = getParameter("PLAYLABEL");
		if (play != null) remove(play);
		play = new Button((s != null) ? s : "Play"); add(play);
		s = getParameter("LOOPLABEL");
		if (loop != null) remove(loop);
		loop = new Button((s != null) ? s : "Loop"); add(loop);
		s = getParameter("STOPLABEL");
		if (stop != null) remove(stop);
		stop = new Button((s != null) ? s : "Stop"); add(stop);
		s = getParameter("LOADLABEL");
		if (load != null) remove(load);
		load = new Checkbox((s != null) ? s : "Load");
		if (loading) load.setState(true);
		s = getParameter("NOLOAD");
		if (clip == null && ((s != null) ? ! s.equals("true") : true))
			add(load);

		s = getParameter("BACKLOAD");
		backload = (s != null) ? s.equals("true") : false;
		s = getParameter("BACKPLAY");
		backplay = (s != null) ? s.equals("true") : false;

		preAction("PRELOAD", load);
		preAction("PRELOOP", loop);
		preAction("PREPLAY", play);
	}

	void preAction(String param, Component button) {
		String s = getParameter(param);
		if ((s != null) ? ! s.equals("true") : true) return;
		action(new Event(button, Event.ACTION_EVENT, this), this);
	}

// ClipControl // Events //////////////////////////////////////////////////////

	public boolean mouseEnter(Event evt, int x, int y) {
		showStatus(url);
		return true;
	}

	public boolean mouseExit(Event evt, int x, int y) {
		showStatus(null);
		return true;
	}

	public boolean mouseUp(Event evt, int x, int y) {
		if (url != null) {
			try { getAppletContext().showDocument(new URL(url)); }
			catch (MalformedURLException e) {
				url = null;
				showStatus("Ignoring Malformed Home URL");
			}
		}
		return true;
	}

	public synchronized boolean keyDown(Event evt, int key) {
		if (clip != null) clip.stop();
		if (thrd != null) thrd.stop();
		load.setState(false);
		clip = null; pend = null; thrd = null; loading = false;
		getClip(null);
		return true;
	}

	public boolean action(Event e, Object o) {
		if (e.target == null) return true;
		if (e.target.equals(play)) {
			if (! getClip(e)) {
				showStatus("Playing " + clipname);
				clip.play();
			}
		} else if (e.target.equals(loop)) {
			if (! getClip(e)) {
				showStatus("Looping " + clipname);
				clip.loop();
			}
		} else if (e.target.equals(stop)) {
			pend = null;
			if (clip != null) {
				showStatus("Stopping " + clipname);
				clip.stop();
			}
		} else if (e.target.equals(load)) {
			getClip(null);
		}
		return true;
	}

// ClipControl // Loading /////////////////////////////////////////////////////

	synchronized boolean getClip(Event e) {
		load.setState(true);
		if (! loading) {
			loading = true;
			if (clip == null) {
				showStatus("Loading " + clipname);
				if (e != null) pend = e;
				thrd = new Thread(this);
				thrd.start();
				return true;
			}
		}
		if (clip == null) {
			if (e != null) pend = e;
			showStatus("Still loading " + clipname);
			return true;
		}
		return false;
	}

	public void run() {
		clip = getAudioClip(clipurl);
		showStatus("Loaded " + clipname);
		remove(load);
		repaint();
		if (pend != null) action(pend, this);
		pend = null; thrd = null;
	}

// ClipControl // Suspension //////////////////////////////////////////////////

	public synchronized void stop() {
		if (clip != null && ! backplay) clip.stop();
		if (thrd != null && ! backload) {
			thrd.stop();
			load.setState(false);
			clip = null; pend = null; thrd = null; loading = false;
		}
	}
}
