/*global $, document, window, navigator, Nifty, Constants, Ajax, Flash, MbitInfo, SequencerMain, statusBar, noop, isset, getPos, JSON */
var MbitLinksSingleton = function (obj) {
	var unavailMsg = "This cannot be MSG at this time. Try again later.",
		_playing = false,
		playImg = Constants.images()+"mbitIcons/play.gif",
		pauseImg = Constants.images()+"mbitIcons/pause.gif",
		stopImg = Constants.images()+"mbitIcons/stop.gif",
		playerName = 'smallPlayerHidden',
		inTimer,
		outTimer,
		selected,
		isSequencer,
		isHovered = false,
		self = {},
		topOffset, topBgndOffset,					// offsets of the mbit popdown icons and background image
		leftOffset, leftBgndOffset,
		pinCallback, unpinCallback, hoverCallback, unhoverCallback,	// callbacks after the pin/unpin action is taken
		relativeToContentHolder;					// is the popdown absolutely positioned or relative to contentHolder?
	
	var mbitLinks, mbitLinksBgnd, play, playX, download, downloadX, purchase, purchaseX,
		familyTree, comment, profile, sequencer, sequencerX, pinImg, unpinImg;
		
	//// Private Methods ////
	
	function getValue(newValue, oldValue, defaultValue) {
		return (isset(typeof(newValue))) ? newValue :
			   (isset(typeof(oldValue))) ? oldValue :
			   defaultValue;
	}

	function toggleIcons(bool, trueIcon, falseIcon) {
		if (trueIcon) {
			if (!bool) {
				trueIcon.hide();
				falseIcon.show('inline');
			} else {
				trueIcon.show('inline');
				falseIcon.hide();
			}
		}
	}

	function selectResult(result, id) {
		if (selected && ((result && id) || (outTimer != -1))) {
			selected.removeClassName("selected");
			selected.removeEvent('mousemove', self.prolong);
			selected.removeEvent('mouseout', self.closeResult);
		}
		if (result && id) {
			result.addEvent('mousemove', self.prolong);
			result.addEvent('mouseout', self.closeResult);
			self.prolong();
			var canplay = MbitInfo[id].canplay,
				canseq = MbitInfo[id].canseq,
				mine = MbitInfo[id].mine,
				ispinned = MbitInfo[id].ispinned,
				isseq = MbitInfo[id].isseq;
	
			selected = result;
			result.addClassName("selected");
			var pos = getPos(result),
				height = result.offsetHeight;
			if (relativeToContentHolder) {
				var contentHolderPos = getPos($('contentHolder')),
					newY = (pos.y - contentHolderPos.y) + height;
				mbitLinks.style.top = (newY-topOffset) + "px";
				mbitLinksBgnd.style.top = (newY-topBgndOffset) + "px";
				mbitLinks.style.left = leftOffset + "px";
				mbitLinksBgnd.style.left = leftBgndOffset + "px";
			} else {
				mbitLinks.style.top = (pos.y+height-topOffset) + "px";
				mbitLinksBgnd.style.top = (pos.y+height-topBgndOffset) + "px";
				mbitLinks.style.left = leftOffset + "px";
				mbitLinksBgnd.style.left = leftBgndOffset + "px";
			}
			mbitLinks.show('block');
			mbitLinksBgnd.show('block');
			// set links
			toggleIcons(canplay, play, playX);
			if (download) {
				download.href = Constants.downLink(id);
				toggleIcons(canplay, download, downloadX);
			}
			if (purchase) {
				if (mine === 0) {
					purchase.href = '#';
					if (canplay && $(id+"_name")) {
						purchase.nextSibling.innerHTML = id+'|'+$(id+"_name").scan();
					}
					toggleIcons(canplay, purchase, purchaseX);
				} else {
					purchase.hide();
					purchaseX.hide();
				}
			}
			if (familyTree) { familyTree.href = Constants.treeLink(id); }
			if (comment) { comment.href = Constants.profileLink(id) + Constants.commentAnchor(); }
			if (profile) { profile.href = Constants.profileLink(id); }
			if (sequencer) {
				sequencer.href = Constants.seqLink(id);
				toggleIcons(canseq, sequencer, sequencerX);
				if (isSequencer) {
					var seqFunc;
					if (isseq) {
						seqFunc = function () {
							if (canseq && canplay) {
								// this is defined in sequencer
								SequencerMain.showSessionPopup(id);
							} else if (canseq && !canplay) {
								Flash.loadSession(id);
							}
							return false;
						};
					} else {
						seqFunc = function () {
							Flash.loadMbit(id);
							return false;
						};
					}
					sequencer.setClick(seqFunc);
				}
			}
			if (pinImg) {
				if (ispinned) {
					pinImg.hide();
					unpinImg.show('inline');
				} else {
					unpinImg.hide();
					pinImg.show('inline');
				}
				pinImg.setClick(function () { self.pin(id); });
				unpinImg.setClick(function () { self.unPin(id); });
			}
			if (canplay) {
				if (id !== Flash.curID) {
					if (_playing) { playPause(); }
					Flash.update(playerName, id);
					play.bitID = id;
				}
			} else { play.bitID = null; }
			hoverCallback();
		} else if (outTimer != -1) {
			selected = null;
			self.hideLinks();
			unhoverCallback();
		}
	}
	
	function checkPin(request, add) {
		var response = JSON.parse(request.responseText);
		if (add && response.status == 1) {
			if (selected && response.id == selected.id.substring(selected.id.indexOf('_')+1)) {
				unpinImg.show('inline');
				pinImg.hide();
			}
			MbitInfo[response.id].ispinned = 1;
			statusBar.talk("Added to your palette.");
			pinCallback(request);
		} else if (add && response.status == 2) {
			if (selected && response.id == selected.id.substring(selected.id.indexOf('_')+1)) {
				unpinImg.show('inline');
				pinImg.hide();
			}
			MbitInfo[response.id].ispinned = 1;
			statusBar.talk("This is already in your palette.");
		} else if (!add && response.status == 1) {
			if (selected && response.id == selected.id.substring(selected.id.indexOf('_')+1)) {
				unpinImg.hide();
				pinImg.show('inline');
			}
			MbitInfo[response.id].ispinned = 0;
			statusBar.talk("Removed from your palette.");
			unpinCallback(request);
		} else if (!add) {
			statusBar.error("Failed to remove from your palette");
		} else if (add) {
			statusBar.error("Failed to add to your palette");
		} else {
			statusBar.error("Unknown error.");
		}
	}
	
	function playPause(e, pause) {
		var src = _playing ? playImg : (!!pause ? pauseImg : stopImg),
			alt = _playing ? 'Play' : (!!pause ? 'Pause' : 'Stop');
		play.src = src;
		play.setAttribute('alt', alt);
		play.setAttribute('title', alt);
		if (_playing) { Flash.stop(playerName); }
		else { Flash.play(playerName); }
		if (_playing && !pause) {
			Flash.update(playerName);
			Flash.update(playerName, play.bitID);
		}
		_playing = !_playing;
	}

	function buildUnavailImg(type, imgName) {
		var msg = unavailMsg.replace(/MSG/, type),
			img = document.create('img').hide();
		img.src = Constants.images()+"mbitIcons/"+imgName+"X.gif";
		img.setAttribute('alt', msg);
		img.setAttribute('title', msg);
		return img;
	}

	function buildUpUnavailable() {
		var preload = [],
			imgNames = ['play', 'download', 'purchase', 'sequencer'];
		for (var i=0; i<imgNames.length; i++) {
			var img = new Image();
			img.src = Constants.images()+"mbitIcons/"+imgNames[i]+"X.gif";
			preload.push(img);
		}
		
		playX = buildUnavailImg("played", "play");
		if (mbitLinks) { mbitLinks.insertBefore(playX, play); }
		if (download) {
			downloadX = buildUnavailImg("downloaded", "download");
			mbitLinks.insertBefore(downloadX, download);
		}
		purchaseX = buildUnavailImg("purchased", "purchase");
		if (mbitLinks) { mbitLinks.insertBefore(purchaseX, purchase); }
		sequencerX = buildUnavailImg("sequenced", "sequencer");
		if (mbitLinks) { mbitLinks.insertBefore(sequencerX, sequencer); }
	}

	function setup() {
		self.applySettings(obj);
		
		mbitLinks = $('mbitLinks');
		mbitLinksBgnd = $('mbitLinksBackground');
		play = $('play');
		download = $('download');
		purchase = $('purchase');
		familyTree = $('familyTree');
		if (familyTree) { familyTree.hide(); }
		comment = $('comment');
		profile = $('mbitProfile');
		sequencer = $('sequencer');
		pinImg = $('pin');
		unpinImg = $('remove');
		
		var preload = new Image();
		preload.src = pauseImg;

		if (mbitLinks) {
			mbitLinks.addEvent('mouseover', self.prolong);
			mbitLinks.addEvent('mousemove', self.prolong);
		}
		if (mbitLinksBgnd) {
			mbitLinksBgnd.addEvent('mouseover', self.prolong);
			mbitLinksBgnd.addEvent('mousemove', self.prolong);
		}
		if (play) {
			play.addClassName('hand');
			play.addClick(playPause);
		}
		buildUpUnavailable();
	}
	
	/// public methods ///
	
	self.selected = function () { return !!selected ? selected.id : null; };
	self.hoverResult = function (result, id, callback) {
		inTimer = setTimeout(selectResult.bind(self,result,id), 100);
		clearTimeout(outTimer);
		if (callback) { hoverCallback = callback; }
	};

	self.hideLinks = function (stop) {
		if (mbitLinks) { mbitLinks.style.top = "-5000px"; }
		if (mbitLinksBgnd) { mbitLinksBgnd.style.top = "-5000px"; }
//		if (!!stop) { Flash.stop(playerName); } // hiding the links stops the mBit
	};
	
	self.closeResult = function (timeoutOrFunc) {
		if (typeof(timeoutOrFunc) === 'object') {
			// this means its a browser event object
			timeoutOrFunc = null;
		}
		if (typeof(timeoutOrFunc)==='function') {
			unhoverCallback = timeoutOrFunc;
			timeoutOrFunc = null;
		}
		if (!isset(typeof(timeoutOrFunc)) || timeoutOrFunc === null) { timeoutOrFunc = 300; }
		isHovered = false;
		clearTimeout(inTimer);
		outTimer = setTimeout(selectResult.bind(self), timeoutOrFunc);
	};

	self.prolong = function () {
		isHovered = true;
		clearTimeout(outTimer);
		outTimer = -1;
	};
	self.prolongFromCart = self.prolong;

	self.stillHovered = function () { return isHovered; };

	self.applySettings = function (obj) {
		obj = obj || {};
		// this will only update settings that are set in obj
		// if a setting is not set, it will keep the old value if that is set, else a default
		isSequencer = getValue(obj.isSequencer, isSequencer, false);
		topOffset = getValue(obj.topOffset, topOffset, 202);
		topBgndOffset = getValue(obj.topBgndOffset, topBgndOffset, 200);
		leftOffset = getValue(obj.leftOffset, leftOffset, 172);
		leftBgndOffset = getValue(obj.leftBgndOffset, leftBgndOffset, 172);
		pinCallback = getValue(obj.pinCallback, pinCallback, noop);
		unpinCallback = getValue(obj.unpinCallback, unpinCallback, noop);
		hoverCallback = getValue(obj.hoverCallback, hoverCallback, noop);
		unhoverCallback = getValue(obj.unhoverCallback, unhoverCallback, noop);
		relativeToContentHolder = getValue(obj.relativeToContentHolder, relativeToContentHolder, false);
	};

	self.pin = function (id) {
		pinnedId = id;
		var pars = "f=a&id=" + id,
			ajax = new Ajax(Constants.ajax()+'tag',
				{	method: 'get',
					parameters: pars,
					onSuccess: function (request) { checkPin(request, true); },
					onFailure: function () { statusBar.error('Pinning failed. Please try again.'); }
				}
			);
		ajax.send();
	};
	
	self.unPin = function (id) {
		pinnedId = id;
		var pars = "f=r&id=" + id,
			ajax = new Ajax( Constants.ajax()+'tag',
				{	method: 'get', 
					parameters: pars,
					onSuccess: function (request) { checkPin(request, false); },
					onFailure: function () { statusBar.error('Removing failed. Please try again.'); }
				}
			);
		ajax.send();
	};

	setup();

	return self;
};

var MbitLinks = function () {
	var singleton;	// static member - singleton
	return function (obj) {
		if (!singleton) { singleton = new MbitLinksSingleton(); }
		singleton.applySettings(obj);
		return singleton;
	};
}();

