// requires: rjs.1.1.js

/************************************************************************************
 * Class Definition
 ************************************************************************************/

// define root namespace
var TeamWarp = function() {

	/** A list of custom tags. The replacement function will be called for each appearance of [tagName]...[/tagName]. The 1st argument to the replacement function is the entire match and the 2nd argument is the contents of the tag. */
	// this constant is not defined on the prototype because it references other class functions
	this.customTags = [
		{ tagName: "card", replaceFunc: this.replaceCard },
		{ tagName: "deck", replaceFunc: this.replaceDeck }
	];
};


/************************************************************************************
 * Constants
 ************************************************************************************/

//TeamWarp.prototype.baseUrl = "http://studio.lorostar.operaunite.com/server/content/TeamWarp/";
//TeamWarp.prototype.baseUrl = "http://homepage.mac.com/samgaard/Sites/.Public/";
TeamWarp.prototype.baseUrl = "http://manicexpressyv.com/magic/teamwarp/";

// The standard height of card images.
TeamWarp.prototype.cardHeight = 285;
TeamWarp.prototype.cardWidth = 200;
TeamWarp.prototype.maxStackSize = 4;
TeamWarp.prototype.thumbnailWidth = 135;

/** A dictionary used to map what cards are affectionately known by to their actual card name. */
TeamWarp.prototype.cardNicknames = {
	"Jeff" : "Jace, the Mind Sculptor",
	"Jeffrey" : "Jace, the Mind Sculptor",
	"Jeffrey Jazzhands" : "Jace, the Mind Sculptor",
	"Jeffrey Jazz-hands" : "Jace, the Mind Sculptor",
	"Jace" : "Jace, the Mind Sculptor",
	"Jace 2.0" : "Jace, the Mind Sculptor",
	"Jace 1.0" : "Jace Beleren",
	"DJ Jace" : "Jace Beleren",
	"Sarah" : "Elspeth, Knight-Errant",
	"Sarah 2.0" : "Elspeth Tirel",
	"Venser" : "Venser, the Sojourner",
	"Garruk" : "Garruk Wildspeaker",
	"Creeping Shit Pool" : "Creeping Tar Pit",
	"Kozilek" : "Kozilek, Butcher of Truth",
	"Ulamog" : "Ulamog, the Infinite Gyre",
	"Emrakul" : "Emrakul, the Aeons Torn",
	"Linvala" : "Linvala, Keeper of Silence"
};

TeamWarp.prototype.unusedMenuItems = [
	"Gallery"
];

/************************************************************************************
 * Utility Functions
 ************************************************************************************/

// Identity function can come in handy
var I = function(x) { return x; };

TeamWarp.prototype.replaceCard = function(match, attrString, cardName) {

	var attrs = TeamWarp.parseAttributes(attrString);
	
	if(attrs.view == "visual") {
		return createHtml(TeamWarp.createCardImage(attrs.name || cardName));
	}
	else {
		return createHtml(["a.card", { 
			href: TeamWarp.getLinkUrl(attrs.name || cardName),
			"data-name": attrs.name,
			target : "card"
		}, cardName]);
	}
};

/** Returns a dictionary of attributes parsed from the given attribute string. */
TeamWarp.prototype.parseAttributes = function(attrString) {

	attrString = attrString || "";
	var re = /(\S+)=["']?((?:.(?!["']?\s+(?:\S+)=|[>"']))+.)["']?/g;
	var dict = {};
	while(match = re.exec(attrString)) {
		dict[match[1]] = match[2];
	};

	return dict;
};

TeamWarp.prototype.replaceNumberedCard = function(match, cardNumber, cardName) {
	return "{0} {1}".format(cardNumber, TeamWarp.replaceCard(null, null, cardName));
};

TeamWarp.prototype.replaceDeck = function(match, attrString, contents) {
	var attrs = TeamWarp.parseAttributes(attrString);
	var re = /(\d+) ([^<]*)/g; // stopping on the < character (assuming <br>) is pretty loose

	var newContents;
	if(attrs.view == "list") {
		newContents = contents.replace(re, TeamWarp.replaceNumberedCard);
	}
	else {
		var deck = new Deck(contents);
		newContents = createHtml(["div.deck", {}, deck.cards.map(TeamWarp.createCardStack)]);
	}

	return newContents;
};

/** Constructs the URL to the image of the given card name. */
TeamWarp.prototype.getImageUrl = function(cardName) {
	cardName = TeamWarp.cardNicknames[cardName] || cardName;
	return "http://gatherer.wizards.com/Handlers/Image.ashx?type=card&name={0}&size=small&".format(cardName);
	// .replace(/[- ]/g, "_").replace(/[',]/g, "")
};

