VPSASPPlayer = function(id, radius, innerRadius, color)
{	
	if (radius == null)
	{
		radius = 55;
	}
	
	if (innerRadius == null)
	{
		innerRadius = 26;
	}
	
	if (color == null)
	{
		color = "#000000";
	}
	
	var BUTTON = "button";
	var SEEKBAR = "seekbar";
	var SPECTRUM = "spectrum";
	var NONE = "none";

	var self = this;
	
	var id = id;
	var color = color;
	var radius = radius;
	var innerRadius = innerRadius;

	var source = null;		
	var progress = 0;
	var position = 0;
	var autoPlay = false;
	
	var seekBarScale = 0.8;
	var seekBarRadius = Math.round(innerRadius * seekBarScale);
	var seekBarThickness = radius / 22;
	var seekBarRotation = -Math.PI / 2;

	var iconScale = 0.6;
	var iconSize = Math.round(seekBarRadius * iconScale);
	var iconWidth = iconSize * 0.85;
	var iconHeight = iconSize;
	var iconX = Math.round(radius - iconWidth / 2);
	var iconY = Math.round(radius - iconHeight / 2);
	
	var spectrum = [];
	var spectrumDuration = 1200;
	var spectrumNumPeaks = 8;
	var spectrumWidth = (radius - innerRadius) / 16;
	
	var canvas = document.createElement("canvas");
	canvas.width = radius * 2;
	canvas.height = radius * 2;
	
	var audio = document.createElement("audio");
	audio.style.display = "none";
	audio.style.visibility = "hidden";
	audio.style.position = "absolute";
	audio.style.top = "-50px";
	
	var player = document.getElementById(id);
	player.appendChild(audio);
	player.appendChild(canvas);
	
	var intervalDuration = 30;
	var interval = null;

	/**
	 * load an audio file
	 */
	self.load = function(url)
	{
		source = url;
		window.clearInterval(interval);
		
		if (source && source.length > 0)
		{
			canvas.addEventListener("click", clickHandler, false);
			canvas.addEventListener("mousemove", mousemoveHandler, false);
			
			audio.addEventListener("loadstart", loadstartHandler, false);
			//audio.addEventListener("progress", progressHandler, false);
			audio.addEventListener("timeupdate", timeupdateHandler, false);
			audio.addEventListener("ended", endedHandler, false);
			audio.src = source;
			audio.load();
			//audio.volume = 0;
			
			interval = window.setInterval(draw, intervalDuration);
		}
	}
	
	/**
	 * play the playback
	 */
	self.play = function()
	{
		if (source)
		{
			audio.play();
		}
	}
	 
	/**
	 * pause the playback
	 */
	self.pause = function()
	{	
		if (source)
		{
			audio.pause();
			draw();
		}
	}
	
	
	/**
	 * seek to a time
	 */
	self.seek = function(time)
	{
		if (source)
		{
			audio.currentTime = time;
			
			if (audio.paused && progress >= 1)
			{
				draw();
			}
		}
	}
	
	/**
	 * stop the playback
	 */
	self.stop = function()
	{
		self.pause();
		
		if (audio.currentTime > 0)
		{
			audio.currentTime = 0;
		}
		
		position = 0;
		spectrum.length = 0;
	}
	
	/**
	 * close the file
	 */
	self.close = function()
	{
		self.stop();
		
		progress = 0;
		source = null;
		
		canvas.removeEventListener("click", clickHandler, false);
		canvas.removeEventListener("mousemove", mousemoveHandler, false);
		
		audio.src = source;
		audio.removeEventListener("loadstart", loadstartHandler, false);
		//audio.removeEventListener("progress", progressHandler, false);
		audio.removeEventListener("timeupdate", timeupdateHandler, false);
		audio.removeEventListener("ended", endedHandler, false);
	}
					
	/**
	 * draw the canvas
	 */
	var draw = function()
	{	
		if (canvas.getContext)
		{  
			var context = canvas.getContext("2d");
			context.clearRect(0, 0, radius * 2, radius * 2);
			
			// background
			context.beginPath();
			context.arc(radius, radius, radius, 0, Math.PI * 2, false);
			context.closePath();
			context.fillStyle = color; 
			context.fill();
			
			// spectrum
			if (!audio.paused)
			{
				calculateSpectrum();
			}
			
			for (var i = 0; i < spectrum.length; i++)
			{				
				context.beginPath();
				context.arc(radius, radius, spectrum[i].current - spectrumWidth, 0, Math.PI * 2, false);
				context.strokeStyle = "rgba(255, 255, 255, " + spectrum[i].alpha * (1 - spectrum[i].progress) + ")";
				context.lineWidth = spectrumWidth;
				context.stroke();
			}
			
			// download progress
			/*if ((audio.networkState == audio.NETWORK_LOADED || typeof audio.buffered != "object") && progress < 1)
			{
				progress = 1;
			}*/
			if (progress < 1 && audio.duration)
			{
				progress = audio.buffered.end(0) / audio.duration;
			}

			context.beginPath();
			context.arc(radius, radius, seekBarRadius, seekBarRotation, ((Math.PI * 2) * progress) + seekBarRotation, false);
			context.strokeStyle = "rgba(255, 255, 255, 0.15)";
			context.lineCap = "butt";
			context.lineWidth = seekBarThickness;
			context.stroke();
			
			// audio time
			context.beginPath();
			context.arc(radius, radius, seekBarRadius, seekBarRotation, ((Math.PI * 2) * position) + seekBarRotation, false);
			context.strokeStyle = "rgba(255, 255, 255, 0.75)";
			context.lineCap = "butt";
			context.lineWidth = seekBarThickness;
			context.stroke();
			
			// play pause icon
			var gradient = context.createLinearGradient(iconX, iconY, iconX, iconY + iconHeight);
			gradient.addColorStop(0, "rgba(255, 255, 255, 1)");
			gradient.addColorStop(1, "rgba(255, 255, 255, 0.65)");
									
			if (audio.paused)
			{
				context.beginPath();
				context.moveTo(iconX, iconY);
				context.lineTo(iconX + iconWidth, iconY + (iconHeight / 2));
				context.lineTo(iconX, iconY + iconHeight);
				context.closePath();
				context.fillStyle = gradient;  
				context.fill();
			}
			else
			{
				context.beginPath();
				context.moveTo(iconX, iconY);
				context.lineTo(iconX + (iconWidth * (2/5)), iconY);
				context.lineTo(iconX + (iconWidth * (2/5)), iconY + iconHeight);
				context.lineTo(iconX, iconY + iconHeight);
				context.moveTo(iconX + (iconWidth * (3/5)), iconY);
				context.lineTo(iconX + iconWidth, iconY);
				context.lineTo(iconX + iconWidth, iconY + iconHeight);
				context.lineTo(iconX + (iconWidth * (3/5)), iconY + iconHeight);
				context.closePath();
				context.fillStyle = gradient; 
				context.fill();
			}
		}
	}			
	
	/**
	 * calculate the spectrum 
	 * this is all fake
	 */			
	var calculateSpectrum = function()
	{				
		for (var i = 0; i < spectrum.length; i++)
		{
			var peak = spectrum[i];
			
			peak.progress = (peak.current - peak.radius) / (radius - peak.radius);
			peak.current += peak.shift;
			
			if(peak.current >= radius)
			{
				spectrum.splice(i, 1);
			}

		}
		
		var create = Boolean(Math.round(Math.random()));
		
		if(create && spectrum.length < spectrumNumPeaks)
		{
			spectrum.push(createPeak());
		}

	}
	
	/**
	 * create a peak
	 */	
	var createPeak = function()
	{
		var peak = {};
		peak.radius = Math.floor(Math.random() * (radius - (innerRadius + (spectrumWidth * 2)))) + innerRadius + spectrumWidth + spectrumWidth;
		
		if (peak.radius > radius - (spectrumWidth * 2))
		{
			peak.radius = radius - (spectrumWidth * 2);
		}
		
		peak.alpha = (Math.random() * 0.5) + 0.3;
		peak.current = peak.radius;
		peak.shift = (radius - peak.radius) / (spectrumDuration / intervalDuration);
		peak.progress = 0;
		
		return peak;
	}
	
	/**
	 * check if the coordinates are 
	 * inside the circle radius
	 */			
	var insideCircle = function(x, y, circleRadius)
	{
		var inside = false;
		
		var mouseX = Math.abs(x - radius);
		var mouseY = Math.abs(y - radius);
		var mouseRadius = Math.sqrt(Math.pow(radius - x, 2) + Math.pow(radius - y, 2));
		
		if (mouseRadius <= circleRadius && mouseRadius <= circleRadius)
		{
			inside = true;
		}
		
		return inside;
	}
	
	/**
	 * get the button zone depending 
	 * on the mouse position
	 */			
	var getZone = function(x, y)
	{
		if(insideCircle(x, y, seekBarRadius - (seekBarThickness / 2)))
		{
			return BUTTON;
		}
		else if(insideCircle(x, y, seekBarRadius + (seekBarThickness / 2)))
		{
			return SEEKBAR;
		}
		else
		{
			return NONE;
		}
		
		return NONE;
	}
	
	/**
	 * debug
	 */
	var debug = function(str)
	{
		var p = document.createElement("p");
		p.innerHTML = str;
		player.appendChild(p);
	}
	
	// ---------------------------------
	// Events
	// ---------------------------------
	
	/**
	 * canvas mouse move handler
	 */ 			
	var mousemoveHandler = function(event)
	{
		if(getZone(event.offsetX, event.offsetY) == NONE)
		{
			canvas.style.cursor = "default";
		}
		else
		{
			canvas.style.cursor = "pointer";
		}
	}
	
	/**
	 * canvas mouse click handler
	 */			
	var clickHandler = function(event)
	{
		var zone = getZone(event.offsetX, event.offsetY);
		
		switch (zone)
		{
			case BUTTON:
				if(audio.paused)
				{
					self.play();
				}
				else
				{
					self.pause();
				}
				
				// dispatch play state change event
				var event = document.createEvent("Events"); 
				event.initEvent("playStateChange", true, true);		
				player.dispatchEvent(event);
				
				break;
				
			case SEEKBAR:
				var angle = Math.atan2(event.offsetY - radius, event.offsetX - radius) - seekBarRotation;
				
				if(angle < 0)
				{
					angle += Math.PI * 2;
				}
				
				var clickedPosition = (angle / (Math.PI * 2));
									
				if (clickedPosition <= progress)
				{
					position = clickedPosition;
					self.seek(audio.duration * clickedPosition);
				}
				break;
				
			default:
				break;
		}
		
		draw();
	}
					
	/**
	 * audio download start handler
	 */			
	var loadstartHandler = function(event)
	{
		if (autoPlay)
		{
			self.play();
		}
	}

	/**
	 * audio download progress handler
	 */	
	var progressHandler = function(event)
	{
		//progress = audio.buffered.end(audio.buffered.length - 1) / audio.duration;
		progress = event.loaded / event.total;
		
		if (audio.paused)
		{
			draw();
		}
	}			
	
	/**
	 * audio play time update handler
	 */	
	var timeupdateHandler = function(event)
	{
		position = audio.currentTime / audio.duration;
	}
	
	/**
	 * audio play ended handler
	 */	
	var endedHandler = function(event)
	{
		self.stop();
	}		
}