var HighlightRanges = new Array();
var SCRIPT_PATH = '/annotations.php';
var BASE_NODE_CLASS = 'book-content';
var PopUpSettings = 'resizable,toolbar=no,location=no,scrollbars=no,status=no,width=251,height=225,left=0,top=0';
var NoteLocation;
var NoteID;
var NoteOffset;
var NOTE_ICON_SRC = 'http://static.ccel.org/pix/note.png';


function annotateText () {
	var postData = 'op=request&uid='+uid+'&url='+window.location.pathname;

	var request = newXHR();
	request.onreadystatechange = function () {if (request.readyState == 4 && request.status == 200) parseAnnotations(request.responseXML);};
	request.open('POST', SCRIPT_PATH, true);
	request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
	request.send(postData);
}

function parseAnnotations (xmlDoc) {
   xmlDoc = xmlDoc.getElementsByTagName('annotations')[0];
	if (xmlDoc.hasChildNodes()) {
		var ids = xmlDoc.getElementsByTagName('aid');
		var annotations = xmlDoc.getElementsByTagName('annotation');
		for (var i=0; i<annotations.length; i++) {
			if (annotations[i].getElementsByTagName('type')[0].firstChild.nodeValue == 'highlight') {
				var start = annotations[i].getElementsByTagName('start')[0];
				var end = annotations[i].getElementsByTagName('end')[0];
				var color = annotations[i].getElementsByTagName('color')[0].firstChild.nodeValue;
				var xStart = new Location(start.getElementsByTagName('id')[0].firstChild.nodeValue, start.getElementsByTagName('offset')[0].firstChild.nodeValue);
				var xEnd = new Location(end.getElementsByTagName('id')[0].firstChild.nodeValue, end.getElementsByTagName('offset')[0].firstChild.nodeValue);
				HighlightRanges[ids[i].firstChild.nodeValue] = Array(xStart, xEnd, color);
				a_highlight(xStart, xEnd, color);
		}
			else {
				var start = annotations[i].getElementsByTagName('start')[0];
				placeNote(new Location(start.getElementsByTagName('id')[0].firstChild.nodeValue, start.getElementsByTagName('offset')[0].firstChild.nodeValue), ids[i].firstChild.nodeValue);
			}
		}
	}
}

function addAnnotation (type, color) {
	var range = getPosition();
	// check to ensure user has selected something and that the selection with within the book's text
	if (range != 0 && range != -1 && childOf(range.startContainer, 'book-content') && childOf(range.endContainer, 'book-content') && !childOf(range.startContainer, 'mnote') && !childOf(range.endContainer, 'mnote')) {
		var startOffset = collapseSpacesOffset(range.startContainer, range.startOffset);
		var endOffset = collapseSpacesOffset(range.endContainer, range.endOffset);
		var path = getPath(range.startContainer);
		var start = new Location(path[path.length-1].id, getOffset(path) + startOffset);
		path = getPath(range.endContainer);
		var end = new Location(path[path.length-1].id, getOffset(path) + endOffset);
		
		if (type == 'highlight') {
         Hightlight_Color = color;
			submitHighlight(start, end, color);
			a_highlight(start, end, color);
		}
		else if (type == 'note') {
			NoteLocation = end; 
			popupNoteEditor(range.endContainer.parentNode, OnInsert, '');
		}
		else if (type == 'remove') {
			removeHighlights(start, end);
		}
	}
	else if (range == 1) {
		alert('Due to limitations of Internet Explorer, please either use Firefox or not begin or end selection on a blank line.');
	}
	else {
		alert('Please highlight text within book page and try again.');
	}
}

