// ==UserScript==
// @name           Wikitrails
// @namespace      http://wikitrail.appspot.com/
// @description    Adds a set of links to the bottom of every Wikipedia page to show where you and others end up after viewing that page
// @include        http://*.wikipedia.org/wiki/*
// ==/UserScript==

var MAX_TRAIL_DEPTH = 6;
var GAMMA_FORWARD = .6;
var GAMMA_BACK = .6;
var FULL_URL_REGEX = /.*\/\/(.*).wikipedia.org\/wiki\/([^#]*)/;
var SHORT_URL_REGEX = /\/wiki\/([^#]*)/; // not needed?
var API_PATH = 'http://wikitrail.appspot.com/api/';
//var API_PATH = 'http://localhost:8080/api/';

/* each trail is a list of WikiPage's */
var g_allTrails = null;
var g_trail = null;

var g_currentPage = null;
var g_trailLinksToRegister = [];

Array.prototype.toString = function() {
	var res = '[';
	this.forEach(function(el) {
		res += el.toString();
		res += ',';
	});
	return res + ']'
}

if(!console) {
	console = {};
	console.log = function() { };
}

function unpackTrails(trailsStr) {
	if(!trailsStr) return null;
	trails = eval(trailsStr);
	trails.forEach(function(trail) {
		for(var k = 0; k < trail.length; k++) {
			trail[k] = WikiPage.fromStr(trail[k]);
		}
	});
	return trails;
}

function currentTrail() {
	for(var k = 0; k < g_allTrails.length; k++) {
		if(g_allTrails[k][g_allTrails[k].length - 1].equals(g_currentPage))
			return g_allTrails[k];
	}
	
	// at this point the user opened a new page to wikipedia
	var res = [g_currentPage];
	g_allTrails.push(res);
	return res;
}

function pageLoaded(url) {
	g_currentPage = WikiPage.fromUrl(url);
	if(!g_currentPage) return;
	//GM_setValue("allTrails", "");
	g_allTrails = unpackTrails(GM_getValue("allTrails", null));
	if(!g_allTrails) g_allTrails = [[g_currentPage]];
	//console.log(g_allTrails);
	g_trail = currentTrail();
	
	requestLinksForPage(g_currentPage.toStr(), function(links) { addLinks(links); installLinkHandlers(); });
}

function addGlobalStyle(css) {
    var head, style;
    head = document.getElementsByTagName('head')[0];
    if (!head) { return; }
    style = document.createElement('style');
    style.type = 'text/css';
    style.innerHTML = css;
    head.appendChild(style);
}

function addLinks(links) {
	links.sort(function(left, right) { return left.rating < right.rating; });

	addGlobalStyle('.row0 { background-color: #F5F5F5; height: 2em; } .row1 {		background-color: #F3F3F3;	}	.row2 {		background-color: #F0F0F0;	} #softLinksContainer { border: solid #CCCCCC 1px; margin-top: 5px; padding: 1px; } #softlinks { width: 100%;	}	#softlinks td {	width:20%;	text-align: center;		font-family: sans-serif;		font-weight: bold;	}');
	
	var html = "<div id=\"softLinksContainer\"><table id=\"softlinks\">";
	var linksPlaced = 0;
	for(var i = 0; i < 3; i++) {
		html += "<tr class=\"row" + i + "\">";
		for(var k = 0; k < 5; k++) {
			if(linksPlaced < links.length) {
				var page = WikiPage.fromStr(links[linksPlaced].to_str);
				html += "<td><a href=\"" + page.toUrl() + "\">" + page.title.replace(/_/g, ' ') + "</a></td>";
				linksPlaced++;
			}
			else {
				html += "<td></td>";
			}
		}
		html += "</tr>";
		if(linksPlaced >= links.length) {
			break;
		}
	}
	html += "</table></div>";
	document.getElementById('content').innerHTML += html;
}

function requestLinksForPage(pageStr, cb) {
	GM_xmlhttpRequest({
		method: 'GET',
		url: API_PATH + pageStr,
		onload: function(response) { /*console.log(pageStr + ': ' + response.responseText);*/ cb(eval('(' + response.responseText + ')').links); },
	});
}

/* class WikiPage */
function WikiPage(lang, title) {
	this.lang = lang;
	this.title = title;
	this.toUrl = function() {
		return 'http://' + lang + '.wikipedia.org/wiki/' + title;
	}
	this.toStr = function() {
		return lang + '|' + title;
	}
	this.toString = function() {
		return '"' + this.toStr() + '"';
	}
	this.equals = function(other) { return this.lang == other.lang && this.title == other.title; }
}
WikiPage.fromUrl = function(url) {
	var res = FULL_URL_REGEX(url);
	return res && new WikiPage(res[1], res[2]); // shoutout to benjamn
}
WikiPage.fromStr = function(str) {
	var res = str.split('|');
	return new WikiPage(res[0], res[1])
}

function filter(list, fn) {
	var res = [];
	for(var el in list) {
		if (fn(list[el])) {
			res.push(list[el]);
		}
	}
	return res;
}

function printit(response) {
	console.log(response.responseText);
}

function pushLinksToServer() {
	if (g_trailLinksToRegister.length <= 0) return;
	var to_page = g_trailLinksToRegister[0][1];
	var queryString = "num_links=" + g_trailLinksToRegister.length;
	for (var k = 0; k < g_trailLinksToRegister.length; k++) {
		queryString += "&from_str" + k + "=" + g_trailLinksToRegister[k][0].toString() + "&to_str" + k + "=" + g_trailLinksToRegister[k][1].toString() + "&weight" + k + "=" + g_trailLinksToRegister[k][2];
	}
//	console.log(queryString);
	GM_xmlhttpRequest({
		method: 'POST',
		url: API_PATH,
		data: queryString,
		headers: {'Content-type':'application/x-www-form-urlencoded'},
//		onload: function(response) { alert(response.responseText); }
	});
	g_trailLinksToRegister = [];
}

function max(l, r) {
	return l > r ? l : r;
}

function handleTransition(to) {
	var trail = g_trail.slice();
	g_allTrails.push(trail);
	trail.push(to);
	
	for(var k = 0; k < trail.length - 1; k++) {
		var from = g_trail[k];
		registerTrailLink(from, to, 4 - Math.abs(4 - k % 4) + (Math.random() - .5) / 10 + .2);
		registerTrailLink(to, from, 4 - Math.abs(4 - k % 4) + (Math.random() - .5) / 10 + .2);
	}
	
	if(trail.length > MAX_TRAIL_DEPTH)
		trail.shift();
	
	if(g_allTrails.length > MAX_TRAIL_DEPTH * MAX_TRAIL_DEPTH) {
		g_allTrails.shift(); // eh, sometimes we lose information. too bad.
	}
	
	pushLinksToServer();
}

function registerTrailLink(from, to, ratio) {
	g_trailLinksToRegister.push([from, to, ratio]);
}

function linkHandler(linkedPage) {
	handleTransition(linkedPage);
}

function installLinkHandlers() {
	links = document.getElementsByTagName('a');
	for (var i = 0; i < links.length; i++) {
		var link = links[i];
		var page = WikiPage.fromUrl(link.href);
		if (page) {
			(function(thePage) {
				var destUrl = link.href;
				link.addEventListener("mouseup", function(e) { 
					//e.stopPropagation();
					//e.preventDefault();
					linkHandler(thePage);
					GM_setValue("allTrails", g_allTrails.toString());
					//window.location.href = destUrl;
				}, false);
			})(page);
		}
	}
}

pageLoaded(window.location.href);
