/*\
title: $:/core/modules/parsers/wikiparser/rules/table.js
type: application/javascript
module-type: wikirule

Wiki text block rule for tables.

\*/
(function(){

/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";

exports.name = "table";
exports.types = {block: true};

exports.init = function(parser) {
	this.parser = parser;
	// Regexp to match
	this.matchRegExp = /^\|(?:[^\n]*)\|(?:[fhck]?)\r?\n/mg;
};

var processRow = function(prevColumns) {
	var cellRegExp = /(?:\|([^\n\|]*)\|)|(\|[fhck]?\r?\n)/mg,
		cellTermRegExp = /((?:\x20*)\|)/mg,
		tree = [],
		col = 0,
		colSpanCount = 1,
		prevCell;
	// Match a single cell
	cellRegExp.lastIndex = this.parser.pos;
	var cellMatch = cellRegExp.exec(this.parser.source);
	while(cellMatch && cellMatch.index === this.parser.pos) {
		if(cellMatch[1] === "~") {
			// Rowspan
			var last = prevColumns[col];
			if(last) {
				last.rowSpanCount++;
				$tw.utils.addAttributeToParseTreeNode(last.element,"rowspan",last.rowSpanCount);
				$tw.utils.addAttributeToParseTreeNode(last.element,"valign","center");
				if(colSpanCount > 1) {
					$tw.utils.addAttributeToParseTreeNode(last.element,"colspan",colSpanCount);
					colSpanCount = 1;
				}
			}
			// Move to just before the `|` terminating the cell
			this.parser.pos = cellRegExp.lastIndex - 1;
		} else if(cellMatch[1] === ">") {
			// Colspan
			colSpanCount++;
			// Move to just before the `|` terminating the cell
			this.parser.pos = cellRegExp.lastIndex - 1;
		} else if(cellMatch[2]) {
			// End of row
			if(prevCell && colSpanCount > 1) {
				$tw.utils.addAttributeToParseTreeNode(prevCell,"colspan",colSpanCount);
			}
			this.parser.pos = cellRegExp.lastIndex - 1;
			break;
		} else {
			// For ordinary cells, step beyond the opening `|`
			this.parser.pos++;
			// Look for a space at the start of the cell
			var spaceLeft = false,
				chr = this.parser.source.substr(this.parser.pos,1);
			while(chr === " ") {
				spaceLeft = true;
				this.parser.pos++;
				chr = this.parser.source.substr(this.parser.pos,1);
			}
			// Check whether this is a heading cell
			var cell;
			if(chr === "!") {
				this.parser.pos++;
				cell = {type: "element", tag: "th", children: []};
			} else {
				cell = {type: "element", tag: "td", children: []};
			}
			tree.push(cell);
			// Record information about this cell
			prevCell = cell;
			prevColumns[col] = {rowSpanCount:1,element:cell};
			// Check for a colspan
			if(colSpanCount > 1) {
				$tw.utils.addAttributeToParseTreeNode(cell,"colspan",colSpanCount);
				colSpanCount = 1;
			}
			// Parse the cell
			cell.children = this.parser.parseInlineRun(cellTermRegExp,{eatTerminator: true});
			// Set the alignment for the cell
			if(cellMatch[1].substr(cellMatch[1].length-1,1) === " ") { // spaceRight
				$tw.utils.addAttributeToParseTreeNode(cell,"align",spaceLeft ? "center" : "left");
			} else if(spaceLeft) {
				$tw.utils.addAttributeToParseTreeNode(cell,"align","right");
			}
			// Move back to the closing `|`
			this.parser.pos--;
		}
		col++;
		cellRegExp.lastIndex = this.parser.pos;
		cellMatch = cellRegExp.exec(this.parser.source);
	}
	return tree;
};

exports.parse = function() {
	var rowContainerTypes = {"c":"caption", "h":"thead", "":"tbody", "f":"tfoot"},
		table = {type: "element", tag: "table", children: []},
		rowRegExp = /^\|([^\n]*)\|([fhck]?)\r?\n/mg,
		rowTermRegExp = /(\|(?:[fhck]?)\r?\n)/mg,
		prevColumns = [],
		currRowType,
		rowContainer,
		rowCount = 0;
	// Match the row
	rowRegExp.lastIndex = this.parser.pos;
	var rowMatch = rowRegExp.exec(this.parser.source);
	while(rowMatch && rowMatch.index === this.parser.pos) {
		var rowType = rowMatch[2];
		// Check if it is a class assignment
		if(rowType === "k") {
			$tw.utils.addClassToParseTreeNode(table,rowMatch[1]);
			this.parser.pos = rowMatch.index + rowMatch[0].length;
		} else {
			// Otherwise, create a new row if this one is of a different type
			if(rowType != currRowType) {
				rowContainer = {type: "element", tag: rowContainerTypes[rowType], children: []};
				table.children.push(rowContainer);
				currRowType = rowType;
			}
			// Is this a caption row?
			if(currRowType === "c") {
				// If so, move past the opening `|` of the row
				this.parser.pos++;
				// Move the caption to the first row if it isn't already
				if(table.children.length !== 1) {
					table.children.pop(); // Take rowContainer out of the children array
					table.children.splice(0,0,rowContainer); // Insert it at the bottom						
				}
				// Set the alignment - TODO: figure out why TW did this
//				rowContainer.attributes.align = rowCount === 0 ? "top" : "bottom";
				// Parse the caption
				rowContainer.children = this.parser.parseInlineRun(rowTermRegExp,{eatTerminator: true});
			} else {
				// Create the row
				var theRow = {type: "element", tag: "tr", children: []};
				$tw.utils.addClassToParseTreeNode(theRow,rowCount%2 ? "oddRow" : "evenRow");
				rowContainer.children.push(theRow);
				// Process the row
				theRow.children = processRow.call(this,prevColumns);
				this.parser.pos = rowMatch.index + rowMatch[0].length;
				// Increment the row count
				rowCount++;
			}
		}
		rowMatch = rowRegExp.exec(this.parser.source);
	}
	return [table];
};

})();