function a_highlight (start, end, color) {
	var loc = start.convert();
	var startNode = loc[0];
	var startOffset = loc[1];
	loc = end.convert();
	var endNode = loc[0];
	var endOffset = loc[1];
	var currentNode = startNode;
	
	for (;;) {
		// currentNode is a middle node that could possibly contain the end node
		if (currentNode != endNode && currentNode != startNode) {
			if (currentNode.hasChildNodes()) {
				var nextNode = findNextNode(currentNode);
				var done = highlightChildren(currentNode, endNode, endOffset, color);
				if (done) {
					break;
				}
				else {
					currentNode = nextNode;
					continue;
				}
				break;
			}
			else {
				currentNode = highlightNode(currentNode, color);
			}
		}
		// currentNode is the start node
		else if (currentNode == startNode) {
			// the start node and the end node are the same
			if (startNode == endNode) {
				currentNode = highlightSelection(currentNode, startOffset, endOffset, color);
				break;
			}
			else {
				currentNode = highlightSelection(currentNode, startOffset, getTextContent(currentNode).length, color);
			}
		}
		// currentNode is the end node
		else {
			highlightSelection(currentNode, 0, endOffset, color);
			break;
		}
		currentNode = findNextNode(currentNode);
	}
}

function highlightChildren (currentNode, endNode, endOffset, color) {
	var children = currentNode.childNodes;
	var length = children.length;  // store length in case number of children changes
	var done = false;
	for (var i=0; i<length; i++) {
		// if node has children recursively proccess its children
		if (children[i].className == 'mnote') {}
		else if (children[i].hasChildNodes()) {
			done = highlightChildren(children[i], endNode, endOffset, color);
			if (done) {
				return true;
			}
		}
		// if node doesn't have children and isn't the endNode
		else if (children[i] != endNode){
			highlightNode(children[i], color);
		}
		// if node is the endNode
		else {
			highlightSelection(endNode, 0, endOffset, color);
			return true;
		}
	}
	return done;
}

function highlightNode (node, color) {
   var content = getTextContent(node);
   if (content != '') {
      var highlight = document.createElement('span');
      highlight.className = 'highlight';
      highlight.style.backgroundColor = color;
      highlight.appendChild(node.cloneNode(true));
      node.parentNode.replaceChild(highlight, node);
      return highlight;     
   }
   else {
      return node;
   }
}

function highlightSelection (node, startOffset, endOffset, color) {
	var highlight = document.createElement('span');
	highlight.className = 'highlight';
	highlight.style.backgroundColor = color;
	var content = getTextContentWithSpaces(node).replace(/[\t\n\r ]+/g, ' ');
	if (startOffset == 0 && endOffset == content.length) {
		return highlightNode(node, color);
	}
	else {
		var beginning = content.substring(0, startOffset);
		var middle = content.substring(startOffset, endOffset);
		var end = content.substring(endOffset, content.length);
		highlight.appendChild(document.createTextNode(middle));	
		if (startOffset == 0) {
			var endNode = document.createTextNode(end);
			node.parentNode.insertBefore(highlight, node);
		}
		else if (endOffset == content.length) {
			var endNode = highlight;
			node.parentNode.insertBefore(document.createTextNode(beginning), node);
		}
		else {
			var endNode = document.createTextNode(end);
			node.parentNode.insertBefore(document.createTextNode(beginning), node);
			node.parentNode.insertBefore(highlight, node);
		}
		node.parentNode.replaceChild(endNode, node);
		return endNode;
	}
}

function submitHighlight (start, end, color) {
	var xmlData = '<annotation>'+
							'<type>highlight</type>'+
							'<start><id>'+start.id+'</id><offset>'+start.offset+'</offset></start>'+
							'<end><id>'+end.id+'</id><offset>'+end.offset+'</offset></end>'+
							'<color>'+color+'</color>'+
						'</annotation>';

	var postData = 'op=insert&uid='+uid+'&url='+window.location.pathname+'&data='+xmlData;

	var request = newXHR();
	request.onreadystatechange = function () {if (request.readyState == 4 && request.status == 200) {
			HighlightRanges[request.responseText] = Array(start, end, color);}};
	request.open('POST', SCRIPT_PATH, true);
	request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
	request.send(postData);
}

