package ca.carleton.gcrc.atlas.tracks.conversion;

import java.net.URL;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

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

import ca.carleton.gcrc.atlas.logger.Logger;

public class AudioStreamConverter {
	final private Logger logger = Logger.forClass(getClass());
	
	private URL url;
	private List<InternalConverter> converters = new Vector<InternalConverter>();
	
	public AudioStreamConverter(URL url) {
		this.url = url;

		String urlPath = this.url.getPath();
		if( null != urlPath ) {
			urlPath.toLowerCase();
			
			if (urlPath.endsWith(".mp3")) {
				converters.add( Mp3Converter.createInstance() );
				converters.add( SystemConverter.createInstance() );
				converters.add( OggVorbisConverter.createInstance() );
			} else if (urlPath.endsWith("ogg")) {
				converters.add( OggVorbisConverter.createInstance() );
				converters.add( SystemConverter.createInstance() );
				converters.add( Mp3Converter.createInstance() );
			} else {
				converters.add( SystemConverter.createInstance() );
				converters.add( Mp3Converter.createInstance() );
				converters.add( OggVorbisConverter.createInstance() );
			}
		} 
	}

	public URL getUrl() {
		return url;
	}

	/**
	 * Get an AudioInputStream for the stored URL.  Convert it to a decoding AudioInputStream if necessary.
	 * 
	 * @return the AudioInputStream, converted if necessary.
	 */
	public AudioInputStream getAndConvertAudioInputStream() {
		AudioInputStream inputStream = getAudioInputStreamFromUrl();
		if (null == inputStream) {
			logger.error("getAndConvertAudioInputStream: failed to open URL " + url);
		} else {
			try {
				AudioFormat format = inputStream.getFormat();
				logger.debug("getAndConvertAudioInputStream: Input audio stream format: " + format);
	
				/**
				 * we can't yet open the device for compressed format playback
				 * (ALAW/ULAW, ogg vorbis, etc.) - convert ALAW/ULAW to PCM
				 */
				if (format.getEncoding() != AudioFormat.Encoding.PCM_SIGNED) {
					AudioInputStream convertedStream = getConversionAudioInputStream(inputStream);
					if (null != convertedStream) {
						return convertedStream;
					}
				}
	
				return inputStream;
			} catch(Exception e) {
				logger.error("getAndConvertAudioInputStream: error while converting " + url);
				return null;
			}
		}

		return null;
	}
	
	/**
	 * Use the standard AudioSystem infrastructure to get the input stream for
	 * the input URL. If that does not work, explicitly attempt to open the URL
	 * as a Vorbis audio stream or as an MP3 audio stream. This is done on the
	 * assumption that the Ogg Vorbis SPI and the MP3 SPI code code is being
	 * packaged as part of the applet jar but may not be available in the target
	 * system's classpath.
	 * 
	 * @return an audio input stream for the url.
	 */
    protected AudioInputStream getAudioInputStreamFromUrl() {
		Iterator<InternalConverter> it = converters.iterator();
		while( it.hasNext() ) {
			InternalConverter converter = it.next();
			AudioInputStream inputStream = converter.getAudioInputStream(url);
			
			if( null != inputStream ) {
				return inputStream;
			}
		}

		return null;
	} // getAudioInputStreamFromUrl

    /**
     * Use the standard AudioSystem infrastructure to get an input conversion stream. If that fails
     * then explictly attempt to create an Ogg Vorbis or MP3 conversion input stream. This is done on the
     * assumption that the Ogg Vorbis and MP3 SPI code is being packaged as part of the applet jar but may
     * not be available in the target system's classpath.
     *
     * @param inStream the audio stream to be converted.
     * @return output conversion audio stream.
     */
   protected AudioInputStream getConversionAudioInputStream(AudioInputStream inStream) {
    		AudioInputStream outStream;
		int nSampleSizeInBits;
		AudioFormat format;

		// create a PCM_SIGNED format as target format for conversion
		format = inStream.getFormat();
		nSampleSizeInBits = format.getSampleSizeInBits();
		if (nSampleSizeInBits <= 0) {
			nSampleSizeInBits = 16;
		}
		if ( (format.getEncoding() == AudioFormat.Encoding.ULAW) 
		 ||	(format.getEncoding() == AudioFormat.Encoding.ALAW) )  {
			nSampleSizeInBits = 16;
		}
		if (nSampleSizeInBits != 8) {
			nSampleSizeInBits = 16;
		}
		format = new AudioFormat(
				AudioFormat.Encoding.PCM_SIGNED,
				format.getSampleRate(),
				nSampleSizeInBits,
				format.getChannels(),
				format.getChannels() * (nSampleSizeInBits / 8),
				format.getSampleRate(),
				false); // ogg only works as little endian (not sure why...)
		logger.info("getConversionAudioInputStream: requested target conversion format: " + format);
		
		Iterator<InternalConverter> it = converters.iterator();
		while( it.hasNext() ) {
			InternalConverter converter = it.next();
			
			outStream = converter.getAudioInputStream(format, inStream);
			if( null != outStream ) {
				return outStream;
			}
		}
		
		return null;
	}

}
