/**
 * PlayerThreaded class
 * @author Philip Peng
 * 
 * Extends JLayer 1.0's Player class
 * Plays file as separate thread
 * 
 * Created by Philip Peng
 * Last updated: 2007/10/12
 * 
 * Licensed under GNU GPL (Version 3):
 * http://www.gnu.org/licenses/gpl.txt
 * 
 * JLayer website:
 * http://www.javazoom.net/javalayer/sources.html
 * 
 */

import javazoom.jl.player.Player;
import java.io.FileInputStream;

// Main class
public class PlayerThreaded extends Player {
	
	// PlayerThread thread
	private PlayerThreadPlay foobarThread;
	
	// New pause boolean
	public boolean paused = false;
	
	/**
	 * Constructor
	 * @param stream	File stream (FileInputStream object)
	 */
	public PlayerThreaded (FileInputStream stream)
	throws javazoom.jl.decoder.JavaLayerException {
		super(stream, null);
	}
	
	/**
	 * Constructor
	 * @param file	File location (String object)
	 */
	public PlayerThreaded (String file)
	throws javazoom.jl.decoder.JavaLayerException, java.io.FileNotFoundException {
		super(convertStringToStream(file), null);
	}
	
	/**
	 * Convert String object to FileInputStream object
	 * @param file	String of file location.
	 * @return stream	File location as a FileInputStream
	 * @see PlayerThreaded
	 */
	static private FileInputStream convertStringToStream(String file)
	throws java.io.FileNotFoundException {
		FileInputStream stream = new FileInputStream(file);
		return stream;
	}
	
	/**
	 * Parent play() method in a separate thread
	 */
	public void play() {
		if (paused = true) {
			this.resume();
		} else {
		//if (foobarThread == null) {
			foobarThread = new PlayerThreadPlay(this);
			foobarThread.start();
		//} else {
			//this.resume();
		//}
		}
	}
	
	/**
	 * Parent play() method
	 */
	public void playParent()
	throws javazoom.jl.decoder.JavaLayerException {
		super.play();
	}
	
	public void stop() {
		foobarThread.stop();
		paused = false;
	}
	
	/*
	 * Changed October 14, 2007
	 * 
	 * Uses Thread's depreciated .suspend() and .resume() methods.
	 * Original methods were depreciated due to possible unstability;
	 * however, since they don't seem to cause any issues here and are
	 * much simpler than modifying/over-riding the original JLayer's
	 * Player class source code, they are used. See commented out code
	 * below for the alternative.
	 * 
	 */
	
	/**
	 * Suspends playback thread
	 * Note: Uses Thread's depreciated .suspend() method. May be unstable
	 */
	public void pause() {
		foobarThread.suspend();
		paused = true;
	}
	
	/**
	 * Resumes playback thread.
	 * Note: Uses Thread's depreciated .resume() method. May be unstable
	 */
	public void resume() {
		foobarThread.resume();
		paused = false;
	}	
	
	/*
	 * Changed October 14, 2007
	 * 
	 * This method of adding pause/resume functionality requires
	 * the modification of the original method. It pauses the playback
	 * by disrupting frame decoding. While it does work, however, repeated
	 * use causes the audio device to become unsynchronized and results in
	 * either the audio device not resuming or not pausing.
	 * And so, while this method of implementing pause/resume does "work",
	 * to avoid buggyness and incompatibility with future versions of
	 * JLayer, this class uses Thread's depreciated but simpler
	 * .suspend() and .resume() methods (see above for more info).
	 * 
	 * To uncomment this section using Eclipse, select all the commented out code,
	 * right click and Source > Toggle Comment
	 */ 
	
//	// Parent variables; make accessible
//	private AudioDevice	audio;
//	private boolean		closed = false;
//	private boolean		complete = false;
//	
//	/**
//	 * Modified parent play(int frames) boolean with paused flag check
//	 * @param frames	The number of frames to play. 
//	 * @return	true if the last frame was played, or false if there are
//	 *			more frames. 
//	 */
//	// Basically a direct source code copy
//	public boolean play(int frames) throws JavaLayerException
//	{
//		boolean ret = true;
//		
//		while (frames-- > 0 && ret)
//		{
//			// Added paused boolean check
//			if (!paused) {
//				ret = decodeFrame();
//			}
//		}
//		
//		if (!ret)
//		{
//			// last frame, ensure all data flushed to the audio device. 
//			AudioDevice out = audio;
//			if (out!=null)
//			{				
//				out.flush();
//				synchronized (this)
//				{
//					complete = (!closed);
//					close();
//				}				
//			}
//		}
//		return ret;
//	}
//	
//	/**
//	 * Sets "paused" flag to true
//	 * SNAFU: Repeated pause/resume causes buggyness possibly from sync loss >_<
//	 */
//	public void pause() {
//		paused = true;
//	}
//	
//	/**
//	 * Sets "paused" flag to false
//	 * SNAFU: Repeated pause/resume causes buggyness possibly from sync loss >_<
//	 */
//	public void resume() {
//		paused = false;
//	}
	
} // End public class PlayerThreaded extends Player


// Helper thread class
class PlayerThreadPlay extends Thread {

	private PlayerThreaded foobarThreaded;
	
	/**
	 * Constructor
	 * @param foobar	The Player itself (PlayerThreaded object)
	 */
	public PlayerThreadPlay (PlayerThreaded thread) {
		foobarThreaded = thread;
	}
	
	/**
	 * Run() method; called by start()
	 */
	public void run() {
		try {
			foobarThreaded.playParent();
		} catch ( Exception e ) {
			e.printStackTrace();
		}
	}
	
} // End class PlayerThreadPlay extends Thread