function placeNote (lPoint, aid) {
	var nPoint = lPoint.convert();
	var note = document.createElement('img');
	note.setAttribute('width', '11');
	note.setAttribute('height', '11');
	note.className = 'note';
	note.setAttribute('src', NOTE_ICON_SRC);
	note.onmouseover = function() {popupNote(this, aid);};
	note.onmouseout = function() {leaveVerse();};
	var content = getTextContentWithSpaces(nPoint[0]).replace(/\s+/g, ' ');

	if (nPoint[1] == 0) {
		nPoint[0].parentNode.insertBefore(note, nPoint[0]);
	}
	else if (nPoint[1] == content.length) {
		nPoint[0].parentNode.insertBefore(nPoint[0].cloneNode(true), nPoint[0]);
		nPoint[0].parentNode.replaceChild(note, nPoint[0]);
	}
	else {
		var beginning = content.substring(0, nPoint[1]);
		var end = content.substring(nPoint[1], content.length);
		nPoint[0].parentNode.insertBefore(document.createTextNode(beginning), nPoint[0]);
		nPoint[0].parentNode.insertBefore(note, nPoint[0]);
		nPoint[0].parentNode.replaceChild(document.createTextNode(end), nPoint[0]);
	}
}

function submitNote (note) {
	note = note.replace(/^\s+|\s$/g, '');
	if (note != '') {
		note = replaceSpecialChars(note);
		var xmlData = '<annotation>'+
								'<type>note</type>'+
								'<start><id>'+NoteLocation.id+'</id><offset>'+NoteLocation.offset+'</offset></start>'+
								'<text>'+note+'</text>'+
							'</annotation>';

		var postData = 'op=insert&uid='+uid+'&url='+window.location.pathname+'&data='+xmlData;

		var request = newXHR();
		request.onreadystatechange = function () {if (request.readyState == 4 && request.status == 200) {
				placeNote(new Location(NoteLocation.id, NoteLocation.offset), request.responseText);}};
		request.open('POST', SCRIPT_PATH, true);
		request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
		request.send(postData);
	}
	closeEditor();
}

function updateNote (note, id) {
	note = note.replace(/^\s+|\s$/g, '');
	if (note != '') {
		note = replaceSpecialChars(note);
		var xmlData = '<annotation>'+
								'<type>note</type>'+
								'<start><id>'+NoteID+'</id><offset>'+NoteOffset+'</offset></start>'+
								'<text>'+note+'</text>'+
							'</annotation>';

		var postData = 'op=update&id='+id+'&data='+xmlData;

		var request = newXHR();
		request.open('POST', SCRIPT_PATH, true);
		request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
		request.send(postData);
	}
	else {
		deleteAnnotation(id);
		refreshPage();
	}
	closeEditor();
}

function removeHighlights (start, end) {
	for (var id in HighlightRanges) {
		// start of selection before or same as start of highlight
		if (start.compare(HighlightRanges[id][0]) >= 0) {
			// end of selection before end of highlight AND end of selection after start of highlight
			if (end.compare(HighlightRanges[id][1]) > 0 && end.compare(HighlightRanges[id][0]) < 0) {
				submitHighlight(end, HighlightRanges[id][1], HighlightRanges[id][2]);
				deleteAnnotation(id);			
			}
			// end of selection after or same as end of highlight
			else if (end.compare(HighlightRanges[id][1]) <= 0) {
				deleteAnnotation(id);
			}
			else {
				updateTimestamp(id);
			}
		}
		// end of seletion after or same as end of highlight
		else if (end.compare(HighlightRanges[id][1]) <= 0) {
			// start of selection after start of highlight AND start of selection before end of highlight
			if (start.compare(HighlightRanges[id][0]) < 0 && start.compare(HighlightRanges[id][1]) > 0) {
				submitHighlight(HighlightRanges[id][0], start, HighlightRanges[id][2]);
				deleteAnnotation(id);	
			}
			// start of selection before or same as start of highlight
			else if (start.compare(HighlightRanges[id][0]) >= 0) {
				deleteAnnotation(id);
			}
			else {
				updateTimestamp(id);
			}
		}
		// selection within highlight
		else {
         submitHighlight(HighlightRanges[id][0], start, HighlightRanges[id][2]);
         submitHighlight(end, HighlightRanges[id][1], HighlightRanges[id][2]);
         deleteAnnotation(id);
		}
	}
	refreshPage();
}

function deleteAnnotation (id) {
	var postData = 'op=delete&id='+id;

	var request = newXHR();
	request.open('POST', SCRIPT_PATH, true);
	request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
	request.send(postData);
}

