/*
	Реализация спецификаций CLDC версии 1.1 (JSR-139), MIDP версии 2.1 (JSR-118)
	и других спецификаций для функционирования компактных приложений на языке
	Java (мидлетов) в среде программного обеспечения Малик Эмулятор.

	Copyright © 2016, 2019 Малик Разработчик

	Это свободная программа: вы можете перераспространять ее и/или изменять
	ее на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
	в каком она была опубликована Фондом свободного программного обеспечения;
	либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.

	Эта программа распространяется в надежде, что она будет полезной,
	но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
	или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
	общественной лицензии GNU.

	Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
	вместе с этой программой. Если это не так, см.
	<https://www.gnu.org/licenses/>.
*/


package malik.emulator.midp.player;

import java.io.*;
import javax.microedition.io.*;
import javax.microedition.media.*;
import malik.emulator.fileformats.sound.sampled.wavelib.*;
import malik.emulator.fileformats.sound.synthetic.midilib.*;
import malik.emulator.io.j2me.*;
import malik.emulator.media.sound.*;

public class SupportedPlayerFormats extends Object
		implements VirtualPlayerManager
{
	private static final String CONTENT_TYPE_AUDIO_MIDI = "audio/midi";
	private static final String CONTENT_TYPE_AUDIO_WAVE = "audio/x-wav";
	private static final String PROTOCOL_SIGNATURE = "://";
	private static final String PROTOCOL_RESOURCE = "resource";
	private static final String PROTOCOL_RESOURCE_SIGNATURE =
			PROTOCOL_RESOURCE + PROTOCOL_SIGNATURE;

	private static final class NoteOffTask extends Scheduler.Task
	{
		private int message;
		private SoundPlayerMessaged generator;

		NoteOffTask(SoundPlayerMessaged generator, int message)
		{
			this.message = message;
			this.generator = generator;
		}

		public void run()
		{
			generator.sendMessage(message);
		}
	}

	public static int midpVolumeToDeviceVolume(int volume)
	{
		return volume < 0 ? 0 : (volume >= 100 ? 127 : (volume * 127) / 100);
	}

	public static int midpVolumeToMIDPVolume(int volume)
	{
		return volume < 0 ? 0 : (volume > 100 ? 100 : volume);
	}

	public static int deviceVolumeToMIDPVolume(int volume)
	{
		return volume < 0 ? 0 : (volume >= 127 ? 100 : (volume * 100) / 127);
	}

	private static long continueReadSignature(long signature, InputStream stream, int bytes)
			throws IOException
	{
		int i;
		for(i = bytes; i-- > 0; )
		{
			signature = (signature << 8) | (long) stream.read();
		}
		return signature;
	}


	private String[] supportedContentTypes;
	private String[] supportedProtocols;
	private String[] emptyList;
	private SoundPlayerMessaged toneGenerator;

	public SupportedPlayerFormats()
	{
		SoundPlayerMessaged generator;
		try
		{
			generator = SoundPlayerSynthetic.open();
		}
		catch(SoundPlayerException e)
		{
			generator = null;
		}
		this.supportedContentTypes = new String[] {
				CONTENT_TYPE_AUDIO_MIDI, CONTENT_TYPE_AUDIO_WAVE
		};
		this.supportedProtocols = new String[] {
				PROTOCOL_RESOURCE
		};
		this.emptyList = new String[0];
		this.toneGenerator = generator;
	}

	public void playTone(int note, int duration, int volume)
			throws MediaException
	{
		int msg;
		SoundPlayerMessaged generator;
		if(note < 0x00 || note > 0x7f)
		{
			throw new IllegalArgumentException("Manager.playTone: " +
					"параметр note выходит из диапазона.");
		}
		if(duration <= 0)
		{
			throw new IllegalArgumentException("Manager.playTone: " +
					"параметр duration может быть только положительным.");
		}
		if((generator = toneGenerator) == null)
		{
			throw new MediaException("Manager.playTone: " +
					"системе не хватает памяти для создания нового MIDI-проигрывателя или " +
					"система не поддерживает стандарт MIDI.");
		}
		generator.sendMessage((msg = 0x900000 | (note << 8)) | midpVolumeToDeviceVolume(volume));
		Scheduler.schedule(new NoteOffTask(generator, msg), (long) duration, Scheduler.MONOPOLY);
	}

	public String[] getSupportedContentTypes(String protocol)
	{
		int i;
		String[] list;
		String[] result = emptyList;
		if(protocol != null)
		{
			for(i = (list = supportedProtocols).length; i-- > 0; )
			{
				if(protocol.equalsIgnoreCase(list[i]))
				{
					result = supportedContentTypes;
					break;
				}
			}
		} else
		{
			result = supportedContentTypes;
		}
		Array.copy(result, 0, result = new String[i = result.length], 0, i);
		return result;
	}

	public String[] getSupportedProtocols(String type)
	{
		int i;
		String[] list;
		String[] result = emptyList;
		if(type != null)
		{
			for(i = (list = supportedContentTypes).length; i-- > 0; )
			{
				if(type.equalsIgnoreCase(list[i]))
				{
					result = supportedProtocols;
					break;
				}
			}
		} else
		{
			result = supportedProtocols;
		}
		Array.copy(result, 0, result = new String[i = result.length], 0, i);
		return result;
	}

	public Player createPlayer(InputStream stream, String type)
			throws IOException, MediaException
	{
		long signature;
		ContentPlayer result;
		if(stream == null)
		{
			throw new IllegalArgumentException("Manager.createPlayer: " +
					"параметр stream равен нулевой ссылке.");
		}
		signature = continueReadSignature(0L, stream, 4);
		if(type == null)
		{
			if(signature == WaveDecoder.RIFF_SIGNATURE)
			{
				result = new SampledPlayer(new WaveDecoder(), CONTENT_TYPE_AUDIO_WAVE);
			}
			else if(continueReadSignature(signature, stream, 4) == MIDIDecoder.MIDI_SIGNATURE)
			{
				result = new SyntheticPlayer(new MIDIDecoder(), CONTENT_TYPE_AUDIO_MIDI);
			}
			else
			{
				throw new MediaException("Manager.createPlayer: " +
						"не удалось распознать тип содержимого.");
			}
		}
		else if(signature == WaveDecoder.RIFF_SIGNATURE &&
				CONTENT_TYPE_AUDIO_WAVE.equalsIgnoreCase(type))
		{
			result = new SampledPlayer(new WaveDecoder(), CONTENT_TYPE_AUDIO_WAVE);
		}
		else if(continueReadSignature(signature, stream, 4) == MIDIDecoder.MIDI_SIGNATURE &&
				CONTENT_TYPE_AUDIO_MIDI.equalsIgnoreCase(type))
		{
			result = new SyntheticPlayer(new MIDIDecoder(), CONTENT_TYPE_AUDIO_MIDI);
		}
		else
		{
			throw new MediaException("Manager.createPlayer: " +
					"неизвестный тип содержимого " + type);
		}
		result.getData(stream);
		return result;
	}

	public Player createPlayer(String locator)
			throws IOException, MediaException
	{
		int len;
		InputStream stream;
		if(locator == null)
		{
			throw new IllegalArgumentException("Manager.createPlayer: " +
					"параметр locator равен нулевой ссылке.");
		}
		if(PROTOCOL_RESOURCE_SIGNATURE.regionMatches(true, 0, locator, 0,
				len = PROTOCOL_RESOURCE_SIGNATURE.length()))
		{
			stream = getClass().getResourceAsStream(locator.substring(len - 1));
		}
		else
		{
			throw new ConnectionNotFoundException("Manager.createPlayer: " +
					"протокол не поддерживается.");
		}
		if(stream == null)
		{
			throw new ConnectionNotFoundException("Manager.createPlayer: " +
					"соединение " + locator + " не найдено.");
		}
		return createPlayer(stream, null);
	}
}
