package gameCanvasUtil;

import java.io.*;

import javax.sound.midi.*;

/** midiを管理し、再生などを行う
 *
 */
public class midiManager  implements soundManagerInterface{
	// コンストラクタで指定されたファイル名を保持
	private String fileName;

	//音を鳴らすシーケンサー
	private Sequencer sequencer[];
	private Synthesizer synthesizer = null;

	//音ごとにループするかのフラグ
	private boolean loop_flag[];

	//音ごとの音量
	private int volume[];


	//サウンドの数
	private static final int MIDI_MAX_NUM = 64;
	//MIDIの仕様で、サウンド音量変更命令
	private static final int CHANGE_VOLUME = 7;

	/** 画像枚数などよきに計らえ **/
	public static final int NUMBER_AUTO_CULC = -1;
	/** 画像リストをテキストから読み込みやがれ */
	public static final int READ_LIST_FROM_TEXT = -2;

	/** コンストラクタ
	 * @param file 読み込む連番MIDIファイルを指定
	 * @param number MIDI数の指定。
	 * 何ファイルになるかわからない場合は"NUMBER_AUTO_CULC"を指定するとよきに計らいます。<br />
	 * また READ_LIST_FROM_TEXT を指定すると連番ファイル読み込みではなく、引数"file"を<br />
	 * MIDIのリストが書いてあるテキストファイルとして読み込み、そのリストに従ってMIDIを読み込みます。<br />
	 * (リストは一行に1ファイルずつ書いていく感じです。)
	 */
	public midiManager(String file,int number){
		this.fileName = file;
		if(number==NUMBER_AUTO_CULC){
			sequencer = new Sequencer[MIDI_MAX_NUM];
			loop_flag = new boolean[MIDI_MAX_NUM];
			volume = new int[MIDI_MAX_NUM];
			this.loadNumberingMidi(false);

		}else if(number==READ_LIST_FROM_TEXT){
			String list[] = saveDataUtil.readResourceStrings(file);
			sequencer = new Sequencer[list.length];
			loop_flag = new boolean[list.length];
			volume = new int[number];
			for(int i=0;i<list.length;i++){
				sequencer[i] = loadMidi(list[i]);
			}
		}else{
			sequencer = new Sequencer[number];
			loop_flag = new boolean[number];
			volume = new int[number];
			this.loadNumberingMidi(true);
		}
		initSynthesizer();
	}


	/** 連番MIDIをロードする
	 * @param skip 読み込めなかった場合スキップするか？
	 * これがfalseだと、読み込めないと、そこで終了する。
	 */
	private void loadNumberingMidi(boolean skip){
		for(int i=0;i<MIDI_MAX_NUM;i++){
			this.sequencer[i] = loadMidi(this.fileName +i+".mid");
			if(this.sequencer[i]==null && !skip)
				break;
		}
	}

	/** MIDIをロードする
	 * @param id MIDIのIDを指定する
	 * @return 読み込んだMIDIオブジェクトを返す
	 */
	private Sequencer loadMidi(String file){
		Sequencer tmp;
		try{
			tmp = MidiSystem.getSequencer();
			tmp.open();
			InputStream is=null;
			is = this.getClass().getResourceAsStream("/"+file);
			Sequence s = MidiSystem.getSequence(is);
			tmp.setSequence(s);
			is.close();
			return tmp;
		}catch(Exception e){
			System.out.println( "loadMidi "+file +" "+e);
			return null;
		}
	}

	/** MIDIを再生する
	 * @param id MIDIのIDを指定
	 * @param flag ループを行うかをセット。行うならtrue。
	 */
	public void playSound(int id,boolean flag){
		try{
			this.loop_flag[id] = flag;
			sequencer[id].start();
		}catch(Exception e){
			e.printStackTrace();
		}
	}

	/** midiを一時停止する
	 * @param id 停止させるMIDIを指定する
	 */
	public void pauseSound(int id){
		try{
			if(this.sequencer[id]!=null)
				this.sequencer[id].stop();
		}catch(Exception e){
		}
	}

