package net.sf.fmj.media.multiplexer;

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

import javax.media.Buffer;
import javax.media.Format;
import javax.media.ResourceUnavailableException;
import javax.media.protocol.ContentDescriptor;
import javax.media.protocol.DataSource;

import net.sf.fmj.media.AbstractMultiplexer;
import net.sf.fmj.media.BufferQueueInputStream;
import net.sf.fmj.utility.LoggerSingleton;

/**
 * Mux that can be implemented simply by copying streams.
 * Override createInputStreamPushDataSource and create an 
 * overridden version of StreamCopyPushDataSource overriding write.
 * @author Ken Larson
 *
 */
public abstract class AbstractStreamCopyMux extends AbstractMultiplexer
{
	private static final Logger logger = LoggerSingleton.logger;

	private BufferQueueInputStream[] bufferQueueInputStreams;
	private StreamCopyPushDataSource dataOutput;
	
	private final ContentDescriptor contentDescriptor;
	
	// TODO: deal with n tracks properly
	
	public AbstractStreamCopyMux(final ContentDescriptor contentDescriptor)
	{
		super();
		this.contentDescriptor = contentDescriptor;
		
	}

	public DataSource getDataOutput()
	{
		if (dataOutput == null)
			dataOutput = createInputStreamPushDataSource(outputContentDescriptor, numTracks, bufferQueueInputStreams, inputFormats);
		logger.finer(getClass().getSimpleName() + " getDataOutput");
		return dataOutput;
	}

	public abstract Format[] getSupportedInputFormats();

	@Override
	public void close()
	{
		logger.finer(getClass().getSimpleName() + " close");
		super.close();
		
		if (dataOutput != null)
		{	try
			{
				dataOutput.stop();
			} catch (IOException e)
			{
				logger.log(Level.WARNING, "" + e, e);
			}
			dataOutput.disconnect();
		}
	}

	@Override
	public void open() throws ResourceUnavailableException
	{
		logger.finer(getClass().getSimpleName() + " open");
		super.open();
	}

	public ContentDescriptor[] getSupportedOutputContentDescriptors(Format[] inputs)
	{
		// TODO: should this match the # of entries in inputs?
		return new ContentDescriptor[] {contentDescriptor};
	}

	public int process(Buffer buffer, int trackID)
	{
		logger.finer(getClass().getSimpleName() +  " process " + buffer + " " + trackID + " length " + buffer.getLength());
		// need a PushDataSource, with a PushSourceStream that reads from out
		
		if (buffer.isEOM())
			logger.finer("processing EOM buffer for track: " + trackID);
		
		if (!bufferQueueInputStreams[trackID].put(buffer))
			return INPUT_BUFFER_NOT_CONSUMED;
		
		try
		{
			if (buffer.isEOM())
			{
				logger.fine("EOM, waitUntilFinished...");
				if (dataOutput != null)
					dataOutput.waitUntilFinished();
				// wait until done processing
				logger.fine("EOM, finished.");
			}
			
			if (dataOutput != null)
				dataOutput.notifyDataAvailable(trackID);
			
			return BUFFER_PROCESSED_OK;
		
		} catch (InterruptedException e)
		{
			logger.log(Level.WARNING, "" + e, e);
			return BUFFER_PROCESSED_FAILED;
		}
	}

	@Override
	public int setNumTracks(int numTracks)
	{
		numTracks = super.setNumTracks(numTracks);
		
		bufferQueueInputStreams = new BufferQueueInputStream[numTracks];
		for (int track = 0; track < numTracks; ++track)
		{
			bufferQueueInputStreams[track] = new BufferQueueInputStream();
		}
		return numTracks;
	}
	

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

	


}
