package net.sf.fmj.media.multiplexer.audio;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.logging.Logger;

import javax.media.Format;
import javax.media.format.AudioFormat;
import javax.media.protocol.ContentDescriptor;
import javax.media.protocol.FileTypeDescriptor;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;

import net.sf.fmj.media.multiplexer.AbstractStreamCopyMux;
import net.sf.fmj.media.multiplexer.StreamCopyPushDataSource;
import net.sf.fmj.media.renderer.audio.JavaSoundUtils;
import net.sf.fmj.utility.LoggerSingleton;

/**
 * 
 * Audio multiplexer base class implemented using JavaSound.
 * This mux has the job of taking buffers passed in to process, and converting them to a stream to be read by
 * Javasound, and then converting the stream written by Javasound into something that can be read
 * as a track of a datasource.
 * TODO: size is written to file headers as Integer.MAXINT, because the seeking back to the beginning and
 * rewriting of the header is not yet implemented.
 * JMF appears to do this with a hack, casting the SourceTransferHandler to a Seekable, and seeking the output, 
 * then rewriting.
 * 
 * TODO: not so sure of all the open/close/connect/disconnect and which should call what when.
 * @author Ken Larson
 *
 */
public abstract class JavaSoundMux extends AbstractStreamCopyMux
{
	private static final Logger logger = LoggerSingleton.logger;

	private final AudioFileFormat.Type audioFileFormatType;
	
	// TODO: deal with n tracks properly
	
	public JavaSoundMux(final FileTypeDescriptor fileTypeDescriptor, AudioFileFormat.Type audioFileFormatType)
	{
		super(fileTypeDescriptor);
		this.audioFileFormatType = audioFileFormatType;
	}

	private static final int MAX_TRACKS = 1;

	@Override
	public int setNumTracks(int numTracks)
	{
		return super.setNumTracks(numTracks > MAX_TRACKS ? MAX_TRACKS : numTracks);
	}
	

	@Override
	protected StreamCopyPushDataSource createInputStreamPushDataSource(ContentDescriptor outputContentDescriptor, int numTracks, InputStream[] inputStreams, Format[] inputFormats)
	{
		return new MyPushDataSource(outputContentDescriptor, numTracks, inputStreams, inputFormats);
	}


	@Override
	public Format[] getSupportedInputFormats()
	{
		// TODO: query AudioSystem
		return new Format[] {new AudioFormat(AudioFormat.LINEAR)};
	}

	private class MyPushDataSource extends StreamCopyPushDataSource
	{
		final javax.sound.sampled.AudioFormat javaSoundFormats[];
		
		public MyPushDataSource(ContentDescriptor outputContentDescriptor, int numTracks, InputStream[] inputStreams, Format[] inputFormats)
		{
			super(outputContentDescriptor, numTracks, inputStreams, inputFormats);
			javaSoundFormats = new javax.sound.sampled.AudioFormat[numTracks];
			for (int track = 0; track < numTracks; ++track)
			{
				javaSoundFormats[track] = JavaSoundUtils.convertFormat((AudioFormat) inputFormats[track]);
			}
		}

		@Override
		protected void write(InputStream in, OutputStream out, int track) throws IOException
		{
			JavaSoundMux.this.write(in, out, javaSoundFormats[track]);
		}
		
	}
	
	protected void write(InputStream in, OutputStream out, javax.sound.sampled.AudioFormat javaSoundFormat) throws IOException
	{
		final long lengthInFrames = Integer.MAX_VALUE;	// TODO: get java.io.IOException: stream length not specified for most formats (WAV, AIFF)
		final AudioInputStream ais = new AudioInputStream(in, javaSoundFormat, lengthInFrames);
		final AudioFileFormat.Type targetFileFormatType = audioFileFormatType;
		final int bytesWritten = AudioSystem.write(ais, targetFileFormatType, out);
		logger.fine("Audio OutputStream bytes written: " + bytesWritten);
	}
	


}
