/*
 * "Peko" Visual Novel System
 *
 * All Rights Reserved.
 * Copyright (c) 1999-2003 Tsukuba Bunko.
 *
 * $Id: AudioPlayer.java,v 1.1.2.1 2003/12/11 10:23:10 ppoi Exp $
 */
package tsukuba_bunko.peko.canvas.stage;

import	java.io.Serializable;

import	java.net.URL;

import	java.util.Map;

import	tsukuba_bunko.peko.Logger;

import	tsukuba_bunko.peko.resource.ResourceManager;

import	tsukuba_bunko.peko.canvas.stage.audio.MP3AudioClip;
import	tsukuba_bunko.peko.canvas.stage.audio.SampledAudioClip;

//	テスト用
import	java.io.File;


/**
 * 音楽クリップの再生を行います。
 * @author	$Author: ppoi $
 * @version	$Revision: 1.1.2.1 $
 */
public class AudioPlayer	implements Serializable	{

	/**
	 * クリップマップ
	 */
	transient private Map	_clips = new java.util.HashMap( 17 );

	/**
	 * BGM registry
	 */
	private Map	_bgmRegistry = new java.util.HashMap( 17 );

	/**
	 * SE registry
	 */
	private Map	_seRegistry = new java.util.HashMap( 17 );


	/**
	 * <code>AudioPlayer</code> のインスタンスを作成します。
	 */
	public AudioPlayer()
	{
		super();
	}


	/**
	 * BGM を再生します。
	 * @param	id	BGM ID
	 * @param	clipName	クリップ名
	 * @param	loop	ループ再生する場合 <code>true</code>、しない場合 <code>false</code>
	 */
	public void playBGM( String id, String clipName, boolean loop )
	{
		ResourceManager	resources = ResourceManager.getInstance();
		try	{
			if( _clips.containsKey(id) )	{
				Logger.error( "[canvas.stage] specified bgm clip id was already used. :" + id );
				return;
			}
			_bgmRegistry.put( id, new Object[]{clipName, (loop?Boolean.TRUE:Boolean.FALSE)} );
			URL	clipURL = new URL( resources.getLocationResources().getSoundsDirecotryURL(), clipName );
			playClip( id, clipURL, loop );
		}
		catch( Exception e )	{
			Logger.error( "[canvas.stage] fail to play bgm :" + clipName, e );
		}
	}

	/**
	 * SE(Sound Effect) を再生します。
	 * @param	id	BGM ID
	 * @param	clipName	クリップ名
	 * @param	loop	ループ再生する場合 <code>true</code>、しない場合 <code>false</code>
	 */
	public void playSE( String id, String clipName, boolean loop )
	{
		ResourceManager	resources = ResourceManager.getInstance();
		try	{
			if( _clips.containsKey(id) )	{
				Logger.error( "[canvas.stage] specified se clip id was already used. :" + id );
				return;
			}
			_seRegistry.put( id, new Object[]{clipName, (loop?Boolean.TRUE:Boolean.FALSE)} );
			URL	clipURL = new URL( resources.getLocationResources().getSoundsDirecotryURL(), clipName );
			playClip( id, clipURL, loop );
		}
		catch( Exception e )	{
			Logger.error( "[canvas.stage] fail to play bgm :" + clipName, e );
		}
	}

	/**
	 * クリップを再生します。
	 * @param	id	クリップ ID
	 * @param	clipURL	クリップの URL
	 * @param	loop	ループ再生する場合 <code>true</code>、しない場合 <code>false</code>
	 */
	protected void playClip( String id, URL clipURL, boolean loop )
	{
		try	{
			AudioClip	clip = getAudioClip( id, clipURL );
			if( clip != null )	{
				register( clip );
				clip.play( loop );
			}
		}
		catch( Exception e )	{
			Logger.error( "[canvas.stage] fail to play clip. :" + clipURL, e );
		}
	}

