/*
 *	Highlights matching text in marked DOM nodes but does not span between DOM nodes
 *	Only text nodes are modified, other nodes and thus other operating is not affected
 *	serching for bbl won't find it in blubb<a href="#">link</a>
 */

function kwasiHighlightText() {
	var HIGHLIGHT_ID = "gBody";
	var HILIT_CLASS = "hilit";
	var MIN_LENGTH = 1;					//length of the shortest hilitable item (must be > 0)
	var textNodes_a = new Array();		//Stores all DOM Nodes where to place highlights
	var nodeRelation_a = new Array();	//Stores relations between created and replaced nodes for restoring
	var occurenceCounter = 0;

	/*
	 *	prepares the STRING for use with Highlight, then calls highlight (public)
	 */
	//fix me: make me more compact
	this.query = function( KEYWORD_A ) {
		var keyWord_a = KEYWORD_A.slice( 0, KEYWORD_A.length ); //copy to not alter the keyword array
		restore();
		occurenceCounter = 0;
		
		if ( typeof( keyWord_a ) == "string" ) {
			return occurenceCounter;
		}
		
		var keyWords_a = new Array();
		
		for ( var key = 0; key < keyWord_a.length; key++ ) {
			var maxLength = 0;
			
			for ( var keyb = 0; keyb < keyWord_a[key].length; keyb++ ) {
				if ( keyWord_a[key][keyb].length > maxLength ) {
					maxLength = keyWord_a[key][keyb].length;
				}
			}
			
			if ( maxLength >= MIN_LENGTH ) {
				for ( var keyb = 0; keyb < keyWord_a[key].length; keyb++ ) {
					keyWords_a.push( new RegExp( keyWord_a[key][keyb], "" ) );
				}
			}
		}
		
		if ( keyWords_a.length ) {
			highlight( keyWords_a );
		}
		
		return occurenceCounter;
	};
		
	/*
	 *	highlights the occurences of the keywords in the nodes that are supposed to be highliten (private)
	 */
	function highlight( KEYWORD_A ) {
		traverseDOM_r( document.getElementById( "gBody" ) );
		
		//process text nodes
		for ( var node = 0; node < textNodes_a.length; node++ ) {
			var text = textNodes_a[node].nodeValue;
			var keyPos_a = markKeywordsInString( text.toLowerCase().kwasiCUTBL().kwasiSNLC(), KEYWORD_A );
			
			if ( keyPos_a.length ) {
				var containerNode = document.createElement( "span" );
				occurenceCounter += keyPos_a.length;
				keyPos_a.unshift( { start:0, end:0 } );	//head
				
				for ( var pos = 1; pos < keyPos_a.length; pos++ ) {
					var newNode = document.createElement( "span" );
					newNode.className = "hilit";
					newNode.appendChild( document.createTextNode( text.substring( keyPos_a[pos].start, keyPos_a[pos].end ) ) );
					containerNode.appendChild( document.createTextNode( text.substring( keyPos_a[pos-1].end, keyPos_a[pos].start ) ) );	//not hilit
					containerNode.appendChild( newNode );	//hilit
				}
				
				containerNode.appendChild( document.createTextNode( text.substring( keyPos_a[keyPos_a.length - 1].end, text.length ) ) ); //tail
				textNodes_a[node].parentNode.replaceChild( containerNode, textNodes_a[node] );
				nodeRelation_a.push( { before:textNodes_a[node], after:containerNode } );
			}
		}
	}
	
	/*
	 *	generates an Array of markers of areas containing keywords from KEYWORD_A in a STRING to highlight (private)
	 */
	function markKeywordsInString( STRING, KEYWORD_A ) {
		var keyPos_a = new Array();
		var sortByStart = function( A, B ) { return A.start - B.start; };
		
		for ( var key = 0; key < KEYWORD_A.length; key++ ) {
			var string = STRING;
			var pos = string.search( KEYWORD_A[key] );			
			var head = 0;
			
			while ( pos != -1 ) {
				var test = KEYWORD_A[key].exec( string );
				keyPos_a.push( { start:( head + pos ), end:( head + pos + test[0].length ) } );// KEYWORD_A[key].length ) } );
				head += ( pos + 1 );
				string = string.substring( ( pos + 1 ), string.length );
				pos = string.search( KEYWORD_A[key] );
			}
		}
		
		//sort keywords by the order as they appear in the document
		keyPos_a.sort( sortByStart );
		
		//union overlapping or touching elements
		for ( var pos = 0; pos < keyPos_a.length; pos++ ) {
			while ( ( pos+1 < keyPos_a.length ) && ( keyPos_a[pos+1].start <= keyPos_a[pos].end ) ) {
				if ( keyPos_a[pos].end < keyPos_a[pos+1].end ) {
					keyPos_a[pos].end = keyPos_a[pos+1].end;
				}
				
				keyPos_a.splice( pos+1, 1 );
			}
		}
		
		return keyPos_a;
	}
	
	/*
	 *	Replaces the created DOM nodes with the backed up text nodes (private)
	 */
	function restore() {
		while ( nodeRelation_a.length ) {
			var node = nodeRelation_a.pop();
			node.after.parentNode.replaceChild( node.before, node.after );
			//fix me: node.after has to be erased to free memory
			//DOMGarbageCollector.appendChild( node.after );
			//eraseNodes_r( DOMGarbageCollector );
			//node = null;
		}
		
		while ( textNodes_a.length ) {
			textNodes_a.pop();
		}
	}
	
	/*
	//fix me: this is not working as expected
	var DOMGarbageCollector = document.createElement( "div" );

	function eraseNodes_r( DOM_ELEMENT ) {
		//debug( DOM_ELEMENT )
		if ( DOM_ELEMENT.childNodes ) {
			for ( var node = 0; node < DOM_ELEMENT.childNodes.length; node++ ) {
				eraseNodes_r( DOM_ELEMENT.childNodes[node] );
			}
		}
		
		if ( DOM_ELEMENT.parentNode != null ) {
			DOM_ELEMENT.parentNode.removeChild( DOM_ELEMENT );
		}
	}
	*/
	
	/*
	 *	Recursive function puts all text nodes to highlight into an Array (private)
	 */
	function traverseDOM_r( DOM_ELEMENT ) {
		//if we have a text node and that node does not contain only whitespaces
		if ( ( DOM_ELEMENT.nodeName == "#text" ) && ( DOM_ELEMENT.nodeValue.strip().length > 0 ) ) {
			textNodes_a.push( DOM_ELEMENT );
		} else {
			for ( var node = 0; node < DOM_ELEMENT.childNodes.length; node++ ) {
				traverseDOM_r( DOM_ELEMENT.childNodes[node] );
			}
		}
	}
}