/** Constructs the URL to the MagicCards.info card lookup. */
TeamWarp.prototype.getLinkUrl = function(cardName) {
	cardName = TeamWarp.cardNicknames[cardName] || cardName;
	// http://4-of.com/?page=card&name=
	return "http://magiccards.info/query?q=" + cardName;
};

/** Creates an image element of the given card. */
TeamWarp.prototype.createCardThumbnail = function(cardName) {
	return create(["a", { href: TeamWarp.getLinkUrl(cardName), target: "card" }, [
		["img.thumbnail", { src: TeamWarp.getImageUrl(cardName), width: TeamWarp.thumbnailWidth }]
	]]);
};

/** Creates an image element of the given card. */
TeamWarp.prototype.createCardImage = function(cardName) {
	return create(["img", { src: TeamWarp.getImageUrl(cardName) }]);
};

TeamWarp.prototype.createCardStack = function(card) {

	if(card.count > 4) {
		return create(["div.cardStack", {}, [
			["span.overlayCount", {}, card.count],
			TeamWarp.createCardThumbnail(card.name)
		]]);
	}
	else {

		return create(["div.cardStack", {}, range(0,card.count-1).map(function(n) {

			var cascadeOffsetTop = 13;
			var cascadeOffsetLeft = 7;
			var stackMarginTop = card.count * cascadeOffsetTop;
			var stackMarginLeft = (card.count-1) * cascadeOffsetLeft;

			var img = $(TeamWarp.createCardThumbnail(card.name));

			// the first card in the stack needs bottom and right margin to keep the other stacks from overlapping
			if(n == 0) {
				img.css("margin-bottom", stackMarginTop)
				   .css("margin-right", stackMarginLeft);
			}
			// other in the stack needs top and left margin to cascade
			else {
				img.css("position", "absolute")
				   .css("margin-top", n * cascadeOffsetTop)
				   .css("margin-left", -TeamWarp.thumbnailWidth - stackMarginLeft + n * cascadeOffsetLeft);
			}
			return img[0];
		})]);
	}
};

/** Replaces custom tags with the result of a replacement function.
	@arg customTags		An array of customTag items that define the tag name and replacement function
	@arg root			The root element to replace the contents of
*/
TeamWarp.prototype.replaceTags = function(customTags, root) {
	var newHtml = root.html();

	// for each tag in the customTags dictionary, search the html for that tag and call the associated replacement function
	each(customTags, function(tag) {
		var re = new RegExp("\\[{0}([^\\]]*)?\\](.*?)\\[\\/{0}\\]".format(tag.tagName), "g");
		newHtml = newHtml.replace(re, tag.replaceFunc);
	});

	// Replace [[Card Name]] with card link as well
	newHtml = newHtml.replace(/\[\[(.*?)\]\]/g, function(match, cardName) { return TeamWarp.replaceCard(null, null, cardName); });

	root.html(newHtml);
};

/** Calculate a hypergeometric distribution from the probabilities panel. */
TeamWarp.prototype.hyperCalculate = function() {
	$("#hyperOutput").val("");

	var maxTarget = !!$("#hyperAtLeast").val() ? Math.min($("#hyperNumberOfDraws").val(), $("#hyperNumberInDeck").val()) : $("#hyperTargetNumber").val();
	var output = 0;
	for(var i=$("#hyperTargetNumber").val(); i<=maxTarget; i++) {
		output += hyper(
			i,
			$("#hyperNumberInDeck").val(),
			$("#hyperNumberOfDraws").val(),
			$("#hyperTotalCardsInDeck").val()
		);
	}
	$("#hyperOutput").val(output);
};


/************************************************************************************
 * Events
 ************************************************************************************/

/** Handles the event that he card link is moused over. Appends and positions the hover card image. */
TeamWarp.prototype.onCardLinkOver = function(e) {
	var el = $(e.target);
	var hoverImage = $(TeamWarp.createCardImage(el.attr("data-name") || el.text())).attr("id", "hoverCard");
	el.after(hoverImage);

	// move the image within view if it would fall below the bottom/right of the window
	var windowBottom = $(window).scrollTop() + $(window).height();
	var windowRight = $(window).scrollLeft() + $(window).width();
	if(hoverImage.offset().top + TeamWarp.cardHeight > windowBottom) {
		hoverImage.css("top", windowBottom - TeamWarp.cardHeight);
	}
	if(hoverImage.offset().left + TeamWarp.cardWidth > windowRight) {
		hoverImage.css("left", el.offset().left - TeamWarp.cardWidth - 30); // additional offset accounts for hoverCard margin
		//hoverImage.css("margin-top", el.height());
	}
};

