


// ant includes
import org.apache.tools.ant.Task;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.BuildListener;
import org.apache.tools.ant.BuildEvent;

// imports for all the sound classes required
// note: comes with jmf or jdk1.3 +
// these can be obtained from http://java.sun.com/products/java-media/sound/
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.Line;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineListener;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;

import java.io.IOException;
import java.io.File;
import java.io.InputStream;
import java.util.Date;
import java.lang.Runnable;


/**
 * This class is designed to be used by any AntTask that requires audio output.
 *
 * It implements the BuildListener interface to listen for BuildEvents and could
 * be easily extended to provide audio output upon any specific build events occuring.
 *
 * I have only tested this with .WAV and .AIFF sound file formats. Both seem to work fine.
 *
 * @author Nick Pellow
 * @version $Revision$, $Date$
 */

public class AntSoundPlayer implements Runnable, LineListener, BuildListener {

    private File fileSuccess = null;
    private int loopsSuccess = 1;
    private Long durationSuccess = null;

    private File fileFail = null;
    private int loopsFail = 1;
    private Long durationFail = null;

    public AntSoundPlayer() {

    }

    /**
     * @param fileName the location of the audio file to be played when the build is succesful
     * @param loops the number of times the file should be played when the build is succesful
     * @param duration the number of milliseconds the file should be played when the build is succesful
     */
    public void addBuildSuccesfulSound(File file, int loops, Long duration) {
        this.fileSuccess = file;
        this.loopsSuccess = loops;
        this.durationSuccess = duration;
    }


    /**
     * @param fileName the location of the audio file to be played when the build fails
     * @param loops the number of times the file should be played when the build is fails
     * @param duration the number of milliseconds the file should be played when the build fails
     */
    public void addBuildFailedSound(File fileFail, int loopsFail, Long durationFail) {
        this.fileFail = fileFail;
        this.loopsFail = loopsFail;
        this.durationFail = durationFail;
    }

    /**
     * Kicks off the thread.
     *
     * Listens for a BuildEvent.
     */
    public void run() {
            while ( true ) {
            }
    }

    /**
     * Plays the file for duration milliseconds or loops loops.
     */
    private void play(File file, int loops, Long duration) {

        Clip audioClip = null;

        System.out.println("Using sound file: "+file);

        AudioInputStream audioInputStream = null;


		try {
			audioInputStream = AudioSystem.getAudioInputStream(file);
		}
		catch (UnsupportedAudioFileException uafe) {
			System.out.println("Audio format is not yet supported: "+uafe.getMessage());
		}
        catch (IOException ioe) {
            ioe.printStackTrace();
        }

		if (audioInputStream != null) {
			AudioFormat	format = audioInputStream.getFormat();
			DataLine.Info	info = new DataLine.Info(Clip.class, format, AudioSystem.NOT_SPECIFIED);
			try {
				audioClip = (Clip) AudioSystem.getLine(info);
				audioClip.addLineListener(this);
                audioClip.open(audioInputStream);
			}
			catch (LineUnavailableException e) {
                System.out.println("The sound device is currently unavailable");
                return;
			}
			catch (IOException e) {
				e.printStackTrace();
			}

            if (duration != null) {
                playClip(audioClip, duration.longValue());
            } else {
                playClip(audioClip, loops);
            }
            audioClip.drain();
            audioClip.close();
		}
		else {
			System.out.println("SoundTask: can't get data from file " + file.getName());
		}
    }

    private void playClip(Clip clip, int loops) {

        clip.loop(loops);
        while (clip.isRunning()) {
        }
    }

    private void playClip(Clip clip, long duration) {

        long currentTime = System.currentTimeMillis();
        clip.loop(Clip.LOOP_CONTINUOUSLY);
        for (long endTime = currentTime+duration ; endTime > currentTime; currentTime = System.currentTimeMillis())  {
        }
    }

    /**
     * This is implemented to listen for any line events and closes the clip if required.
     */
    public void update(LineEvent event) {

        //System.out.println("Sound Event: "+event.toString());

        if (event.getType().equals(LineEvent.Type.STOP)) {
            Line line = event.getLine();
            line.close();
		}
		else if (event.getType().equals(LineEvent.Type.CLOSE)) {
			/*
			 *	There is a bug in JavaSound 0.90 (jdk1.3beta).
			 *	It prevents correct termination of the VM.
			 *	So we have to exit ourselves.
			 */
			//System.exit(0);
		}
	}


    /**
     *  Fired before any targets are started.
     */
    public void buildStarted(BuildEvent event){
        // System.out.println("buildStarted: "+event.getMessage());
    }
    /**
     *  Fired after the last target has finished. This event
     *  will still be thrown if an error occured during the build.
     *
     *  @see BuildEvent#getException()
     */
    public void buildFinished(BuildEvent event){
        // System.out.println("buildFinished: "+event.getMessage());
        if (event.getException() == null) {
            // build successfull!
            play(fileSuccess, loopsSuccess, durationSuccess);
        } else {
            play(fileFail, loopsFail, durationFail);
        }
    }

    /**
     *  Fired when a target is started.
     *
     *  @see BuildEvent#getTarget()
     */
    public void targetStarted(BuildEvent event){

        //System.out.println("targetStarted: "+event.getMessage());
    }

    /**
     *  Fired when a target has finished. This event will
     *  still be thrown if an error occured during the build.
     *
     *  @see BuildEvent#getException()
     */
    public void targetFinished(BuildEvent event){

        // System.out.println("targetFinished: "+event.getMessage());
    }

    /**
     *  Fired when a task is started.
     *
     *  @see BuildEvent#getTask()
     */
    public void taskStarted(BuildEvent event){

        // System.out.println("taskStarted: "+event.getMessage());
    }

    /**
     *  Fired when a task has finished. This event will still
     *  be throw if an error occured during the build.
     *
     *  @see BuildEvent#getException()
     */
    public void taskFinished(BuildEvent event){

        //   System.out.println("taskFinished: "+event.getMessage());
    }

    /**
     *  Fired whenever a message is logged.
     *
     *  @see BuildEvent#getMessage()
     *  @see BuildEvent#getPriority()
     */
    public void messageLogged(BuildEvent event){
        //System.out.println("messageLogged: "+event.getMessage());
    }
}