function updateTimestamp (id) {
	var postData = 'op=time&id='+id;
	
	var request = newXHR();
	request.open('POST', SCRIPT_PATH, true);
	request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
	request.send(postData);
}

function refreshPage() {
	if (window.location.pathname.search("niv") == -1) {
		var postData = 'op=refresh&uid='+uid+'&url='+window.location.pathname;

		var request = newXHR();
		request.onreadystatechange = function () {
			if (request.readyState == 4 && request.status == 200) {
				var beginning = request.responseText.indexOf('<div');
				var end = request.responseText.lastIndexOf('</div>');
				var content = request.responseText.substring(beginning+26, end);
				var node = document.getElementById('book_navbar_top');
				node.nextSibling.innerHTML = content;
				removeLeadingSpaces();
				annotateText();
				highlightSearch();
				reFootNote();
			}
		};

		request.open('POST', SCRIPT_PATH, true);
		request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
		request.send(postData);
	}
	else {
		processXLinks();
	}

	HighlightRanges = new Array();
}

function reFootNote () {
   var footNotes = document.getElementsByTagName('sup');
   for (var i=0; i<footNotes.length; i++) {
      if (footNotes[i].className == 'Note') {
         initNote(footNotes[i].nextSibling.id);      
      }
   }
}

function Location (id, offset) {
	this.id = id;
	this.offset = offset;
	this.convert = Location_convert;
	this.compare = Location_compare;
}

function Location_convert () {
	return findOffset(document.getElementById(this.id), this.offset);
}

function Location_compare (right) {
	if (this.id == right.id) {
		if (this.offset < right.offset) {
			return 1;
		}
		else if (this.offset == right.offset) {
			return 0;
		}
		else {
			return -1;
		}
	}
	else {
		var path1 = getPathToBase(document.getElementById(this.id));
		var path2 = getPathToBase(document.getElementById(right.id));
		var i = path1.length-1;
		var j = path2.length-1;
		while (path1[i] == path2[j]) {
			i--;
			j--;
		}
		
		var child = path1[i+1].firstChild;
		
		if (i < 0) {
         path1[0] = this.convert()[0];
         i = 0;
		}
		if (j < 0) {
         path2[0] = right.convert()[0];
         j = 0;
		}
		
		while (child != path1[i] && child != path2[j]) {
			child = child.nextSibling;
		}
		if (child == path1[i]) {
			return 1;
		}
		else {
			return -1;
		}
	}
}

function getPosition () {
   var selection;
   var range;
	// Emulate Mozilla range for IE
   if (navigator.appName == 'Microsoft Internet Explorer') {
      selection = document.selection;
      range = selection.createRange();
      if (range.text == '') {
         return 0;
      }
	   var start = range.duplicate();
      var end = range.duplicate();
      start.collapse(true);
      end.collapse(false);
      start = start.parentElement();
      end = end.parentElement();
		if (start.id == 'nivChapter' || end.id == 'nivChapter') {
			return -1;
		}
      var sPath = getPath(start);
      var ePath = getPath(end);
      var startRange = range.duplicate();
      var endRange = range.duplicate();
      startRange.moveToElementText(start);
      endRange.moveToElementText(end);
      startRange.setEndPoint('EndToStart', range);
      endRange.setEndPoint('EndToEnd', range);
      var sOffset = startRange.text.length;
      var eOffset = endRange.text.length;
      var lStart = new Location(sPath[sPath.length-1].id, getOffset(sPath)+sOffset);
      var lEnd = new Location(ePath[ePath.length-1].id, getOffset(ePath)+eOffset);
      aStart = lStart.convert();
      aEnd = lEnd.convert();
		if (aStart[0] == aEnd[0] && (aEnd[1] - aStart[1] == 1) && aStart[0].nodeValue.charAt(aStart[1]) == ' ') {
			return 0;
		}
		else {
			return new MozRange(aStart[0], aStart[1], aEnd[0], aEnd[1]);
		}
   }
   else {
      selection = window.getSelection();
     	if (selection.toString == '') {
         return 0;
      }
		else {
			range = selection.getRangeAt(0);
			if (range.startContainer.hasChildNodes()) {
				range.setStart(range.startContainer.childNodes[range.startOffset], 0);
			}
			if (range.endContainer.hasChildNodes()) {
				range.setEnd(range.endContainer.childNodes[range.endOffset], 0);
			}
			return range;
		}
   }
}