	/**
	 * クリップを登録します。
	 * @param	clip	登録するクリップ
	 */
	protected void register( AudioClip clip )
	{
		clip.setAudioPlayer( this );
		_clips.put( clip.getID(), clip );
		Logger.debug( "[canvas.stage] registered clip :" + clip.getID() );
	}

	/**
	 * クリップの登録を解除します。
	 * @param	clip	登録解除するクリップ
	 */
	protected void unregister( AudioClip clip )
	{
		_clips.remove( clip.getID() );
		_bgmRegistry.remove( clip.getID() );
		_seRegistry.remove( clip.getID() );
		Logger.debug( "[canvas.stage] unregistered clip :" + clip.getID() );
	}

	/**
	 * 指定されたクリップの再生を停止します。
	 * @param	id	クリップ ID
	 */
	public void stop( String id )
	{
		AudioClip	clip = (AudioClip)_clips.get( id );
		if( clip != null )	{
			clip.stop();
		}
	}

	/**
	 * 全クリップの再生を停止します。
	 */
	public void stopAll()
	{
		AudioClip[]	clips = (AudioClip[])_clips.values().toArray( new AudioClip[_clips.size()] );
		for( int i = 0; i < clips.length; ++i )	{
			clips[i].stop();
		}
	}

	/**
	 * クリップを再生する AudioClip のインスタンスを作成します。
	 * @param	id	クリップ ID
	 * @param	clipURL	クリップの URL
	 */
	protected AudioClip getAudioClip( String id, URL clipURL )
	{
		String	clipName = clipURL.getFile().toLowerCase();
		if( clipName.endsWith(".mid") || clipName.equals(".smf") )	{
			Logger.error( "[canvas.stage] unsupported format :" + clipURL );
			return null;
		}
		else if( clipName.endsWith( "mp3") )	{
			return new MP3AudioClip( id, clipURL );
		}
		else if( clipName.endsWith(".wav") || clipName.endsWith(".au") )	{
			return new SampledAudioClip( id, clipURL );
		}
		else	{
			Logger.error( "[canvas.stage] unsupported format :" + clipURL );
			return null;
		}
	}


	/**
	 * 再生の準備を実行します。
	 */
	public void prepare()
	{
		if( _clips == null )	{
			Logger.debug( "[cavnas.stage] initialize clips." );
			_clips = new java.util.HashMap( 17 );
		}

		String[]	id = null;
		Object[]	clip = null;

		Map	registry = _bgmRegistry;
		if( registry.size() > 0 )	{
			id = (String[])registry.keySet().toArray( new String[registry.size()] );
			for( int i = 0; i < id.length; ++i )	{
				clip = (Object[])registry.get( id[i] );
				playBGM( id[i], (String)clip[0], ((Boolean)clip[1]).booleanValue() );
			}
		}

		registry = _seRegistry;
		if( registry.size() > 0 )	{
			id = (String[])registry.keySet().toArray( new String[registry.size()] );
			for( int i = 0; i < id.length; ++i )	{
				clip = (Object[])registry.get( id[i] );
				playSE( id[i], (String)clip[0], ((Boolean)clip[1]).booleanValue() );
			}
		}
	}

//
//	デバッグ用
//
	public static void main( String[] args )
		throws Exception
	{
		Logger.prepare();
		if( args.length < 1 )	{
			System.err.println( "にょわー" );
			return;
		}

		File	target = new File( args[0] );
		if( !target.exists() )	{
			System.err.println( "ねぇぞごらぁ！" );
			return;
		}

		boolean	loop = false;
		if( args.length >= 2 )	{
			loop = args[1].equals("loop");
		}

		long	span = -1L;
		if( args.length >= 3 )	{
			span = Long.parseLong( args[2] );
		}

		AudioClip	clip = new tsukuba_bunko.peko.canvas.stage.audio.SampledAudioClip( "hoge", target.toURL() );
		clip.play( loop );

		if( span > 0 )	{
			synchronized( clip )	{
				clip.wait( span );
			}
			clip.stop();
		}
	}
}
