package audio;

import java.util.HashMap;

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;

public class AudioPlayer implements Runnable {
	
	public static HashMap<String, AudioPlayer> sfx = new HashMap<String, AudioPlayer>();

	private Clip clip;
	private FloatControl gainControl;

	// basic volume
	public static final float MAX_VOLUME = 6.0206f;
	public static final float MIN_VOLUME = -80;
	private float volume = MAX_VOLUME;

	// fading
	private float currDB = 0F;
	private float targetDB = 0F;
	private float fadePerStep = .1F;
	private boolean fading = false;

	public void setFadePerStep(float fadePerStep) {
		this.fadePerStep = fadePerStep;
	}

	public AudioPlayer(String s, boolean loop) {

		try {

			AudioInputStream ais = AudioSystem.getAudioInputStream(getClass().getResource(s));
			AudioFormat baseFormat = ais.getFormat();
			AudioFormat decodeFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, baseFormat.getSampleRate(), 16, baseFormat.getChannels(), baseFormat.getChannels() * 2, baseFormat.getSampleRate(), false);
			AudioInputStream dais = AudioSystem.getAudioInputStream(decodeFormat, ais);
			clip = AudioSystem.getClip();
			clip.open(dais);
			if (loop) {
				clip.loop(Clip.LOOP_CONTINUOUSLY);
			}

			gainControl = (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN);
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	public void play(boolean loop) {
		if (clip == null)
			return;
		stop();
		clip.setFramePosition(0);
		if (loop) {
			clip.loop(Clip.LOOP_CONTINUOUSLY);
		}
	}

	public void play() {
		if (clip == null)
			return;
		stop();
		clip.setFramePosition(0);
		clip.start();
	}

	public void setVolume(double value) {
		value = (value <= 0.0) ? 0.0001 : ((value > 1.0) ? 1.0 : value);
		try {
			float dB = (float) (Math.log(value) / Math.log(10.0) * 20.0);
			gainControl.setValue(dB);
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}

	public void shiftVolumeTo(double value) {
		// value is between 0 and 1
		value = (value <= 0.0) ? 0.0001 : ((value > 1.0) ? 1.0 : value);
		targetDB = (float) (Math.log(value) / Math.log(10.0) * 20.0);

		if (!fading) {
			Thread t = new Thread(this); // start a thread to fade volume
			t.start(); // calls run() below
		}
	}

	public void changeVolume(float volume) {
		this.volume += volume;
		gainControl.setValue(this.volume);
	}

	public void stop() {
		if (clip.isRunning())
			clip.stop();
	}

	public void close() {
		stop();
		clip.close();
	}

	@Override
	public void run() {
		fading = true; // prevent running twice on same sound
		if (currDB > targetDB) {
			while (currDB > targetDB) {
				currDB -= fadePerStep;
				try {
					gainControl.setValue(currDB);
				} catch (Exception e1) {
					e1.printStackTrace();
				}
				try {
					Thread.sleep(10);
				} catch (Exception e) {
				}
			}
		}

		if (currDB < targetDB) {
			while (currDB < targetDB) {
				currDB += fadePerStep;
				gainControl.setValue(currDB);
				try {
					Thread.sleep(10);
				} catch (Exception e) {
				}
			}
		}
		fading = false;
		currDB = targetDB; // now sound is at this volume level
	}

}