/** Handles the event that he card link is moused out of. Removes the image container */
TeamWarp.prototype.onCardLinkOut = function(e) {
	$("#hoverCard").remove();
};

//TeamWarp.prototype.onThumbnailOver = function(e) {
//	var el = $(e.target);
//	el.css("z-index", 1000)
//	  .animate("width", TeamWarp.cardWidth);
//	//el.animate({ width: 200 }, 300);
//};
//
//TeamWarp.prototype.onThumbnailOut = function(e) {
//	var el = $(e.target);
//	//el.css("position", "")
//	//  .css("z-index", "")
//	//  .css("width", TeamWarp.thumbnailWidth);
//	//el.animate({ width: 100 }, 300);
//};

TeamWarp.prototype.addEventHandlers = function() {

	// add link hover event handlers
	$("a.card").each(function(i, link) {
		$(link).mouseover(TeamWarp.onCardLinkOver)
			   .mouseout(TeamWarp.onCardLinkOut);
	});

	// add thumbnail hover event handlers
	//$("img.thumbnail").each(function(i, img) {
	//	$(img).mouseover(TeamWarp.onThumbnailOver)
	//		  .mouseout(TeamWarp.onThumbnailOut);
	//});
};


/************************************************************************************
 * Main
 ************************************************************************************/

/** Parses each post on the page and adds functionality to card tags by replacing the tag text with links or other custom html. */
TeamWarp.prototype.activateCardTags = function() {

	// replace tags in each post
	$("div.post div.content").each(function(i, post) {
		TeamWarp.replaceTags(TeamWarp.customTags, $(post));
	});

	TeamWarp.addEventHandlers();
};

/** Removes unused items from the menu (that are not removable through the Forum administration panel). */
//TeamWarp.prototype.removeUnusedMenuItems = function() {
//	$("a.mainmenu").each(function(i, el) {
//		var link = $(el);
//		if(contains(TeamWarp.unusedMenuItems, link.text())) {
//			link.parent().remove();
//		}
//	});
//};

/** Adds the Probabilities link to the main menu and attaches the click event to open the Probabilities panel. */
TeamWarp.prototype.addProbabilities = function() {
	
	// add Probabilities link
	var linkInsertionPoint = $($("ul.navlinks").children()[7]);
	linkInsertionPoint.after(create(
		["li", {}, [
			["a.mainmenu.probabilities", {}, [
				["img", { hspace: 2, border: 0, title: "Probabilities", src: TeamWarp.baseUrl + "percent-icon.png" }],
				"Probabilities "
			]]
		]]
	));
	
	// add Probabilities panel
	$("ul.navlinks").after(create(
		["div.probabilitiesPanel.dropdownPanel", { style: "display: none" }, [
			"What is the probability of drawing ",
			["input#hyperTargetNumber.numberInput", { type: "text", value: 1 }],
			["select#hyperAtLeast", {}, [
				["option"],
				["option", { value: 1, selected: "selected" }, "+"]
			]],
			" of a card that is a ",
			["input#hyperNumberInDeck.numberInput", { type: "text", value: 4 }],
			"-of in ",
			["input#hyperNumberOfDraws.numberInput", { type: "text", value: 7 }],
			" draws in a deck of ",
			["input#hyperTotalCardsInDeck.numberInput", { type: "text", value: 60 }],
			" cards? ",
			["input#hyperCalculate", { type: "button", value: "Calculate" }],
			" ",
			["input#hyperOutput", { type: "text" }]
		]]
	));
	
	// event handlers
	$("a.mainmenu.probabilities").click(function() {
		$("div.probabilitiesPanel").slideToggle();
	});
	$("#hyperCalculate").click(TeamWarp.hyperCalculate);
};

/** Initiates all the action. */
TeamWarp.prototype.onDomLoaded = function() {
	
	// add stylesheet
	//$("head").append(create(
	//	["link", {
	//		type: "text/css",
	//		rel: "stylesheet",
	//		href: TeamWarp.baseUrl + "style.css"
	//	}]
	//));

	//TeamWarp.removeUnusedMenuItems();
	TeamWarp.addProbabilities();
	TeamWarp.activateCardTags();
};

// create singleton
TeamWarp = new TeamWarp();

// ondomloaded
$(TeamWarp.onDomLoaded);
