/* this file is a part of Check4Change 
 *
 * Copyright 2010 Ronen Zilberman
 * See license.txt for details.
 */

/*
c4cDocSelection object holds selection info for a specific document.
Used internally by c4cSelection that may hold several if a frame doc.
*/ 
function c4cDocSelection(doc, range) {
	this.getOriginalText = c4cDocSelection_getOriginalText;	
	this.getText = c4cDocSelection_getText;
	this.compare = c4cDocSelection_compare;
	this.restore = c4cDocSelection_restore;
	this.recreateRange = c4cDocSelection_recreateRange;
	this.updateTextOnly = c4cDocSelection_updateTextOnly;

	
	//-----------------------
	this.text = range.toString();
	
	// save container, by array of child indices.
	this.startPath = c4cUti_domNodeToPath(doc,range.startContainer);
    this.endPath = c4cUti_domNodeToPath(doc,range.endContainer);	
	
	this.startOffset = range.startOffset;
	// if end offset is the end of the element, mark it as such.
	this.endOffset   = range.endOffset;
	if (c4cUti_getNodeMaxOffset(doc, range.endContainer) == range.endOffset) {
		this.endOffset   = 10000; 
	}

}

function c4cDocSelection_updateTextOnly(doc, range) {
	if (range) {
		this.text = range.toString();
	} else {
		this.text = "";
	}
	
}

function c4cDocSelection_recreateRange(doc, doAbsPos) {
    try {
		// recreate the range
		var range = doc.createRange();
		
		var sres = c4cUti_domPathToNode(doc, this.startPath, doAbsPos);
		var startContainer = sres.node;
		var startOffset; 
		if (sres.bestGuess) {
			startOffset = 0;
		} else {
			 startOffset = Math.min(this.startOffset, c4cUti_getNodeMaxOffset(doc, startContainer));
		}
		
		var eres = c4cUti_domPathToNode(doc, this.endPath, doAbsPos);
		var endContainer = eres.node;
		var endOffset;		
		if (eres.bestGuess) {
			endOffset = c4cUti_getNodeMaxOffset(doc, endContainer)
		} else {
			endOffset = Math.min(this.endOffset, c4cUti_getNodeMaxOffset(doc, endContainer));			
		}
		
		
		range.setStart(startContainer, startOffset);
		range.setEnd(endContainer,endOffset);

	    return range;
     } catch (e) {
		 // if we failed to reconstruct the selection, return null;
		 c4cUti_assert(false, "error reconstructing range: " + e);
		 return null;
	   }
	

}

function c4cDocSelection_getOriginalText() {
	return this.text;
}

function c4cDocSelection_getText(doc, doAbsPos) {
    try {
		// recreate the range
		var range = this.recreateRange(doc, doAbsPos);
		if (range)
			return range.toString();
		else return null;
     } catch (e) {
		 // shouldnt get here.
		 return null;
	   }
}

function c4cDocSelection_compare(doc, doAbsPos) {
try {
	var newstr = this.getText(doc, doAbsPos);
	var oldstr = this.text;
	
	// remove whitespaces. Due to a bug in FF thats adds a whitespace sometimes.
	newstr = newstr.replace(/\s/g,'');
	oldstr = oldstr.replace(/\s/g,'');
	
	var res = (newstr == oldstr);
	return res;
} catch (e) {return false;}	
}

// try to restore the selection. returns true upon success
function c4cDocSelection_restore(win, doAbsPos) {
    try {
		// recreate the range
		var	doc = win.document;
		var range = this.recreateRange(doc, doAbsPos);
		if (!range) return false;

		win.getSelection().removeAllRanges();
		win.getSelection().addRange(range);
		return true;


     } catch (e) {
		 // shouldn't get here.
		 return false;
	   }

}



//***********************************************************************************************************************

// c4cSelection holds selection (position and text instance) and scroll info for a window.

function c4cSelection(win, abspos) {
	this.constructor = c4cSelection_constructor;
	this.hasSelection = c4cSelection_hasSelection;
	this.restoreScroll = c4cSelection_restoreScroll;
	this.restoreSelection = c4cSelection_restoreSelection;
	this.hasSelectionChanged = c4cSelection_hasSelectionChanged;
	this.getSelectionText = c4cSelection_getSelectionText;
	this.getStoredText = c4cSelection_getStoredText;
	this.updateStoredText = c4cSelection_updateStoredText;
	
	//-----
	this.abspos = abspos;
	this.constructor(win);
}

