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

/*
 * Copyright (c) 1999 - 2002 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.File;
import java.io.IOException;

import java.lang.reflect.Method;

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

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.Mixer;
import javax.sound.sampled.SourceDataLine;

/*	If the compilation fails because this class is not available,
	get gnu.getopt from the URL given in the comment below.
*/
import gnu.getopt.Getopt;
import gnu.getopt.LongOpt;


// TODO: adapt doc
/**	<titleabbrev>DumpJSInfo</titleabbrev>
	<title>Displaying information about the Java Sound implementation/installation</title>

	<formalpara><title>Purpose</title>
	<para>
	This program can display information about available Mixers, Controls,
	MidiDevices and installed service providers.
	</para></formalpara>

	<formalpara><title>Usage</title>
	<para>
	<cmdsynopsis>
	<command>java DumpJSInfo</command>
	<arg choice="plain"><option>-h, --help</option></arg>
	</cmdsynopsis>
	<cmdsynopsis>
	<command>java DumpJSInfo</command>
	<arg choice="plain"><option>-V, --version</option></arg>
	</cmdsynopsis>
	<cmdsynopsis>
	<command>java DumpJSInfo</command>
	<arg choice="opt"><option>-D, --debug</option></arg>
	<arg choice="opt"><option>--mixers</option></arg>
	<arg choice="opt"><option>--controls</option></arg>
	<arg choice="opt"><option>--mididevices</option></arg>
	<arg choice="opt"><option>--providers</option></arg>
	<arg choice="opt"><option>-a, --all</option></arg>
	</cmdsynopsis>
	</para></formalpara>

	<formalpara><title>Parameters</title>
	<variablelist>
	<varlistentry>
	<term><option>-l</option></term>
	<listitem><para>lists the available mixers</para></listitem>
	</varlistentry>
	<varlistentry>
	<term><option>-M <replaceable>mixername</replaceable></option></term>
	<listitem><para>selects a mixer to play on</para></listitem>
	</varlistentry>
	<varlistentry>
	<term><option>-e <replaceable>buffersize</replaceable></option></term>
	<listitem><para>the buffer size to use in the application ("extern")</para></listitem>
	</varlistentry>
	<varlistentry>
	<term><option>-i <replaceable>buffersize</replaceable></option></term>
	<listitem><para>the buffer size to use in Java Sound ("intern")</para></listitem>
	</varlistentry>
	<varlistentry>
	<term><option><replaceable>audiofile</replaceable></option></term>
	<listitem><para>the file name of the audio file to play</para></listitem>
	</varlistentry>
	</variablelist>
	</formalpara>

	<formalpara><title>Bugs, limitations</title>
	<para>
	Not known.
	</para>
	</formalpara>

	<formalpara><title>Source code</title>
	<para>
	<ulink url="DumpJSInfo.java.html">DumpJSInfo.java</ulink>,
	<olink targetdocent="getopt">gnu.getopt.Getopt</olink>
	</para>
	</formalpara>

*/
public class DumpJSInfo
{
	private static final String	BASE_NAME = "META-INF/services/";


	/**	Flag for debugging messages.
	 *	If true, some messages are dumped to the console
	 *	during operation.	
	 */
	private static boolean	DEBUG = false;



	private static final String[]	INDENTATION_TABLE =
	{
		"",
		"    ",
		"        ",
		"            ",
		"                ",
		"                    ",
		"                        ",
		"                            ",
		"                                ",
	};


	// TODO: rename to CONFIGURATION_SOURCES
	public static final Object[]	sm_configurationSources = new Object[]
	{
		// for Sun jdk1.3/1.4
		new String[]{"sun.misc.Service", "providers"},
		// for Tritonus
		new String[]{"org.tritonus.core.Service", "providers"},
	};



	public static void main(String[] args)
		throws Exception
	{
		boolean	bDisplayMixers = false;
		boolean	bDisplayControls = false;
		boolean	bDisplayMidiDevices = false;
		boolean	bDisplayProviders = false;

		/*
		 *	Parsing of command-line options takes place...
		 */
		LongOpt[]	aLongOpts = new LongOpt[]{
			new LongOpt("help", LongOpt.NO_ARGUMENT, null, 'h'),
			new LongOpt("debug", LongOpt.NO_ARGUMENT, null, 'D'),
			new LongOpt("version", LongOpt.NO_ARGUMENT, null, 'V'),
			new LongOpt("mixers", LongOpt.NO_ARGUMENT, null, 1),
			new LongOpt("controls", LongOpt.NO_ARGUMENT, null, 2),
			new LongOpt("mididevices", LongOpt.NO_ARGUMENT, null, 3),
			new LongOpt("providers", LongOpt.NO_ARGUMENT, null, 4),
			new LongOpt("all", LongOpt.NO_ARGUMENT, null, 'a'),
		};
		Getopt	g = new Getopt("DumpJSInfo", args, "hDVa", aLongOpts);
		int	c;
		while ((c = g.getopt()) != -1)
		{
			switch (c)
			{
			case 'h':
				printUsageAndExit();

			case 'V':
				printVersionAndExit();

			case 'D':
				DEBUG = true;
				break;

			case 1:
				bDisplayMixers = true;
				break;

			case 2:
				bDisplayControls = true;
				break;

			case 3:
				bDisplayMidiDevices = true;
				break;

			case 4:
				bDisplayProviders = true;
				break;

			case 'a':
				bDisplayMixers = true;
				bDisplayControls = true;
				bDisplayMidiDevices = true;
				bDisplayProviders = true;
				break;

			case '?':
				printUsageAndExit();

			default:
				out("getopt() returned " + c);
				break;
			}
		}

		/* If no display option is selected, select all.
		 */
		if (! (bDisplayMixers | bDisplayControls |
		       bDisplayMidiDevices | bDisplayProviders))
		{
			bDisplayMixers = true;
			bDisplayControls = true;
			bDisplayMidiDevices = true;
			bDisplayProviders = true;
		}

		if (bDisplayMixers)
		{
			displayMixers();
		}
		if (bDisplayControls)
		{
			displayControls();
		}
		if (bDisplayMidiDevices)
		{
			displayMidiDevices();
		}
		if (bDisplayProviders)
		{
			displayServiceProviders();
		}

		System.exit(0);
	}



