package jongs;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.HashMap;

import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MetaEventListener;
import javax.sound.midi.MetaMessage;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Receiver;
import javax.sound.midi.Sequence;
import javax.sound.midi.Sequencer;
import javax.sound.midi.Synthesizer;
import javax.sound.midi.Transmitter;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.DataLine.Info;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineListener;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;

import java.applet.Applet;
import java.applet.AudioClip;


// 
// JONGS - Java Open Novels Game Script
// 
//    JongsSound.java 
//
//  Copyright 2007  DENZI TOMIZKA
//
//  ver 1.00 Create by DENZI TOMIZKA 2007.5.25
//
/*
 *-----------------------------------------------------------------------
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *----------------------------------------------------------------------
 */


public class jongsSound implements MetaEventListener , LineListener  {
    private static final int MIDI_END = 47;
    private static final int MIDI_MAX = 128;
    private static final int AUDIO_MAX = 128 ;
    private static final int CLIP_MAX = 128 ;
    
    
	Sequencer MIDIsequencer;
	Synthesizer MIDIsynthesizer;
	Sequence MIDIsequence ;	
	
	HashMap <String,Sequence> MIDIFiles;
	HashMap <String,Clip> CLIPFiles;
	HashMap <String,AudioClip> AUDIOFiles;
	public jongsSound() {
		// Midiシーケンサーを初期化する
		this.initSequencer();
		MIDIFiles = new HashMap <String,Sequence>(MIDI_MAX);
		AUDIOFiles = new HashMap <String,AudioClip>(AUDIO_MAX);
		CLIPFiles = new HashMap <String,Clip>(CLIP_MAX);
	}
	
	/**
	 * Midiシーケンサーを初期化する
	 *
	 */
	private int initSequencer() {
		try {
			MIDIsequencer = MidiSystem.getSequencer();
			MIDIsequencer.open();
			MIDIsequencer.addMetaEventListener(this);
			if (!(MIDIsequencer instanceof Synthesizer)) {
				MIDIsynthesizer = MidiSystem.getSynthesizer();
				MIDIsynthesizer.open();
				Receiver synthReceiver = MIDIsynthesizer.getReceiver();
				Transmitter seqTransmitter = MIDIsequencer.getTransmitter();
				seqTransmitter.setReceiver(synthReceiver);
			} else { 
				MIDIsynthesizer = (Synthesizer) MIDIsequencer;
			}
		} catch (MidiUnavailableException mue) {
			//mue.printStackTrace();
			//Error(8,"このコンピューターシステムではMIDIを演奏できません。");
			return 8;
		}
		return 0;
	}
			
	
	public void meta(MetaMessage mm) {
		if (mm.getType() == MIDI_END) {
			if (MIDIsequencer != null && MIDIsequencer.isOpen()) {
				MIDIsequencer.setMicrosecondPosition(0);
				MIDIsequencer.start();
			}
		}
	}

	public int MIDIload(String filename) {
		if ( this.MIDIFiles.size() >= MIDI_MAX ) return 8;
		if ( this.MIDIFiles.containsKey(filename) ) {
			this.MIDIsequence = MIDIFiles.get(filename);			
			MIDIstop();
			return 0;
		}		
		File MidiFile = new File("./"+filename);
		try {
			this.MIDIsequence = MidiSystem.getSequence(MidiFile);
		} catch (InvalidMidiDataException e) {
			return 1;
			//Error(8,"このMIDIを開くことができません。1");
		} catch (IOException e) {
			return 2;
			//Error(8,"このMIDIを開くことができません。2");
		} catch (NullPointerException e) {
			return 3;
			//Error(8,"このMIDIを開くことができません。3");
		}
		MIDIstop();
		return 0;
	}

	
	
	
	
	public int CLIPload(String filename) {
		Clip clip ;
		if ( this.CLIPFiles.size() >= CLIP_MAX ) return 8;
		if ( this.CLIPFiles.containsKey(filename) ) return 0;
		clip = this.getClip(filename);
		if ( clip == null ) return 8;
		return 0;
	}
	
	
	public int CLIPLoop(String filename) {
		Clip clip ;
		if ( this.CLIPFiles.containsKey(filename) ) {
			clip = (Clip) this.CLIPFiles.get(filename);
			clip.start();
			return 0;
		} else {
			if ( this.CLIPFiles.size() >= CLIP_MAX ) return 8;
			clip = this.getClip(filename);
			if ( clip == null ) return 8;
			clip.start();
			return 0;
		}
		
	}


