wmp.selection = {
	getDoc: function (d /*nodeOrDocumentOrWindow*/) 
	{
		if (!d) {
			console.warn("wmp.selection.getDoc: default document");
			return document;
		}
		return d.document || d.ownerDocument || d;
	},
	
	getInfo: function(doc) {
		var doc = this.getDoc(doc);
		if (Browser.Engine.trident)
			var selection = doc.selection || ""
		else
			var selection = doc.defaultView.getSelection() || "";
		return {node: this.getCommonAncestor(doc), range: this.getRange(doc), selection: selection, doc:doc};
	},
	
	selectNode: function (node, options) {
		//console.log("select node", node, node.parentNode);
		if (Browser.Engine.trident) {
			var range = node.ownerDocument.body.createTextRange();
			try {
				range.moveToElementText(node);
			}
			catch(e) {
				console.warn("could not select");
				return;
			}
			if (options == "end")
				range.collapse(false);
			else if (options == "start")
				range.collapse(true);
			range.select();
		}
		else {
			var range = node.ownerDocument.createRange();
			try {
				range.selectNodeContents(node);
			}
			catch(e) {
				console.warn("could not select");
				return;
			}
			var selection = this._selectRange(range);
			if (options == "end")
				selection.collapseToEnd();
			else if (options == "start")
				selection.collapseToStart();
		}
	},
	_selectRange: function(range) {
		var doc = range.startContainer.ownerDocument;
		var selection = doc.defaultView.getSelection();
		if (Browser.Engine.presto) {
			selection.removeRange(this.getRange(doc)); //remove old range likie this
			selection.collapseToEnd(); //Opera Bug: removeRange: selection wont change when something is selected
		}
		else
			selection.removeAllRanges(); //Opera Bug; removeAllRanges: the focus is lost (cannot type).
		selection.addRange(range);
		/*
		//Opera test (does not work in Opera)
		var selection = node.ownerDocument.defaultView.getSelection();
		selection.selectAllChildren(node);
		*/
		return selection;
	},
	_getIeRange: function(doc, index) {
		try {
			var s = doc.selection;
		}
		catch(e) {console.warn("ie selection: selection error", s)}
		try {
			var col = s.createRangeCollection();
			return range = s.createRangeCollection().item(index || 0);
		}
		catch(e) {
			console.warn("ie rangeCollection error");
			try {
				return range = s.createRange();
			}
			catch(e) {console.warn("ie selection: range error");}
		}
		return null;
	},
	
	getCommonAncestor: function(doc) 
	{
		var doc = this.getDoc(doc);
		if (Browser.Engine.trident) {
			var range = this._getIeRange(doc);
			try {
				var node = range.parentElement();
			}
			catch(e) {
				console.warn("ie selection: node error:", range, range.nodeName);
				if (range.nodeName) //sometimes (control selection) ie returns the node instead of a range
					var node = range;
			}
			
			var node = node || doc.body;
			//ie sometimes takes a selection of a different document
			if (node.ownerDocument != doc) {
				console.warn("ie selection: doc error1:",node.ownerDocument.body.innerHTML.substring(0,20) == doc.body.innerHTML.substring(0,20), node, range, node.ownerDocument.body, doc.body);
				if (doc._oldselectioncontainer && doc._oldselectioncontainer.ownerDocument)
					return doc._oldselectioncontainer;
				return doc.body;
			}
			doc._oldselectioncontainer = node;
			return node;
		}
		try {
			var range = doc.defaultView.getSelection().getRangeAt(0);
		}
		catch(e) {
			console.error("can't get range");
			return doc.body;
		}
		if (Browser.Engine.presto) {
			if (range.startContainer.nodeType == 1 && range.endContainer == range.startContainer) {
				if (Math.abs(range.startOffset-range.endOffset) == 1) {
					return range.startContainer.childNodes[range.startOffset];
				}
			}
		}
		
		return range.commonAncestorContainer || doc.body;
	},
	
	getRange: function (doc) {
		return this.getRangeAt(0, doc);
	},
	
	getRangeAt: function (index, doc) {
		var doc = this.getDoc(doc);
		if (Browser.Engine.trident) {
			//todo: try to get range in case of errors
			if (doc.selection == "None") {
				return new wmp.dom.InternetExplorerRange(false, doc);
			}
			var textRange = this._getIeRange(doc, index);
			
			if (textRange) {
				//if the selectedRange is a box around an element, select the contents
				if ($type(textRange)=="element") {
					var node = textRange;
					new wmp.dom.InternetExplorerRange(false, doc);
					var textRange = node.ownerDocument.body.createTextRange();
					try {
						textRange.moveToElementText(node);
					}
					catch(e) {
						console.warn("getRangeAt:could not select");
						return;
					}
					textRange.collapse(true);
					textRange.select();
				}
				var range = new wmp.dom.InternetExplorerRange(textRange, doc);
				range._init();
				return range;
			}
			return new wmp.dom.InternetExplorerRange(false, doc);
		}
		var selection = doc.defaultView.getSelection();
		try {
			return selection.getRangeAt(index);
		} catch(e) {
			console.error("can't get range");
			return document.createRange();
		}
	},
	
	pasteHTML: function (html, doc, deselect) {
		var doc = this.getDoc(doc);
		//console.log("paste:" + html);
		if (Browser.Engine.trident) {
			try {
				var col = doc.selection.createRangeCollection();
				var textRange = col.item(0);
				textRange.pasteHTML(html);
				if (deselect) {
					textRange.collapse(false);
					textRange.select();
				}
			}
			catch (e) {console.warn("wmp.selection.pasteHTML: catch");}
		}
		else if (Browser.Engine.presto) {
			//todo: Opera does not do this properly (focus is lost, or enter is inserted)
			doc.execCommand("inserthtml", false, html);
		}
		else {
			//console.log("pasteHTML", nodes.end);
			var range = this.getRange(doc);
			range.deleteContents();
			var fragment = range.createContextualFragment(html);
			var last = fragment.lastChild;
			range.insertNode(fragment);
			if (deselect) {
				range.setStartAfter(last);
			}
			this._selectRange(range);
			/*
			var div = doc.createElement("div");
			div.innerHTML = html;
			var range = this.getRange(doc);
			range.deleteContents();
			range.insertNode(div);
			var nodes = wmp.xml.unwrapNode(div);
			var range = doc.createRange();
			if (deselect) {
				range.setStartAfter(nodes.end);
			}
			else {
				range.selectNode(nodes.end);
			}
			this._selectRange(range);
			*/
		}
	},
	
	setCursor: function(startNode, startOffset, endNode, endOffset) {
		//console.log("setCursor: startNode", startNode, " at " + startOffset);
		if (Browser.Engine.trident) {
			var range = new wmp.dom.InternetExplorerRange(false, startNode.ownerDocument);
			range.setStart(startNode, startOffset);
			if (endNode)
				range.setEnd(endNode, endOffset);
			else
				range.collapse(false);
			range._range.select();
		}
		else {
			var range = startNode.ownerDocument.createRange();
			try {
				range.setStart(startNode, startOffset);
				if (endNode) {
					range.setEnd(endNode, endOffset);
				}
				else
					range.collapse(true);
			}
			catch(e) {
				return false;
			}
			this._selectRange(range);
		}
		return true;
	},
	
	save: function(doc) {
		var inf = this.getInfo(doc);
		return {
			'startContainer': inf.range.startContainer,
			'startOffset': inf.range.startOffset,
			'endContainer': inf.range.endContainer,
			'endOffset': inf.range.endOffset
		}
	},
	
	restore: function(savedSelection) {
		return this.setCursor(savedSelection.startContainer, savedSelection.startOffset, savedSelection.endContainer, savedSelection.endOffset);
	},
	
	saveInString: function(sel) {
		//todo: endpos
		if (!sel || !sel.range)
			return "";
		var n = sel.range.startContainer;
		if (!n)
			return "";
		var startOffset = sel.range.startOffset;
		var posstr = wmp.xml.getPosition(n);
		while ((n = n.parentNode) && n.nodeName.toLowerCase()!= "body")
			posstr = wmp.xml.getPosition(n) + "," + posstr;
		return posstr + ":" + startOffset;
	},
	
	restoreFromString: function(posstr, doc) {
		if (!posstr)
			return;
		
		//console.log("restore selection");
		var parts = posstr.substr(0,posstr.indexOf(":")).split(",");
		var n = doc.body, pos;
		while (pos = parts.shift()) {
			//console.log(pos, n);
			n = n.childNodes[pos];
			if (!n) {
				console.debug("undoSetCursorPos: nodeNotFound");
				return;
			}
		}
		pos = posstr.substr(posstr.indexOf(":")+1,6);
		wmp.selection.setCursor(n, parseInt(pos));
	},
	
	debug: function(doc) {
		var inf = this.getInfo(doc);
		console.log('parentNode:', inf.node,
			'startContainer:', inf.range.startContainer,
			'startOffset:', inf.range.startOffset,
			'endContainer:', inf.range.endContainer,
			'endOffset:', inf.range.endOffset);
	},
	
	nextTo: function() {
		var el, atbr, sel = wmp.selection.getInfo(this.editor.doc)
		console.log(sel);
		var sc = sel.range.startContainer;
		var so = sel.range.startOffset;
		if ($type(sel.range.startContainer) == "element") {
			el = sc.childNodes[so];
			console.log("element", el);
		}
		else {
			if (so == 0)
				el = sc.previousSibling;
			else if (so == sc.length)
				el = sc.nextSibling;
		}
		if (el && el.nodeName.toLowerCase()=="br"){
			var atbr=true;
			console.log("br", el, el.nextSibling);
			if (true/*ff*/ && (!el.nextSibling || ($type(el.nextSibling)== "whitespace" && !el.nextSibling.nextSibling))) {
				var atbr=false;
			}
			
		}
	}
}
