/*
 *	SequenceAudioInputStream.java
 *
 *	This file is part of jsresources.org
 */

/*
 * Copyright (c) 1999 - 2001 by Matthias Pfisterer
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE
 * COPYRIGHT OWNER OR CONTRIBUTORS 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.
 */

/*
|<---            this code is formatted to fit into 80 columns             --->|
*/

import java.io.ByteArrayInputStream;
import java.io.IOException;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;


public class SequenceAudioInputStream
	extends		AudioInputStream
{
	private static final boolean	DEBUG = true;

	private List			m_audioInputStreamList;
	private	int			m_nCurrentStream;



	public SequenceAudioInputStream(AudioFormat audioFormat, Collection audioInputStreams)
	{
		super(new ByteArrayInputStream(new byte[0]), audioFormat, AudioSystem.NOT_SPECIFIED);
		m_audioInputStreamList = new ArrayList(audioInputStreams);
		m_nCurrentStream = 0;
	}



	// TODO: remove
	private boolean addAudioInputStream(AudioInputStream audioStream)
	{
		if (DEBUG)
		{
			out("SequenceAudioInputStream.addAudioInputStream(): called.");
		}
		// Contract.check(audioStream != null);
		if (!getFormat().matches(audioStream.getFormat()))
		{
			if (DEBUG)
			{
				out("SequenceAudioInputStream.addAudioInputStream(): audio formats do not match, trying to convert.");
			}
			AudioInputStream	asold = audioStream;
			audioStream = AudioSystem.getAudioInputStream(getFormat(), asold);
			if (audioStream == null)
			{
				out("###  SequenceAudioInputStream.addAudioInputStream(): could not convert.");
				return false;
			}
			if (DEBUG)
			{
				out(" converted");
			}
		}
		// Contract.check(audioStream != null);
		synchronized (m_audioInputStreamList)
		{
			m_audioInputStreamList.add(audioStream);
			m_audioInputStreamList.notifyAll();
		}
		if (DEBUG)
		{
			out("SequenceAudioInputStream.addAudioInputStream(): enqueued " + audioStream);
		}
		return true;
	}



	private AudioInputStream getCurrentStream()
	{
		return (AudioInputStream) m_audioInputStreamList.get(m_nCurrentStream);
	}



	private boolean advanceStream()
	{
		m_nCurrentStream++;
		boolean	bAnotherStreamAvailable = (m_nCurrentStream < m_audioInputStreamList.size());
		return bAnotherStreamAvailable;
	}



	public long getFrameLength()
	{
		long	lLengthInFrames = 0;
		Iterator	streamIterator = m_audioInputStreamList.iterator();
		while (streamIterator.hasNext())
		{
			AudioInputStream	stream = (AudioInputStream) streamIterator.next();
			long	lLength = stream.getFrameLength();
			if (lLength == AudioSystem.NOT_SPECIFIED)
			{
				return AudioSystem.NOT_SPECIFIED;
			}
			else
			{
				lLengthInFrames += lLength;
			}
		}
		return lLengthInFrames;
	}



	public int read()
		throws	IOException
	{
		AudioInputStream	stream = getCurrentStream();
		int	nByte = stream.read();
		if (nByte == -1)
		{
			/*
			  The end of the current stream has been signaled.
			  We try to advance to the next stream.
			*/
			boolean	bAnotherStreamAvailable = advanceStream();
			if (bAnotherStreamAvailable)
			{
				/*
				  There is another stream. We recurse into this method
				  to read from it.
				*/
				return read();
			}
			else
			{
				/*
				  No more data. We signal EOF.
				*/
				return -1;
			}
		}
		else
		{
			/*
			  The most common case: We return the byte.
			*/
			return nByte;
		}
	}



	public int read(byte[] abData, int nOffset, int nLength)
		throws	IOException
	{
		AudioInputStream	stream = getCurrentStream();
		int	nBytesRead = stream.read(abData, nOffset, nLength);
		if (nBytesRead == -1)
		{
			/*
			  The end of the current stream has been signaled.
			  We try to advance to the next stream.
			*/
			boolean	bAnotherStreamAvailable = advanceStream();
			if (bAnotherStreamAvailable)
			{
				/*
				  There is another stream. We recurse into this method
				  to read from it.
				*/
				return read(abData, nOffset, nLength);
			}
			else
			{
				/*
				  No more data. We signal EOF.
				*/
				return -1;
			}
		}
		else
		{
			/*
			  The most common case: We return the length.
			*/
			return nBytesRead;
		}
	}



	public long skip(long lLength)
		throws	IOException
	{
		throw new IOException("skip() is not implemented in class SequenceInputStream. Mail if you need this feature.");
	}



	public int available()
		throws	IOException
	{
		return getCurrentStream().available();
	}



	public void close()
		throws	IOException
	{
		// TODO: should we close all streams in the list?
	}



	public void mark(int nReadLimit)
	{
		throw new RuntimeException("mark() is not implemented in class SequenceInputStream. Mail if you need this feature.");
	}



	public void reset()
		throws	IOException
	{
		throw new IOException("reset() is not implemented in class SequenceInputStream. Mail if you need this feature.");
	}



	public boolean markSupported()
	{
		return false;
	}



	private static void out(String strMessage)
	{
		System.out.println(strMessage);
	}
}



/*** SequenceAudioInputStream.java ***/