	private static void displayMixers()
		throws Exception
	{
	}



	private static void displayControls()
		throws Exception
	{
	}



	private static void displayMidiDevices()
		throws Exception
	{
	}



	private static void displayServiceProviders()
		throws Exception
	{
		final String[]	astrCategories =
			{
				"javax.sound.midi.spi.MidiDeviceProvider",
				"javax.sound.midi.spi.MidiFileReader",
				"javax.sound.midi.spi.MidiFileWriter",
				"javax.sound.midi.spi.SoundbankReader",
				"javax.sound.sampled.spi.AudioFileReader",
				"javax.sound.sampled.spi.AudioFileWriter",
				"javax.sound.sampled.spi.FormatConversionProvider",
				"javax.sound.sampled.spi.MixerProvider",
			};


		out(0, "Service Providers");
		for (int nProviderType = 0;
		     nProviderType < astrCategories.length;
		     nProviderType++)
		{
			String	strProviderTypeName = astrCategories[nProviderType];
			out(1, "");
			out(1, strProviderTypeName);
			out(2, "Available Providers");
			displayProviders(strProviderTypeName);
			out(2, "Configuration Files");
			displayConfigurationFiles(strProviderTypeName);
		}
	}



	private static void displayProviders(String strProviderTypeName)
	{
		Class		providerClass = null;
		Method		providersMethod = null;

		for (int i = 0; i < sm_configurationSources.length; i++)
		{
			String	strServiceClassName = ((String[]) sm_configurationSources[i])[0];
			String	strProvidersMethodName = ((String[]) sm_configurationSources[i])[1];
			try
			{
				Class	serviceClass = Class.forName(strServiceClassName);
				providersMethod = serviceClass.getMethod(strProvidersMethodName, new Class[]{Class.class});
				break;
			}
			catch (Exception e)
			{
				if (DEBUG) { out(e); }
			}
		}
		try
		{
			providerClass = Class.forName(strProviderTypeName);
		}
		catch (ClassNotFoundException e)
		{
			out(e);
		}
		Iterator	services = null;
		try
		{
			services = (Iterator) providersMethod.invoke(null, new Object[]{providerClass});
		}
		catch (Throwable e)
		{
			out(e);
		}
		if (services != null)
		{
			while (services.hasNext())
			{
				Object	provider = services.next();
				out(3, provider.getClass().getName());
			}
		}
	}



	private static void displayConfigurationFiles(String strProviderTypeName)
	{
		String	strFullName = BASE_NAME + strProviderTypeName;
		Class	providerClass = null;
		Enumeration	configs = null;
		try
		{
			providerClass = Class.forName(strProviderTypeName);
			configs = ClassLoader.getSystemResources(strFullName);
		}
		catch (Exception e)
		{
			out(e);
		}
		// if (configs != null)
		// {
		while (configs.hasMoreElements())
		{
			Object	config = configs.nextElement();
			out(3, config.toString());
		}
		// }
	}




	private static void printUsageAndExit()
	{
		out("DumpJSInfo: usage:");
		out("\tjava DumpJSInfo -h, --help");
		out("\tjava DumpJSInfo -V, --version");
		out("\tjava DumpJSInfo [-D, --debug] [--mixers] [--controls] [--mididevices] [--providers] [-a, --all]");
		System.exit(0);
	}



	private static void printVersionAndExit()
	{
		out("DumpJSInfo version 0.1.0");
		out("Copyright (c) 1999 - 2003 by Matthias Pfisterer");
		System.exit(0);
	}



	private static void out(int nIndentationStep, String strMessage)
	{
		String	strIndentation = INDENTATION_TABLE[nIndentationStep];
		System.out.println(strIndentation + strMessage);
	}



	private static void out(String strMessage)
	{
		out(0, strMessage);
	}



	private static void out(Throwable throwable)
	{
		throwable.printStackTrace();
	}
}



/*** DumpJSInfo.java ***/