dojo.provide("dojox.av.FLAudio");
dojo.experimental("dojox.av.FLAudio");
dojo.require("dojox.embed.Flash");
dojo.require("dojox.timing.doLater");


dojo.declare("dojox.av.FLAudio", null, {

	// summary:
	//		Play MP3 files through the Flash SWF built in the
	//		DEFT project.
	// description:
	//		This class is brand new, so there is a lot of
	//		functionality not yet available. The initial
	//		purpose is for playing "event" sounds like button
	//		clicks, and for loading and controlling multiple
	//		sounds at once. As of yet, streaming is not supported
	//		and polling the sounds for events during playback
	//		may still be missing information. Markup is not
	//		supported, as it may not be needed.
	//
	//	TODO:
	//		Streaming, playback events, crossdomain, CDN support,
	//		(alternate SWF location), global volume, ID3 tag,
	//		factor out doLater, onLoadStatus needs work,
	//		play(position) / seek()
	//
	// example:
	//		|	new dojox.av.FLAudio({
	//		|		initialVolume:.7,
	//		|		initialPan:0,
	//		|		autoPlay:false
	//		|	});
	//
	//  id: String?
	//		The id of this widget and the id of the SWF movie.
	id:"",
	//
	//	initialVolume: Number
	//		From 0-1
	//		Sets volume for all files unless changed with doPlay
	//		or setVolume
	initialVolume: 0.7,
	//
	//	initialPan: Number
	//		From -1 to 1 (-1 is left, 1 is right, 0 is middle)
	//		Sets pan for all files unless changed with play
	//		or setPan
	initialPan: 0,
	//
	//	autoPlay: Boolean
	//		If true, all files will play upon load. If false,
	//		they load and wait for doPlay() command.
	//
	// isDebug: Boolean?
	//		Setting to true tells the SWF to output log messages to Firebug.
	isDebug: false,
	//
	//	statusInterval: Number
	//		How often in milliseconds that the status of the
	//		player is checked - both load and play
	statusInterval:200,
	//
	// _swfPath: Uri
	//		The path to the video player SWF resource
	_swfPath: dojo.moduleUrl("dojox.av", "resources/audio.swf"),
	//
	//
	// allowScriptAccess: String
	//		Whether the SWF can access the container JS
	allowScriptAccess:"always",
	//
	// allowNetworking: String
	//		Whether SWF is restricted to a domain
	allowNetworking: "all",
	//

	constructor: function(/*Object*/options){

		// Provide this function for the SWF to ensure that the it is playing
		// in HTML.
		dojo.global.swfIsInHTML = function(){ return true; }

		dojo.mixin(this, options || {});
		if(!this.id){ this.id = "flaudio_"+new Date().getTime(); }
		this.domNode = dojo.doc.createElement("div");
		dojo.style(this.domNode, {
			position:"relative",
			width:"1px",
			height:"1px",
			top:"1px",
			left:"1px"
		});
		dojo.body().appendChild(this.domNode);
		this.init();
	},

	init: function(){
		// summary:
		// Initialize the media.
		//
		//
		this._subs = [];
		this.initialVolume = this._normalizeVolume(this.initialVolume);

		var args = {
			path:this._swfPath.uri,
			width:"1px",
			height:"1px",
			minimumVersion:9, // this may need to be 10, not sure
			expressInstall:true,
			params:{
				wmode:"transparent",
				allowScriptAccess:this.allowScriptAccess,
				allowNetworking:this.allowNetworking
			},
			// only pass in simple variables - no deep objects
			vars:{
				id:this.id,
				autoPlay:this.autoPlay,
				initialVolume:this.initialVolume,
				initialPan:this.initialPan,
				statusInterval:this.statusInterval,
				isDebug:this.isDebug
			}
		};

		this._sub("mediaError",    "onError");
		this._sub("filesProgress", "onLoadStatus");
		this._sub("filesAllLoaded", "onAllLoaded");
		this._sub("mediaPosition", "onPlayStatus");
		this._sub("mediaEnd", "onComplete");
		this._sub("mediaMeta",     "onID3");

		this._flashObject = new dojox.embed.Flash(args, this.domNode);
		this._flashObject.onError = function(err){
			console.warn("Flash Error:", err);
		};
		this._flashObject.onLoad = dojo.hitch(this, function(mov){
			this.flashMedia = mov;
			this.isPlaying = this.autoPlay;
			this.isStopped = !this.autoPlay;
			this.onLoad(this.flashMedia);
		});
	},

	//  ==============  //
	//  Loading Files   //
	//  ==============  //

	load: function(/*Object*/options){
		// summary:
		//		Adds a media object to the playlist
		//		***This can be called repeatedly to add multiple items.
		//	options: Object
		//		url: String
		//			(required) path to MP3 media
		//			url must be absolute or relative to SWF,
		//			not dojo or the html. An effort will be made
		//			to fix incorrect paths.
		//		id: String
		//			(optional) an identifier to later determine
		//			which media to control.
		//	returns:
		//		The normalized url, which can be used to identify the
		//		audio.
		//
		if(dojox.timing.doLater(this.flashMedia, this)){ return false; }
		if(!options.url){
			throw new Error("An url is required for loading media");
			return false;
		}else{
			options.url = this._normalizeUrl(options.url);
		}
		this.flashMedia.load(options);

		return options.url; // String
	},

	//  =============================  //
	//  Methods to control the sound   //
	//  =============================  //

	doPlay: function(/*Object*/options){
		// summary:
		//		Tell media to play, based on
		//		the options passed.
		// options: Object
		//		volume: Number
		//			Sets the volume
		//		pan: Number
		//			Sets left/right pan
		//		index:Number OR id:String OR url:String
		//			Choose one of the above to indentify
		//			the media you wish to control. id is
		//			set by you. index is the order in which
		//			media was added (zero based)
		//			NOTE: lack of an identifier will default
		//			to first (or only) item.
		//	NOTE: Can't name this method "play()" as it causes
		//			an IE error.
		this.flashMedia.doPlay(options);
	},

	pause: function(/*Object*/options){
		// summary:
		//		Tell media to pause, based on identifier in
		//		the options passed.
		// options: Object
		//		index:Number OR id:String OR url:String
		//			See doPlay()
		//
		this.flashMedia.pause(options);
	},

	stop: function(/*Object*/options){
		// summary:
		//		Tell media to stop, based on identifier in
		//		the options passed.
		// options:
		//		index:Number OR id:String OR url:String
		//			See doPlay()
		//
		this.flashMedia.doStop(options);
	},

	setVolume: function(/*Object*/options){
		// summary:
		//		Set media volume, based on identifier in
		//		the options passed.
		// options:
		//		volume: Number
		//		0 to 1
		//		index:Number OR id:String OR url:String
		//			See doPlay()
		//
		this.flashMedia.setVolume(options);
	},

	setPan: function(/*Object*/options){
		// summary:
		//		Set media pan, based on identifier in
		//		the options passed.
		// options:
		//		pan:Number
		//			-1 to 1
		//		index:Number OR id:String OR url:String
		//			See doPlay()
		//
		this.flashMedia.setPan(options);
	},

	getVolume: function(/*Object*/options){
		// summary:
		//		Get media volume, based on identifier in
		//		the options passed.
		// options:
		//		index:Number OR id:String OR url:String
		//			See doPlay()
		//
		return this.flashMedia.getVolume(options);
	},

	getPan: function(/*Object*/options){
		// summary:
		//		Set media pan, based on identifier in
		//		the options passed.
		// options:
		//		index:Number OR id:String OR url:String
		//			See doPlay()
		//
		return this.flashMedia.getPan(options);
	},

	getPosition: function(/*Object*/options){
		// summary:
		//		Get the current time.
		// options:
		//		index:Number OR id:String OR url:String
		//			See doPlay()
		//
		return this.flashMedia.getPosition(options);
	},

	//  =============  //
	//  Sound Events   //
	//  =============  //
	onError: function(msg){
		// summary:
		//		stub fired when an error occurs
		console.warn("SWF ERROR:", msg)
	},

	onLoadStatus: function(/*Array*/events){
		// summary:
	},

	onAllLoaded: function(){
		// summary:
		//		stub fired
	},

	onPlayStatus: function(/*Array*/events){
		// summary:
	},

	onComplete: function(/*Array*/events){
		// summary:
		//		Fired at the end of a media file.
	},

	onLoad: function(){
		// summary:
		//		stub fired when SWF is ready
	},
	onID3: function(evt){
		//	summary:
		//		Fired when the ID3 data is received.
	},



	destroy: function(){
		// summary:
		// 		destroys flash
		if(!this.flashMedia){
			this._cons.push(dojo.connect(this, "onLoad", this, "destroy"));
			return;
		}
		dojo.forEach(this._subs, function(s){
			dojo.unsubscribe(s);
		});
		dojo.forEach(this._cons, function(c){
			dojo.disconnect(c);
		});
		this._flashObject.destroy();
		//dojo._destroyElement(this.flashDiv);
	},



	_sub: function(topic, method){
		// summary:
		// helper for subscribing to topics
		dojo.subscribe(this.id+"/"+topic, this, method);
	},

	_normalizeVolume: function(vol){
		// summary:
		//		Ensures volume is less than one
		//
		if(vol>1){
			while(vol>1){
				vol*=.1
			}
		}
		return vol;
	},

	_normalizeUrl: function(_url){
		// summary:
		//		Checks that path is relative to HTML file or
		//		convertes it to an absolute path.
		//
		if(_url && _url.toLowerCase().indexOf("http")<0){
			//
			// Appears to be a relative path. Attempt to  convert it to absolute,
			// so it will better target the SWF.
			var loc = window.location.href.split("/");
			loc.pop();
			loc = loc.join("/")+"/";

			_url = loc+_url;
		}
		return _url;
	}

});