function c4cSelection_constructor(topWindow) {
	// get the framepaths: the path arrays to all the frames.
	this.framePaths = c4cUti_getFramePaths(topWindow);

	// save the selection and position of all frames.
	this.frameSelections = [];
	this.frameScrollPositions = [];
	this.count = 0;

	// go over frames and create frameSelections and frameScrollPostions
	// frameSelections is an array of c4cSelection's one per frame in this.framePaths
	// frameScrollPostions is an array of [scrollX scrollY] per frame
	for (var i=0; i< this.framePaths.length; i++) {
		// get frame
		var frameWindow = c4cUti_getFrameWindow(topWindow, this.framePaths[i]);
		// store scroll
		this.frameScrollPositions[i] = [frameWindow.scrollX, frameWindow.scrollY];
		// get selection
		var sel = c4cUti_getSelection(frameWindow);
		// if valid store it, otherwise set null
		if (sel == null)
			  this.frameSelections[i] = null;
 		else {
			// this means the selection exists and is not ""
			this.frameSelections[i] = new c4cDocSelection(frameWindow.document, sel);
			this.count++;
		}
	}

}

function c4cSelection_hasSelection() {
	return (this.count != 0);
}


function c4cSelection_restoreScroll(topWindow) {
try {
	if (!(topWindow && this.framePaths && this.frameScrollPositions)) return;

	for (var i=0; i< this.framePaths.length; i++) {
		var frameWindow = c4cUti_getFrameWindow(topWindow, this.framePaths[i]);
		if (frameWindow == null) return false;

		frameWindow.scroll(this.frameScrollPositions[i][0], this.frameScrollPositions[i][1]);
	}
	return true;
	
} catch (e) {return false;}	
}


// tryies to restore the selections in the page. returns true upon complete success.
function c4cSelection_restoreSelection(topWindow) {
	if (!(topWindow && this.framePaths && this.frameSelections)) return false;
	for (var i=0; i< this.framePaths.length; i++) {
		var c4csel = this.frameSelections[i];
		if (c4csel == null) continue;
		
		var frameWindow = c4cUti_getFrameWindow(topWindow, this.framePaths[i]);
		// if no such frame, change.
		if (frameWindow == null) return false;
		
		// restore selection
		var res = c4csel.restore(frameWindow, this.abspos);
		if (res == false) return false;
		
	}

	return true;
}


function c4cSelection_hasSelectionChanged(topWindow) {
	for (var i=0; i< this.framePaths.length; i++) {
		var c4csel = this.frameSelections[i];
		if (c4csel == null) continue;

		var frameWindow = c4cUti_getFrameWindow(topWindow, this.framePaths[i]);
		// if no such frame, change.
		if (frameWindow == null) return true;
		// compare document
		if (!c4csel.compare(frameWindow.document, this.abspos)) {
			return true;
		}
	}

	return false;
}


function c4cSelection_getSelectionText(topWindow) {
	var res="";

	for (var i=0; i< this.framePaths.length; i++) {
		var c4csel = this.frameSelections[i];
		if (c4csel == null) continue;

		var frameWindow = c4cUti_getFrameWindow(topWindow, this.framePaths[i]);
		if (frameWindow == null) continue; // NOTE CHECK THIS

		res += c4cUti_trimWS(c4csel.getText(frameWindow.document));
	}

	return res;
}

function c4cSelection_getStoredText() {
	var res="";

	for (var i=0; i< this.framePaths.length; i++) {
		var c4csel = this.frameSelections[i];
		if (c4csel == null) continue;

		res += c4cUti_trimWS(c4csel.getOriginalText());
	}

	return res;
}

function c4cSelection_updateStoredText(topWindow) {
	// go over frames and update selection texts.
	for (var i=0; i< this.framePaths.length; i++) {
		// get frame
		var frameWindow = c4cUti_getFrameWindow(topWindow, this.framePaths[i]);
		// get selection
		var sel = c4cUti_getSelection(frameWindow);
		// if valid update it
		if (this.frameSelections[i]) {
			// this means the selection exists and is not ""
			this.frameSelections[i].updateTextOnly(frameWindow.document, sel);
		}
	}

}