package itdelatrisu.opsu.audio;

import java.io.IOException;
import java.util.LinkedList;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.LineUnavailableException;

//http://stackoverflow.com/questions/1854616/in-java-how-can-i-play-the-same-audio-clip-multiple-times-simultaneously
public class MultiClip {
	/** A list of clips used for this audio sample */
	LinkedList<Clip> clips = new LinkedList<Clip>();
	
	/** The format of this audio sample */
	AudioFormat format;
	
	/** The data for this audio sample */
	byte[] audioData;
	
	/** The name given to this clip */
	String name;
	
	/** Size of a single buffer */
	final int BUFFER_SIZE = 0x1000;
	
	/** Constructor  */
	public MultiClip(String name, AudioInputStream audioIn) throws IOException, LineUnavailableException {
		this.name = name;
		if(audioIn != null){
			format = audioIn.getFormat();
			
			LinkedList<byte[]> allBufs = new LinkedList<byte[]>();
			
			int readed = 0;
			boolean hasData = true;
			while (hasData) {
				readed = 0;
				byte[] tbuf = new byte[BUFFER_SIZE];
				while (readed < tbuf.length) {
					int read = audioIn.read(tbuf, readed, tbuf.length - readed);
					if (read < 0) {
						hasData = false;
						break;
					}
					readed += read;
				}
				allBufs.add(tbuf);
			}
			
			audioData = new byte[(allBufs.size() - 1) * BUFFER_SIZE + readed];
			
			int cnt = 0;
			for (byte[] tbuf : allBufs) {
				int size = BUFFER_SIZE;
				if (cnt == allBufs.size() - 1) {
					size = readed;
				}
				System.arraycopy(tbuf, 0, audioData, BUFFER_SIZE * cnt, size);
				cnt++;
			}
		}
		getClip();
	}
	
	/**
	 * Returns the name of the clip
	 * @return the name
	 */
	public String getName() {
		return name;
	}
	
	/**
	 * Plays the clip with the specified volume.
	 * @param volume the volume the play at
	 * @throws IOException 
	 * @throws LineUnavailableException 
	 */
	public void start(float volume) throws LineUnavailableException, IOException {
		Clip clip = getClip();
		
		// PulseAudio does not support Master Gain
		if (clip.isControlSupported(FloatControl.Type.MASTER_GAIN)) {
			// set volume
			FloatControl gainControl = (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN);
			float dB = (float) (Math.log(volume) / Math.log(10.0) * 20.0);
			gainControl.setValue(dB);
		}
		
		clip.setFramePosition(0);
		clip.start();
	}
	/**
	 * Returns a Clip that is not playing from the list
	 * if one is not available a new one is created
	 * @return the Clip
	 */
	private Clip getClip() throws LineUnavailableException, IOException{
		for(Clip c : clips){
			if(!c.isRunning()){
				return c;
			}
		}
		Clip t = AudioSystem.getClip();
		if (format != null)
			t.open(format, audioData, 0, audioData.length);
		clips.add(t);
		return t;
	}
}