	private Clip getClip(String filename) {
		File audioFile = new File("./"+filename);
        Clip clip;
        AudioInputStream aistr;
        AudioFormat aform , newform ;
        Info dlinfo;
        try {
            aistr = AudioSystem.getAudioInputStream(audioFile.toURI().toURL());
            aform = aistr.getFormat();
            if ( ( aform.getEncoding() == AudioFormat.Encoding.ULAW) 
            || ( aform.getEncoding() == AudioFormat.Encoding.ALAW ) ) {
                newform = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,aform.getSampleRate(),aform.getSampleSizeInBits()*2 , aform.getChannels(),aform.getFrameSize()*2, aform.getFrameRate(), true);
                aistr = AudioSystem.getAudioInputStream(newform, aistr);
                aform = newform;
            }

            dlinfo = new DataLine.Info(Clip.class,aform);
            clip = (Clip) AudioSystem.getLine(dlinfo);
            clip.addLineListener(this);
            clip.open(aistr);
            aistr.close();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        } catch (UnsupportedAudioFileException e) {
            e.printStackTrace();
            return null;
        } catch (LineUnavailableException e) {
            e.printStackTrace();
            return null;
        }

		this.CLIPFiles.put( filename , clip)	;
		return clip;
	}
	
	public void CLIPStop(String filename) {
		Clip clip ;
		if ( this.CLIPFiles.containsKey(filename) ) {
			clip = (Clip) this.CLIPFiles.get(filename);
			clip.stop();
		}		
	}
	
	
	public int MIDIplay (String filename) {
		int retc = this.MIDIload(filename);
		if ( retc == 0 ) {
			this.Midiplay();
			return 0;
		} else {
			return 8;
		}
	}
	

	public int Midiplay() {
		if ( this.MIDIsequence == null ) return 8 ;
		return this.MIDIplay( this.MIDIsequence);
	}

	public int  MIDIplay(Sequence seq) {
		MIDIstop();
		if (MIDIsequencer != null && seq != null) {
			try {
				MIDIsequencer.setSequence(seq);
				MIDIsequencer.start();
			} catch (InvalidMidiDataException imd) {
				return 8 ;
				//Error(8,"このMIDIを演奏できません。");
			}
			return 0;
		}
		return 8 ;
	}

	
	public void MIDIstop() {
		if (MIDIsequencer == null) return ;
		if (MIDIsequencer.isRunning()) MIDIsequencer.stop();
	}

	public void MIDIclose() {
		MIDIstop();
		if (MIDIsequencer != null) {
			MIDIsequencer.removeMetaEventListener(this);
			MIDIsequencer.close();
		}
		MIDIsequencer = null;
	}
	
	/**
	 * Clip Loop再生用
	 */
    public void update(LineEvent le) {
    	Clip clip;
        if ( le.getType() == LineEvent.Type.STOP ) {
            clip = (Clip) le.getSource();
            clip.stop();
            clip.setFramePosition(0);
        }
    }

	
	public int AUDIOload(String filename) {
		AudioClip clip ;
		if ( this.AUDIOFiles.size() >= AUDIO_MAX ) return 8;
		if ( this.AUDIOFiles.containsKey(filename) ) return 0;
		clip = this.getAudioClip(filename);
		if ( clip == null ) return 8;
		return 0;
	}
	
	
	public int AUDIOPlay(String filename) {
		AudioClip clip ;
		if ( this.AUDIOFiles.containsKey(filename) ) {
			clip = (AudioClip) this.AUDIOFiles.get(filename);
			clip.play();
			return 0;
		} else {
			if ( this.AUDIOFiles.size() >= AUDIO_MAX ) return 8;
			clip = this.getAudioClip(filename);
			if ( clip == null ) return 8;
			clip.play();
			return 0;
		}
		
	}

	public int AUDIOLoop(String filename) {
		AudioClip clip ;
		if ( this.AUDIOFiles.containsKey(filename) ) {
			clip = (AudioClip) this.AUDIOFiles.get(filename);
			clip.loop();
			return 0;
		} else {
			clip = this.getAudioClip(filename);
			if ( clip == null ) return 8;
			clip.loop();
			return 0;
		}
	}
	
	private AudioClip getAudioClip(String filename) {
		File audioFile = new File("./"+filename);
		AudioClip clip ;	
		try {
			clip = Applet.newAudioClip(audioFile.toURI().toURL());				
		} catch (NullPointerException ne) {
			return null;
		} catch (MalformedURLException mue) {
			return null;
		}
		this.AUDIOFiles.put(filename,clip)	;
		return clip;
	}
	
	

	public void AUDIOStop(String filename) {
		AudioClip clip ;
		if ( this.AUDIOFiles.containsKey(filename) ) {
			clip = (AudioClip) this.AUDIOFiles.get(filename);
			clip.stop();
		}		
	}
	



}