	/** MIDIを停止させる
	 * @param id 停止させるMIDIを指定する
	 */
	public void stopSound(int id){
		try{
			if(this.sequencer[id]!=null){
				this.sequencer[id].stop();
				this.sequencer[id].setTickPosition(0);
			}
		}catch(Exception e){
		}
	}

	/** MIDIの音量をセット
	 * @param id MIDIのID指定
	 * @param vol 音量を指定(0-100)
	 */
	public void changeVolume(int id,int vol){
		if(vol < 0){
			vol = 0;
		}else if(vol > 127){
			vol = 127;
		}
		volume[id] = (vol * 127) / 100;
	}

	/** MIDIの音量をセット
	 * @param id MIDIのID指定
	 * @param vol 音量を指定(0-100)
	 */
	private void setVolume(int id,int vol){
		if(sequencer[id]==null){
			return;
		}
		int midiVolume = vol;
		try{
			if( synthesizer.getDefaultSoundbank() == null ){
				// HARDWARE SYNTHESIZER
				try{
					ShortMessage volumeMessage = new ShortMessage();
					for( int i = 0; i < 16; i++ ){
						volumeMessage.setMessage( ShortMessage.CONTROL_CHANGE,
												  i, CHANGE_VOLUME, midiVolume );
						MidiSystem.getReceiver().send( volumeMessage, -1 );
					}
				}
				catch( InvalidMidiDataException imde ){
					System.err.println( "Invalid MIDI data." );
					return;
				}
				catch( MidiUnavailableException mue ){
					System.err.println( "MIDI unavailable." );
					return;
				}
			}
			else{
				// SOFTWARE SYNTHESIZER:
				MidiChannel[] channels = synthesizer.getChannels();
				for( int c = 0; channels != null && c < channels.length; c++ ){
					channels[c].controlChange( CHANGE_VOLUME, midiVolume );
				}
			}
		}catch(Exception e){
			e.printStackTrace();
		}
	}


	/** サウンドの更新処理
	 */
	public void update(){
		for(int i=0;i<sequencer.length;i++){
			try{
				setVolume(i,volume[i]);
				if(sequencer[i]!=null && sequencer[i].getTickLength() <= sequencer[i].getTickPosition() ){
					this.sequencer[i].stop();
					this.sequencer[i].setTickPosition(0);
					this.sequencer[i].start();
				}
			}catch(Exception e){
			}
		}
	}

	/** 全てのサウンドを一時停止する
	 */
	public void pauseSound(){
		for(int i=0;i<this.sequencer.length;i++){
			pauseSound(i);
		}
	}

	/** 全てのサウンドを停止する
	 */
	public void stopSound(){
		for(int i=0;i<this.sequencer.length;i++){
			stopSound(i);
		}
	}
	/** 全てのサウンドの音量変更
	 */
	public void changeVolume(int vol){
		for(int i=0;i<this.sequencer.length;i++){
			this.changeVolume(i, vol);
		}
	}

	/** シンセサイザーの初期化
	 */
	private void initSynthesizer(){
		try{
			if (!(sequencer[0] instanceof Synthesizer)){
				synthesizer = MidiSystem.getSynthesizer();
				synthesizer.open();
				if( synthesizer.getDefaultSoundbank() == null )	{
					// HARDWARE SYNTHESIZER
					for(int i=0;i<sequencer.length;i++){
						sequencer[i].getTransmitter().setReceiver(
							MidiSystem.getReceiver() );
					}
				}
				else{
					// SOFTWARE SYNTHESIZER
					for(int i=0;i<sequencer.length;i++){
						sequencer[i].getTransmitter().setReceiver(
							synthesizer.getReceiver() );
					}
				}
			}else{
				synthesizer = (Synthesizer) sequencer[0];
			}
		}catch(Exception e){
		}
	}

}
