// handle instance counters
MooPlayerCounter = 0;

MooPlayerFromFlash = function(instance, arg1, arg2){
	try {
		mp = document.id('mooplayer_instance_'+instance).retrieve('mooplayer');
		var args = [];
		$each(arguments, function(el, i){ 
			if (i >= 2) args.push(el);
		});
		return mp[arg1].run(args, mp);
	} catch(e){
		return false;
	}
};

var MooPlayer = new Class({

	Implements: [Options, Events],
	
	// default options
	options: {
		cssPrefix: "mooplayer-",
		swfPath: "js",
		volume: 80,
		oggSupport: false,
		nativeSupport: true,
		preload: 'none',
		graphicsFix: true,
		errorAlerts: false,
		warningAlerts: false
	},
	
	// config
	config: {
		version: "1.2.0",
		swfVersionRequired: "1.2.0",
		swfVersion: "unknown",
		mooPlayerControllerId: undefined,
		delayedCommandId: undefined,
		isWaitingForPlay:false,
		isFileSet:false
	},
	
	initialize: function(element, options){
	
		// set up self
		var self = this;
			
		// mootoolsize element
		this.element = document.id(element);
		
		// set options
		this.setOptions(options);
		
		// unique identifier
		this.id = MooPlayerCounter++;
		
		// diagnostics
		this.diag = {
			isPlaying: false,
			src: "",
			loadPercent: 0,
			playedPercentRelative: 0,
			playedPercentAbsolute: 0,
			playedTime: 0,
			totalTime: 0
		};
		
		// extend config
		$extend(this.config, {
			swf: this.options.swfPath + ((this.options.swfPath != "" && this.options.swfPath.slice(-1) != "/") ? "/" : "") + "Jplayer.swf",
			fid: this.options.cssPrefix + "flash-" + this.id,
			aid: this.options.cssPrefix + "audio-" + this.id,
			hid: this.options.cssPrefix + "force-" + this.id,
			i: this.id,
			volume: this._limitValue(this.options.volume, 0, 100),
			autobuffer: this.options.preload != 'none'

		});
		
		// create an audio element
		this.config.audio = document.createElement('audio');
		this.config.audio.id = this.config.aid;
		
		// create some booleans
		$extend(this.config, {
			canPlayMP3: !!((this.config.audio.canPlayType) ? (("" != this.config.audio.canPlayType("audio/mpeg")) && ("no" != this.config.audio.canPlayType("audio/mpeg"))) : false),
			canPlayOGG: !!((this.config.audio.canPlayType) ? (("" != this.config.audio.canPlayType("audio/ogg")) && ("no" != this.config.audio.canPlayType("audio/ogg"))) : false)
		});

		$extend(this.config, {
			html5: !!((this.config.oggSupport) ? ((this.config.canPlayOGG) ? true : this.config.canPlayMP3) : this.config.canPlayMP3)
		});

		$extend(this.config, {
			usingFlash: !(this.config.html5 && this.options.nativeSupport),
			usingMP3: !(this.options.oggSupport && this.config.canPlayOGG && this.options.nativeSupport)
		});
							
		// classes we look for
		this.cssClasses = {
			play: "play",
			pause: "pause",
			stop: "stop",
			loadBar: "load-bar",
			playBar: "play-bar",
			volumeMin: "volume-min",
			volumeMax: "volume-max",
			volumeBar: "volume-bar",
			volumeBarValue: "volume-bar-value"
		};
	
		// set up css selectors
		self.cssSelectors = {};
		self.cssDisplay = {};

		// reference our css selectors
		$each(this.cssClasses, function(v, n){
				
			// mootoolize
			self.cssSelectors[n] = self.element.getElement('.' + self.options.cssPrefix + v);
									
			// add event handlers
			if (self.cssSelectors[n]){
				
				// add click event
				self.cssSelectors[n].addEvent('click', function(ev){
					if (ev) ev.stop();
/* 					console.log(n); */
					self[n].run([ev], self);
				});
			
			};
			
			
			
		});
		
		// set up our player div
		this.player = new Element('div', { id: 'mooplayer_instance_' + this.id }).addClass(this.options.cssPrefix + 'playerdiv').setStyles({ 'position': 'absolute', 'left':-999, 'top':-999 });
						
		// shared events
		var events = {
				
			setButtons: function(playing) {
						
				self.diag.isPlaying = playing;
				if(self.cssSelectors.play != undefined && self.cssSelectors.pause != undefined) {
					if(playing) {
						self.cssSelectors.play.setStyle("display", "none");
						self.cssSelectors.pause.setStyle("display", "inline");
					} else {
						self.cssSelectors.play.setStyle("display", "inline");
						self.cssSelectors.pause.setStyle("display", "none");
					}
				}
				if(playing) {
					self.config.isWaitingForPlay = false;
				}
				
			}
		};
								
		// flash or not?
		if (this.config.usingFlash){
		
			if (this._checkForFlash(8)){
				/*if (Browser.Engine.trident){
					var html_obj = '<object id="' + this.config.fid + '"';
					html_obj += ' classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"';
					html_obj += ' codebase="' + document.URL.substring(0,document.URL.indexOf(':')) + '://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab"'; // Fixed IE non secured element warning.
					html_obj += ' type="application/x-shockwave-flash"';
					html_obj += ' width="' + this.config.width + '" height="' + this.config.height + '">';
					html_obj += '</object>';
		
					var obj_param = new Array();
					obj_param[0] = '<param name="movie" value="' + this.config.swf + '" />';
					obj_param[1] = '<param name="quality" value="high" />';
					obj_param[2] = '<param name="FlashVars" value="id=' + escape(this.id) + '&fid=' + escape(this.config.fid) + '&vol=' + this.options.volume + '" />';
					obj_param[3] = '<param name="allowScriptAccess" value="always" />';
			
					var ie_dom = document.createElement(html_obj);
					for(var i=0; i < obj_param.length; i++) {
						ie_dom.appendChild(document.createElement(obj_param[i]));
					}
					this.player.adopt(ie_dom);
				} else {
					var html_embed = '<embed name="' + this.config.fid + '" id="' + this.config.fid + '" src="' + this.config.swf + '"';
					html_embed += ' width="10" height="10"';
					html_embed += ' quality="high" FlashVars="id=' + escape(this.id) + '&fid=' + escape(this.config.fid) + '&vol=' + this.options.volume + '"';
					html_embed += ' allowScriptAccess="always"';
					html_embed += ' type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" />';
					this.player.set('html', html_embed);
				}*/
				this.swiff = new Swiff(
					this.config.swf, { 
						id: this.config.fid, 
						vars: {
							id: escape(this.id),
							fid: escape(this.config.fid),
							vol: this.options.volume
						}
					});
					
				this.player.adopt(this.swiff);
								
				// extend events
				$extend(events, {
				
					setFile: function(mp3, ogg) {
						//try {
							//self._getMovie().CallFunction('<invoke name="fl_setFile_mp3" returntype="javascript">' + __flash__argumentsToXML(mp3) + '</invoke>');
							//if (!self) self = this;
							self.swiff.remote('fl_setFile_mp3', [mp3]);
							if(self.config.autobuffer) {
								self.element.fireEvent("load");
							}
							self.diag.src = mp3;
							self.config.isFileSet = true; // Set here for conformity, but the flash handles this internally and through return values.
							self.element.fireEvent("setButtons", [false]);
						//} catch(err) { self._flashError(err); }
					},
					
					clearFile: function(e) {
						try {
							var self = this;
							self.element.fireEvent("setButtons", [false]); // Before flash method so states correct for when onProgressChange is called
							self.swiff.remote('fl_clearFile_mp3');
							self.diag.src = "";
							self.config.isFileSet = false;
						} catch(err) { self._flashError(err); }
					},
					
					load: function(e) {
						//try {
							self.swiff.remote('fl_load_mp3');
						//} catch(err) { self._flashError(err); }
					},
					
					play: function(e) {
						//try {
							if(self.swiff.remote('fl_play_mp3')) {
								self.element.fireEvent("setButtons", [true], 10);
							}
						//} catch(err) { self._flashError(err); }
					},
					
					pause: function(e) {
						try {
							if(self.swiff.remote('fl_pause_mp3')) {
								self.element.fireEvent("setButtons", [false], 10);
							}
						} catch(err) { self._flashError(err); }
					},
					
					stop: function(e) {
						try {
							if(self.swiff.remote('fl_stop_mp3')) {
								self.element.fireEvent("setButtons", [false], 10);
							}
						} catch(err) { self._flashError(err); }
					},
					
					playHead: function(p) {
						try {
							if(self.swiff.remote('fl_play_head_mp3', [p])) {
								self.element.fireEvent("setButtons", [true], 10);
							}
						} catch(err) { self._flashError(err); }
					},
					
					playHeadTime: function(e, t) {
						try {
							if(self.swiff.remote('fl_play_head_time_mp3', [t])) {
								self.element.fireEvent("setButtons", [true], 10);
							}
						} catch(err) { self._flashError(err); }
					},
					
					volume: function(e, v) {
						self.config.volume = v;
						try {
							self.swiff.remote('fl_volume_mp3', v);
						} catch(err) { self._flashError(err); }
					}
				});
		
			} else {
				this.player.set('html', "<p>Flash 8 or above is not installed. <a href='http://get.adobe.com/flashplayer'>Get Flash!</a></p>");
			}
		} else {
			this.config.audio.autobuffer = this.config.autobuffer;
			this.config.audio.preload = this.options.preload;
			this.config.audio.addEventListener("canplay", function() {
				var rnd = 0.1 * Math.random(); // Fix for Chrome 4: Fix volume being set multiple times before playing bug.
				var fix = (self.config.volume < 50) ? rnd : -rnd; // Fix for Chrome 4: Solves volume change before play bug. (When new vol == old vol Chrome 4 does nothing!)
				self.config.audio.volume = (self.config.volume + fix)/100; // Fix for Chrome 4: Event solves initial volume not being set correctly.
			}, false);
			this.config.audio.addEventListener("ended", function() {
				clearInterval(self.config.mooPlayerControllerId);
				self.onSoundComplete();
			}, false);
			this.player.adopt(this.config.audio);
						
			// add events
			$extend(events, {
			
				setFile: function(mp3, ogg) {
					self = this;
					if(self.config.usingMP3) {
						self.diag.src = mp3;
					} else { 
						self.diag.src = ogg;
					}
					if(self.config.isFileSet  && !self.config.isWaitingForPlay) {
						self.element.fireEvent("pause");
					}
					self.config.audio.autobuffer = self.config.autobuffer; // In case not preloading, but used a mooPlayer("load")
					self.config.audio.preload = self.options.preload; // In case not preloading, but used a mooPlayer("load")
					if(self.config.autobuffer) {
						self.config.audio.src = self.diag.src;
						self.config.audio.load();
					} else {
						self.config.isWaitingForPlay = true;
					}
					self.config.isFileSet = true;
					self.mooPlayerOnProgressChange(0, 0, 0, 0, 0);
					clearInterval(self.config.mooPlayerControllerId);
					if(self.config.autobuffer) {
						self.config.mooPlayerControllerId = window.setInterval( function() {
							self.mooPlayerController(false);
						}, 100);
					}
					clearInterval(self.config.delayedCommandId);
				},
				
				clearFile: function(e) {
					self = this;
					self.setFile("","");
					self.config.isWaitingForPlay = false;
					self.config.isFileSet = false;
				},
				
				load: function(e) {
					self = this;
					if(self.config.isFileSet) {
						if(self.config.isWaitingForPlay) {
							self.config.audio.autobuffer = true;
							self.config.audio.preload = 'auto';
							self.config.audio.src = self.diag.src;
							self.config.audio.load();
							self.config.isWaitingForPlay = false;
							clearInterval(self.config.mooPlayerControllerId);
							self.config.mooPlayerControllerId = window.setInterval( function() {
								self.mooPlayerController(false);
							}, 100);
						}
					}
										
				},
				
				play: function(e) {
					self = this;
					if(self.config.isFileSet) {
						if(self.config.isWaitingForPlay) {
							self.config.audio.src = self.diag.src;
							self.config.audio.load();
						}
						self.config.audio.play();
						self.element.fireEvent("setButtons", [true], 10);
						clearInterval(self.config.mooPlayerControllerId);
						self.config.mooPlayerControllerId = window.setInterval( function() {
							self.mooPlayerController(false);
						}, 100);
						clearInterval(self.config.delayedCommandId);
					}
				},
				
				pause: function(e) {
					self = this;
					if(self.config.isFileSet) {
						self.config.audio.pause();
						self.element.fireEvent("setButtons", [false], 10);
						clearInterval(self.config.delayedCommandId);
					}
				},
				
				stop: function(e) {
					self = this;
					if(self.config.isFileSet) {
						try {
							self.element.fireEvent("pause");
							self.config.audio.currentTime = 0;
							clearInterval(self.config.mooPlayerControllerId);
							self.config.mooPlayerControllerId = window.setInterval( function() {
								self.mooPlayerController(true); // With override true
							}, 100);

						} catch(err) {
							clearInterval(self.config.delayedCommandId);
							self.config.delayedCommandId = window.setTimeout(function() {
								self.stop();
							}, 100);
						}
					}
				},
				
				playHead: function(p) {
					self = this;
					if(self.config.isFileSet) {
						try {
							self.element.fireEvent("load");
							if((typeof self.config.audio.buffered == "object") && (self.config.audio.buffered.length > 0)) {
								self.config.audio.currentTime = p * self.config.audio.buffered.end(self.config.audio.buffered.length-1) / 100;
							} else if(self.config.audio.duration > 0 && !isNaN(self.config.audio.duration)) {
								self.config.audio.currentTime = p * self.config.audio.duration / 100;
							} else {
								throw "e";
							}
							self.element.fireEvent("play");
						} catch(err) {
							self.element.fireEvent("play"); // Fixes a problem on the iPad with multiple instances
							self.element.fireEvent("pause"); // Also clears delayedCommandId interval.
							self.config.delayedCommandId = window.setTimeout(function() {
								self.playHead(p);
							}, 100);
						}
					}
				},
				
				playHeadTime: function(t) {
					self = this;
					if(self.config.isFileSet) {
						try {
							self.element.fireEvent("load");
							self.config.audio.currentTime = t/1000;
							self.element.fireEvent("play");
						} catch(err) {
							self.element.fireEvent("play"); // Fixes a problem on the iPad with multiple instances
							self.element.fireEvent("pause"); // Also clears delayedCommandId interval.
							self.config.delayedCommandId = window.setTimeout(function() {
								self.playHeadTime(t);
							}, 100);
						}
					}
				},
				
				volume: function(v) {
					self = this;
					self.config.volume = v;
					self.config.audio.volume = v/100;
					self.mooPlayerVolume(v);
				}
				
			});
			
		}
		
		// add events
		for (var event in events){
			this.element.addEvent(event, events[event].bindWithEvent(self));
		}
		
		// adopt our player
		this.element.adopt(this.player);
		
		// graphics fix
		if(this.options.graphicsFix) {
		
			var html_hidden = new Element('div', { id: this.config.hid, html: '&nbsp;' });
			this.element.adopt(html_hidden);
		
			$extend(this.config, {
				hSel: html_hidden
			});
			
			this.config.hSel.setStyles({ 'text-indent':'-9999px' });
		}
		
		// store
		this.player.store('mooplayer', this);
				
		if(!this.config.usingFlash) { // Emulate initial flash call after 100ms
			(function() {
				this.volume(this.options.volume);
				this.mooPlayerReady();
			}.delay(100, self));
		}

	},
	
	// limit value
	_limitValue: function(value, min, max){
		return (value < min) ? min : ((value > max) ? max : value);
	},
	
	// check for flash
	_checkForFlash: function(version){
		// Function checkForFlash adapted from FlashReplace by Robert Nyman
		// http://code.google.com/p/flashreplace/
		var flashIsInstalled = false;
		var flash;
		if(window.ActiveXObject){
			try{
				flash = new ActiveXObject(("ShockwaveFlash.ShockwaveFlash." + version));
				flashIsInstalled = true;
			}
			catch(e){
				// Throws an error if the version isn't available			
			}
		}
		else if(navigator.plugins && navigator.mimeTypes.length > 0){
			flash = navigator.plugins["Shockwave Flash"];
			if(flash){
				var flashVersion = navigator.plugins["Shockwave Flash"].description.replace(/.*\s(\d+\.\d+).*/, "$1");
				if(flashVersion >= version){
					flashIsInstalled = true;
				}
			}
		}
		return flashIsInstalled;
	},
	
	// set file
	setFile: function(mp3, ogg) {
		this.element.fireEvent("setFile", [mp3, ogg]);
	},
	
	clearFile: function() {
		this.element.fireEvent("clearFile");
	},
	load: function(ev) {
		this.element.fireEvent("load");
	},
	play: function() {
		$(document.body).getElements('.mooplayer-single-player').fireEvent('stop');
		this.element.fireEvent("play");
	},
	pause: function() {
		this.element.fireEvent("pause");
	},
	stop: function() {
		this.element.fireEvent("stop");
	},
	playHead: function(p) {
		this.element.fireEvent("playHead", [p]);
	},
	playHeadTime: function(t) {
		this.element.fireEvent("playHeadTime", [t]);
	},
	volume: function(v) {
		v = this._limitValue(v, 0, 100);
		this.element.fireEvent("volume", [v]);
	},
	loadBar: function(e) { // Handles clicks on the loadBar
		if (this.cssSelectors.loadBar != undefined){
			var offset = this.cssSelectors.loadBar.getCoordinates();
			var x = e.page.x - offset.left;
			var w = this.cssSelectors.loadBar.getSize().x;
			var p = 100*x/w;
			this.playHead(p);
		}
	},
	playBar: function(e) { // Handles clicks on the playBar
		this.loadBar(e);
	},
	onProgressChange: function(fn) {
		if($typeof(fn) == 'function') {
			this.onProgressChangeCustom = fn;
		} else {
			this._warning("onProgressChange parameter is not a function.");
		}
	},
	onProgressChangeCustom: function() {
		// Replaced in onProgressChange()
	},
	volumeMin: function() {
		this.volume(0);
	},
	volumeMax: function() {
		this.volume(100);
	},
	volumeBar: function(e) { // Handles clicks on the volumeBar
		if( this.config.cssId.volumeBar != undefined ) {
			var offset = this.config.cssSelector.volumeBar.offset();
			var x = e.pageX - offset.left;
			var w = this.config.cssSelector.volumeBar.width();
			var p = 100*x/w;
			this.volume(p);
		}
	},
	volumeBarValue: function(e) { // Handles clicks on the volumeBarValue
		this.volumeBar(e);
	},
	onSoundComplete: function(fn) {
		if(typeof(fn) == 'function') {
			this.onSoundCompleteCustom = fn;
		} else {
			this._warning("onSoundComplete parameter is not a function.");
		}
	},
	onSoundCompleteCustom: function() {
		// Replaced in onSoundComplete()
	},
	mooPlayerOnSoundComplete: function() { // Called from Flash / HTML5 interval
		console.log('complete');
		this.element.fireEvent("setButtons", [false]);
		this.onSoundCompleteCustom();
	},
	getData: function(name) {
		var n = name.split(".");
		var p = this.config;
		for(var i = 0; i < n.length; i++) {
			if(p[n[i]] != undefined) {
				p = p[n[i]];
			} else {
				this._warning("Undefined data requested.\n\nmooPlayer('getData', '" + name + "')");
				return undefined;
			}
		}
		return p;
	},
	_getMovie: function() {
		return document[this.config.fid];
	},
	_forceUpdate: function() { // For Safari and Chrome
		if(this.config.graphicsFix) {
			this.config.hSel.text(""+Math.random());
		}
	},
	_flashError: function(e) {
		this._error("Problem with Flash component.\n\nCheck the swfPath points at the Jplayer.swf path.\n\nswfPath = " + this.config.swfPath + "\nurl: " + this.config.swf + "\n\nError: " + e.message);
	},
	_error: function(msg) {
		if(this.config.errorAlerts) {
			this._alert("Error!\n\n" + msg);
		}
	},
	_warning: function(msg) {
		if(this.config.warningAlerts) {
			this._alert("Warning!\n\n" + msg);
		}
	},
	_alert: function(msg) {
		alert("mooPlayer " + this.config.version + " : id='" + this.id +"' : " + msg);
	},
	
	// these are called by flash
	mooPlayerOnProgressChange: function(lp, ppr, ppa, pt, tt) { // Called from Flash / HTML5 interval
		this.diag.loadPercent = lp;
		this.diag.playedPercentRelative = ppr;
		this.diag.playedPercentAbsolute = ppa;
		this.diag.playedTime = pt;
		this.diag.totalTime = tt;
				
		if (this.cssSelectors.loadBar != undefined) {
			this.cssSelectors.loadBar.setStyle('width', lp+"%");
		}
		if (this.cssSelectors.playBar != undefined ) {
			this.cssSelectors.playBar.setStyle('width', ppr+"%");
		}
		
		if (ppr >= 99.9){
			this.onSoundCompleteCustom();
		}

		//this.onProgressChangeCustom(lp, ppr, ppa, pt, tt);
		this._forceUpdate();
	},
	
	mooPlayerController: function(override) { // The HTML5 interval function.
		var pt = 0, tt = 0, ppa = 0, lp = 0, ppr = 0;
		if(this.config.audio.readyState >= 1) {
			pt = this.config.audio.currentTime * 1000; // milliSeconds
			tt = this.config.audio.duration * 1000; // milliSeconds
			tt = isNaN(tt) ? 0 : tt; // Clean up duration in Firefox 3.5+
			ppa = (tt > 0) ? 100 * pt / tt : 0;
			if((typeof this.config.audio.buffered == "object") && (this.config.audio.buffered.length > 0)) {
				lp = 100 * this.config.audio.buffered.end(this.config.audio.buffered.length-1) / this.config.audio.duration;
				ppr = 100 * this.config.audio.currentTime / this.config.audio.buffered.end(this.config.audio.buffered.length-1);
			} else {
				lp = 100;
				ppr = ppa;
			}
		}

		if(!this.diag.isPlaying && lp >= 100) {
			clearInterval(this.config.mooPlayerControllerId);
		}
		
		if(override) {
			this.mooPlayerOnProgressChange(lp, 0, 0, 0, tt);
		} else {
			this.mooPlayerOnProgressChange(lp, ppr, ppa, pt, tt);
		}
	},
	
	mooPlayerReady: function(swfVersion) { // Called from Flash / HTML5 interval
		this.fireEvent('loaded');
		if(this.config.usingFlash) {
			this.config.swfVersion = swfVersion;
			if(this.config.swfVersionRequired != this.config.swfVersion) {
				this._error("mooPlayer's JavaScript / SWF version mismatch!\n\nJavaScript requires SWF : " + this.config.swfVersionRequired + "\nThe Jplayer.swf used is : " + this.config.swfVersion);
			}
		} else {
			this.config.swfVersion = "n/a";
		}
	},
	
	mooPlayerVolume: function(v) { // Called from Flash / HTML5 event
		if( this.cssSelectors.volumeBarValue != null ) {
			this.cssSelectors.volumeBarValue.setStyle('width', v+"%");
			this._forceUpdate();
		}
	}
});