function MozRange (start, sOffset, end, eOffset) {
   this.startContainer = start;
   this.startOffset = sOffset;
   this.endContainer = end;
   this.endOffset = eOffset;
}

function findNextNode (node) {
	for (;;) {
		// go to siblings first
		if (node.nextSibling != null) {
			node = node.nextSibling;

			if (node.className == 'mnote') {
				return node.nextSibling;
			}
			else {
				return node;
			}
		}
		// if no siblings go to parent node's closests sibling
		node = node.parentNode;
	}
}

function childOf (node, parent) {
	while (node.className != parent) {
		node = node.parentNode;
		if (node.tagName == 'BODY') {
			return false;
		}
	}
	return true;
}

function getPathToBase (node) {
	var path = new Array();
	path[0] = node;
	var i = 0;
	do {
		path[i+1] = path[i++].parentNode;
	} while (path[i].className != BASE_NODE_CLASS);
	return path;
}

function findOffset (node, pOffset) {
	var children = node.childNodes;
	for (var i=0; i<children.length; i++) {
		if (children[i].nodeType == 8) {
			continue;
		}

		var point = null;
		var offset = pOffset - getTextContent(children[i]).length;

		if (children[i].hasChildNodes() && (offset <= 0)) {
			point = findOffset(children[i], pOffset);
		}
		else if (offset <= 0) {
			point = Array(children[i], pOffset);
		}

		pOffset = offset;
		if (point != null) {
			return point;
		}
	}
}


function getPath (node) {
	var path = new Array();
	path[0] = node;
	var i = 0;
	while (path[i].nodeType == 3 || path[i].getAttribute('id') == null || path[i].getAttribute('id') == '' || !path[i].hasChildNodes()) {
		path[i+1] = path[i++].parentNode;
	}
	return path;
}

function getOffset (path) {
	var count = path.length-1;
	var children = path[count].childNodes;
	var offset = 0;
	for (var i=1; i<=count; i++) {
		var j = 0;
		while (children[j] != path[count-i]) {
			if (children[j].nodeType != 8) {
				offset += getTextContent(children[j]).length;
			}
			j++;
		}
		children = children[j].childNodes;
	}
	return offset;
}

function replaceSpecialChars (text) {
	text = text.replace(/</g, '&lt;');
	text = text.replace(/>/g, '&gt;');
	text = text.replace(/&/g, '%26');
	return text;
}

function getTextContent (node) {
	return stripChars(getTextContentWithSpaces(node));
}

function getTextContentWithSpaces (node) {
   if (navigator.appName == 'Microsoft Internet Explorer') {
      if (node.nodeType == 3) {
         return node.nodeValue;
      }
      else {
         return node.outerText;
      }
   }
   else {
      return node.textContent;
   }
}

function collapseSpacesOffset (node, offset) {
	var text = getTextContentWithSpaces(node).substr(0, offset);
	text = stripChars(text);
	return text.length;
}

function stripChars (text) {
	text = text.replace(/[\t\n\r ]+/g, ' ');
	text = text.replace(/^ $/g, '');
	text = text.replace(/<\!--.*?-->/g, '   ');
	return text;
}

function removeLeadingSpaces () {
	if (navigator.appName != 'Microsoft Internet Explorer') {
		var main = document.getElementById('main').childNodes[7];
		var nodes = main.getElementsByTagName('p');
		for (var i=0; i<nodes.length; i++) {
			removeSpace(nodes[i]);
		}
	}
}

function removeSpace (node) {
	if (node.nodeType == 3 && getTextContentWithSpaces(node).length > 0) {
		var newNode = document.createTextNode(node.nodeValue.replace(/(^[\t\n\r ]+)(.*)/g, "$2"));
		node.parentNode.replaceChild(newNode, node);
		return true;
	}
	else if (node.hasChildNodes() && getTextContentWithSpaces(node).length > 0) {
		var test = false;
		var next = node.firstChild;
		while (!test) {
			test = removeSpace(next);
			next = next.nextSibling;
		}
		return true;
	}
	else {
		return false;
	}
}

