(function ($) {

	// Strict Mode for JS
	"use strict";



	TM.forceHTML5Audio = true;



	/**
	 * Sound
	 .d8888b.                              888 
	d88P  Y88b                             888 
	Y88b.                                  888 
	 "Y888b.   .d88b. 888  88888888b.  .d88888 
	    "Y88b.d88""88b888  888888 "88bd88" 888 
	      "888888  888888  888888  888888  888 
	Y88b  d88PY88..88PY88b 888888  888Y88b 888 
	 "Y8888P"  "Y88P"  "Y88888888  888 "Y88888 
	**/
	TM.Sound = function () {
		this.init.apply(this, arguments);
	};

	$.extend(TM.Sound.prototype, {
		id: null,
		howlSound: null,

		paths: null,

		loadDeferred: null,

		playID: null,

		playing: false,
		paused: false,

		init: function init(id, paths, options) {
			var howlOptions = {
					urls: paths,

					buffer: TM.forceHTML5Audio,

					onload: function () {
						console.info('sound loaded ' + this.id);////

						this.loadDeferred.resolve();
					}.use(this),

					onloaderror: function () {
						// Unloading seems to cause an onloaderror for HTML5 audio
						if (this.loadDeferred.state() == 'resolved') {
							return;
						}

						console.error('sound load error ' + this.id);////
						console.log(arguments);

						this.loadDeferred.reject();
					}.use(this),

					onend: function () {
						// HTML5 on Android, this fires while the audio is still playing...
						// If playing == false however, then our pause() doesn't work, and we need that with Android/HTML5 in order to make the audio wait for other things to load.
						if (!TM.soundManager.isUsingWebAudio()) {
							return;
						}

						this.playing = false;
						this.paused = false;
					}.use(this),

					onpause: function () {
						this.playing = false;
						this.paused = true;
					}.use(this)
				};

			this.paths = paths;

			options = options || {};

			this.loadDeferred = $.Deferred();

			this.id = id;

			$.extend(howlOptions, options);

			this.howlSound = new Howl(howlOptions);
		}, // init()

		unload: function unload() {
			if (!this.howlSound) {
				return;
			}

			this.howlSound.unload();
			this.howlSound = null;

			console.info('UNLOADED Audio: ' + this.id);////
		}, // unload()

		play: function play() {
			var playDeferred = $.Deferred();

			// NOTE: With HTML5 Audio we're gonna go ahead and play() even if it isn't loaded... sometimes it won't be loaded with mobile/Android. (Howler handles this appropriately internally anyway.)
			if (!this.howlSound || (!this.howlSound._loaded && TM.soundManager.isUsingWebAudio())) {
				console.error('Sound ' + this.id + ' has not loaded.'); ////

				playDeferred.reject();

				return playDeferred;
			}

			this.howlSound.play(function (playID) {
				this.playing = true;
				this.paused = false;

				this.playID = playID;

				console.log('Sound ' + this.id + ' is playing!');////
				playDeferred.resolve();
			}.use(this));

			return playDeferred;
		}, // play()

		pause: function pause() {
			if (this.howlSound && this.playing) {
				this.howlSound.pause(this.playID);
			}
		}, // pause()

		resume: function resume() {
			if (this.howlSound && this.paused) {
				return this.play(this.playID);
			}

			if (this.howlSound && this.playing) {
				return $.Deferred().resolve();
			}

			return $.Deferred().reject();
		}, // resume()

		stop: function stop() {
			this.howlSound && this.howlSound.stop();

			this.playID = null;
		}, // stop()

		position: function position(pos) {
			var playing = this.playing,
				paused = this.paused;

			this.howlSound && this.howlSound.pos(pos / 1000, this.playID);

			// howl.pos() seems to fire howl's onpause function
			// So we need to restore some of our state vars

			this.playing = playing;
			this.paused = paused;
		} // position()
	}); // TM.Sound


	/**
	 * SoundMgr
	 .d8888b.                              888888b     d888                
	d88P  Y88b                             8888888b   d8888                
	Y88b.                                  88888888b.d88888                
	 "Y888b.   .d88b. 888  88888888b.  .d88888888Y88888P888 .d88b. 888d888 
	    "Y88b.d88""88b888  888888 "88bd88" 888888 Y888P 888d88P"88b888P"   
	      "888888  888888  888888  888888  888888  Y8P  888888  888888     
	Y88b  d88PY88..88PY88b 888888  888Y88b 888888   "   888Y88b 888888     
	 "Y8888P"  "Y88P"  "Y88888888  888 "Y88888888       888 "Y88888888     
	                                                            888        
	                                                       Y8b d88P        
	                                                        "Y88P"         
	**/
	TM.soundManager = {
		sounds: {},

		currentSound: null,
		currentBGM: null,

		init: function () {

		}, // init()

		// Note that preload() handles re-add/duplication correctly
		// See also unload()
		add: function (id, options) {
			var paths = [
					AUDIOPATH + id + '.mp3',
					AUDIOPATH + id + '.ogg'
				];

			console.log('add sound: ' + id);////

			if (options && options.paths) {
				paths = options.paths;
			}

			this.sounds[id] = new TM.Sound(id, paths, options);

			return this.sounds[id].loadDeferred;
		}, // add()

		//// TODO: This check wouldn't really be correct if I'm setting individual audio files to fallback HTML5 Audio via the buffer option of Howler
		isUsingWebAudio: function isUsingWebAudio() {
			return (!TM.forceHTML5Audio && Howler.usingWebAudio);
		}, // isUsingWebAudio()

		isCurrentSound: function (id) {
			return (this.currentSound && this.currentSound.id == id);
		}, // isCurrentSound()

		play: function (id, pos) {
			var df;

			console.log('PLAY SOUND: ' + id + (pos ? ' at ' + pos : ''));////

			this.currentSound && this.currentSound.stop();

			// For HTML5 Audio, create the sound on demand... With mobile/Android, sometimes we can't load or play the sound unless that's both happening from a UI event handler (like a click) ... so we can't do things in advance.
			if (!this.isUsingWebAudio() && !this.sounds[id]) {
				this.add(id);
			}

			this.currentSound = this.sounds[id];

			df = this.currentSound.play();

			if (pos) {
				return df.then(function () {
						this.currentSound.position(pos);
					}.use(this));
			}

			// Hack
			df.then(function () {
				window.audio_node = this.currentSound
								&& this.currentSound.howlSound 
								&& this.currentSound.howlSound._audioNode[0];
			}.use(this));

			return df;
		}, // play()

		playIfNot: function playIfNot(id, pos) {
			if (!this.currentSound || this.currentSound.id != id) {
				return this.play(id, pos);
			}

			return $.Deferred().resolve();
		}, // playIfNot()

		pauseIfCurrent: function pause(id) {
			if (this.currentSound && this.currentSound.id == id) {
				this.currentSound.pause();
			}
		}, // pause()

		resumeIfCurrent: function resume(id) {
			if (this.currentSound && this.currentSound.id == id) {
				this.currentSound.resume();
			}
		}, // resume()

		bgm: function (id, pos) {
			var df;

			console.log('PLAY BGM: ' + id + (pos ? ' at ' + pos : ''));////

			this.currentBGM && this.currentBGM.stop();

			// For HTML5 Audio, create the sound on demand... With mobile/Android, sometimes we can't load or play the sound unless that's both happening from a UI event handler (like a click) ... so we can't do things in advance.
			if (!this.isUsingWebAudio() && !this.sounds[id]) {
				this.add(id);
			}

			this.currentBGM = this.sounds[id];

			df = this.currentBGM.play();

			if (pos) {
				return df.then(function () {
						this.currentBGM.position(pos);
					}.use(this));
			}

			return df;
		}, // bgm()

		stopBgm: function () {
			this.currentBGM && this.currentBGM.stop();

			this.currentBGM = null;
		},

		pauseAll: function pause() {
			this.currentSound && this.currentSound.pause();
		}, // pause()

		resumeAll: function resume() {
			this.currentSound && this.currentSound.resume();
		}, // resume()

		preload: function (ids) {
			var predf,
				preloadDeferreds = [
					// $.Deferred(function () {var df = this; setTimeout(function () {df.resolve();}, 2000);}),//// Here to create extra delays for testing
					$.Deferred().resolve()
				];

			// For HTML5 Audio, we will create the sound on demand... With mobile/Android, sometimes we can't even load the sound unless that's happening in a UI event handler (like a click)
			if (!this.isUsingWebAudio()) {
				return $.when.apply($, preloadDeferreds);
			}

			_.each(ids, function (id) {
				if (this.sounds[id]) {
					preloadDeferreds.push(this.sounds[id].loadDeferred);

				} else {
					predf = this.add(id);

					preloadDeferreds.push(predf);
				}
			}.use(this));

			return $.when.apply($, preloadDeferreds);
		}, // preload()

		stopAndClear: function stopAndClear() {
			this.currentSound && this.currentSound.stop();

			this.currentSound = null;
		}, // stopAndClear()

		unload: function unload(id) {
			if (this.currentSound && this.currentSound.id == id) {
				throw 'Tried to unload current sound!!!';
				return;
			}

			this.sounds[id] && this.sounds[id].unload();

			this.sounds[id] = null;
		} // unload()
	}; // TM.soundManager

}(jQuery));