/**
 * CellSelection.js
 *
 * Released under LGPL License.
 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
 *
 * License: http://www.tinymce.com/license
 * Contributing: http://www.tinymce.com/contributing
 */

/**
 * This class handles table cell selection by faking it using a css class that gets applied
 * to cells when dragging the mouse from one cell to another.
 *
 * @class tinymce.tableplugin.CellSelection
 * @private
 */
define("tinymce/tableplugin/CellSelection", [
	"tinymce/tableplugin/TableGrid",
	"tinymce/dom/TreeWalker",
	"tinymce/util/Tools"
], function(TableGrid, TreeWalker, Tools) {
	return function(editor) {
		var dom = editor.dom, tableGrid, startCell, startTable, hasCellSelection = true, resizing;

		function clear(force) {
			// Restore selection possibilities
			editor.getBody().style.webkitUserSelect = '';

			if (force || hasCellSelection) {
				editor.dom.removeClass(
					editor.dom.select('td.mce-item-selected,th.mce-item-selected'),
					'mce-item-selected'
				);

				hasCellSelection = false;
			}
		}

		function cellSelectionHandler(e) {
			var sel, table, target = e.target;

			if (resizing) {
				return;
			}

			if (startCell && (tableGrid || target != startCell) && (target.nodeName == 'TD' || target.nodeName == 'TH')) {
				table = dom.getParent(target, 'table');
				if (table == startTable) {
					if (!tableGrid) {
						tableGrid = new TableGrid(editor, table);
						tableGrid.setStartCell(startCell);

						editor.getBody().style.webkitUserSelect = 'none';
					}

					tableGrid.setEndCell(target);
					hasCellSelection = true;
				}

				// Remove current selection
				sel = editor.selection.getSel();

				try {
					if (sel.removeAllRanges) {
						sel.removeAllRanges();
					} else {
						sel.empty();
					}
				} catch (ex) {
					// IE9 might throw errors here
				}

				e.preventDefault();
			}
		}

		// Add cell selection logic
		editor.on('MouseDown', function(e) {
			if (e.button != 2 && !resizing) {
				clear();

				startCell = dom.getParent(e.target, 'td,th');
				startTable = dom.getParent(startCell, 'table');
			}
		});

		editor.on('mouseover', cellSelectionHandler);

		editor.on('remove', function() {
			dom.unbind(editor.getDoc(), 'mouseover', cellSelectionHandler);
		});

		editor.on('MouseUp', function() {
			var rng, sel = editor.selection, selectedCells, walker, node, lastNode;

			function setPoint(node, start) {
				var walker = new TreeWalker(node, node);

				do {
					// Text node
					if (node.nodeType == 3 && Tools.trim(node.nodeValue).length !== 0) {
						if (start) {
							rng.setStart(node, 0);
						} else {
							rng.setEnd(node, node.nodeValue.length);
						}

						return;
					}

					// BR element
					if (node.nodeName == 'BR') {
						if (start) {
							rng.setStartBefore(node);
						} else {
							rng.setEndBefore(node);
						}

						return;
					}
				} while ((node = (start ? walker.next() : walker.prev())));
			}

			// Move selection to startCell
			if (startCell) {
				if (tableGrid) {
					editor.getBody().style.webkitUserSelect = '';
				}

				// Try to expand text selection as much as we can only Gecko supports cell selection
				selectedCells = dom.select('td.mce-item-selected,th.mce-item-selected');
				if (selectedCells.length > 0) {
					rng = dom.createRng();
					node = selectedCells[0];
					rng.setStartBefore(node);
					rng.setEndAfter(node);

					setPoint(node, 1);
					walker = new TreeWalker(node, dom.getParent(selectedCells[0], 'table'));

					do {
						if (node.nodeName == 'TD' || node.nodeName == 'TH') {
							if (!dom.hasClass(node, 'mce-item-selected')) {
								break;
							}

							lastNode = node;
						}
					} while ((node = walker.next()));

					setPoint(lastNode);

					sel.setRng(rng);
				}

				editor.nodeChanged();
				startCell = tableGrid = startTable = null;
			}
		});

		editor.on('KeyUp Drop SetContent', function(e) {
			clear(e.type == 'setcontent');
			startCell = tableGrid = startTable = null;
			resizing = false;
		});

		editor.on('ObjectResizeStart ObjectResized', function(e) {
			resizing = e.type != 'objectresized';
		});

		return {
			clear: clear
		};
	};
});