Engine.SoundPlayer = class 
{
	
	get soundVolume()
	{
		return this._soundVolume;
	}
	set soundVolume(value)
	{
		this._soundVolume = Math.max(0, Math.min(value, 1));
		for (var clip of Object.values(this.clips))
        {
            if(clip != null && clip != this.bgMusic)
            {
                clip.volume = this._soundVolume;
            }
        }
	}

	get musicVolume()
	{
		return this._musicVolume;
	}
	set musicVolume(value)
	{
        this._musicVolume = Math.max(0, Math.min(value, 1));
        if (this.bgMusic != null) this.bgMusic.volume = this._musicVolume;
	}

	constructor()
	{
		this._soundVolume = 1;
		this._musicVolume = 1;
		this.enabled = false;
		this.idTable = {};//new Dictionary<int, ISoundEffect>();
		this.lastSoundPlayTime = {};//new Dictionary<string, long>();
		this.bgMusic = null;
		this.volumeTween = new Engine.SimpleTween();
		this.currentPlayingMusic = -1;
		
		this.players = [];//new List<GameObject>();
		this.clips = {};//new Dictionary<string,AudioClip>();
	}

	initialize()
	{
		this.enabled = true;

		if (this.bgMusic == null) return;
		else
		{
			this.bgMusic.play({"loop": true})
			this.bgMusic.volume = this.musicVolume;
		}
	}

	playSound(id /*int*/, volumeScale /*float*/, oneShot /*bool*/)
	{			
		if (volumeScale === undefined) volumeScale = 0.5;
		if (oneShot === undefined) oneShot = true;

		if (!this.enabled || this.soundVolume <= 0) return null;
		if (!this.idTable.hasOwnProperty(id)) return null;
		
		var playVolume = Math.clamp(this.idTable[id].volume, 0, 1) * volumeScale;
		this.getClipID(id, false, (clip) => this.loadedPlaySoundName(clip, oneShot, playVolume));
	}

	loadedPlaySoundName(clip, oneShot, volume)
	{
		if (!this.enabled || clip == null || !clip.isLoaded) return null;

		if (!this.shouldPlaySound(clip.url)) return;
		this.lastSoundPlayTime[clip.url] = Engine.Utils.getTimer();

		clip.play({"loop": !oneShot});
		clip.volume = this.soundVolume * volume;
	}

	shouldPlaySound(name /*string*/)
	{
		if (!this.lastSoundPlayTime.hasOwnProperty(name))
		{
			this.lastSoundPlayTime[name] = 0;
		}
		var now = Engine.Utils.getTimer();
		var last = this.lastSoundPlayTime[name];
		return (now - last) > 100;
	}

	playBGSound(id /*int*/)
	{
		this.getClipID(id, true, (bgMusic) => this.loadedPlayBGSound(bgMusic));
	}

	loadedPlayBGSound(bgMusic)
	{
		this.bgMusic = bgMusic;
		if (!this.enabled || this.bgMusic == null || !this.bgMusic.isLoaded) return null;
		
		this.bgMusic.play({"loop": true})
		this.bgMusic.volume = this.musicVolume;
	}

	changeBGMusic(id /*int*/, timeToFadeOut /*float*/)
	{
		if (timeToFadeOut === undefined) timeToFadeOut = 1;

		if (id == this.currentPlayingMusic) return this.bgMusic;
		this.currentPlayingMusic = id;
		
		if (this.bgMusic == null)
		{
			this.playBGSound(id);
			this.fadeInBGSound(id, timeToFadeOut);
		}
		else
		{
			this.fadeOutSound(this.bgMusic, timeToFadeOut, () => { this.fadeInBGSound(id, timeToFadeOut); });
		}
		
		return this.bgMusic;
	}

	fadeOutSound(clip /*AudioSource*/, timeToFadeOut /*float*/, onFinish /*Action*/)
	{
		if (timeToFadeOut === undefined) timeToFadeOut = 3;
		if (onFinish === undefined) onFinish = null;

		if (clip == null || !clip.isPlaying)
		{
			onFinish();
			return;
		}
		this.volumeTween.stop();
		this.volumeTween.init((volume) => clip.volume = volume, Engine.SimpleTween.easeLinear, clip.volume, 0, timeToFadeOut);
		this.volumeTween.onFinish = () => { if (onFinish != null) onFinish(); };
	}

	fadeInBGSound(id /*int*/, timeToFadeIn /*float*/, onFinish /*Action*/)
	{
		if (timeToFadeIn === undefined) timeToFadeIn = 3;
		if (onFinish === undefined) onFinish = null;

		if (this.bgMusic == null)
		{
			this.playBGSound(id);
			if (this.bgMusic == null) return null;
		}
		
		this.bgMusic.volume = 0;
		
		this.bgMusic.stop();
		this.playBGSound(id);
		this.volumeTween.stop();
		this.volumeTween.init((volume) => this.bgMusic.volume = volume, Engine.SimpleTween.easeLinear, 0, this.musicVolume, timeToFadeIn);
		this.volumeTween.onFinish = () => { if (onFinish != null) onFinish(); };
		
		return this.bgMusic;
	}

	getClipID(id /*int*/, isMusic, playCallback)
	{
		if (isMusic === undefined) isMusic = false;
		if (playCallback === undefined) playCallback = null;

		var name = "";
		if (this.idTable.hasOwnProperty(id)) name = this.idTable[id].name;
		
		this.getClipName(name, isMusic, playCallback);
	}

	getClipName(name /*string*/, isMusic, playCallback)
	{	
		if (isMusic === undefined) isMusic = false;
		if (playCallback === undefined) playCallback = null;

		var clip = null;
		
		if (this.clips.hasOwnProperty(name)) 
		{
			playCallback(this.clips[name]);
		}
		else
		{
			this.clips[name] = null;
			PIXI.sound.Sound.from({
			    url: EngineSettings.LocalAssetRoot + "sounds/" + name + (isMusic ? ".mp3" : ".wav"),
			    preload: true,
			    loaded: (err, sound) => this.clipLoaded(err, sound, name, playCallback),
				singleInstance: true,
			});
		}
	}

	clipLoaded(err, sound, name, playCallback)
	{
		if (err == null)
		{
			this.clips[name] = sound;
			playCallback(sound);
		}
	}
}