var Clipboard = function(table){
	this.table = table;
	this.mode = true;
	this.copySelector = false;
	this.copySelectorParams = {};
	this.copyFormatter = false;
	this.copyFormatterParams = {};
	this.pasteParser = function(){};
	this.pasteAction = function(){};
	this.htmlElement = false;
	this.config = {};

	this.blocked = true; //block copy actions not originating from this command
};

Clipboard.prototype.initialize = function(){
	var self = this;

	this.mode = this.table.options.clipboard;

	if(this.mode === true || this.mode === "copy"){
		this.table.element.addEventListener("copy", function(e){
			var data;

			self.processConfig();

			if(!self.blocked){
				e.preventDefault();

				data = self.generateContent();

				if (window.clipboardData && window.clipboardData.setData) {
					window.clipboardData.setData('Text', data);
				} else if (e.clipboardData && e.clipboardData.setData) {
					e.clipboardData.setData('text/plain', data);
					if(self.htmlElement){
						e.clipboardData.setData('text/html', self.htmlElement.outerHTML);
					}
				} else if (e.originalEvent && e.originalEvent.clipboardData.setData) {
					e.originalEvent.clipboardData.setData('text/plain', data);
					if(self.htmlElement){
						e.originalEvent.clipboardData.setData('text/html', self.htmlElement.outerHTML);
					}
				}

				self.table.options.clipboardCopied.call(this.table, data);

				self.reset();
			}
		});
	}

	if(this.mode === true || this.mode === "paste"){
		this.table.element.addEventListener("paste", function(e){
			self.paste(e);
		});
	}

	this.setPasteParser(this.table.options.clipboardPasteParser);
	this.setPasteAction(this.table.options.clipboardPasteAction);
};

Clipboard.prototype.processConfig = function(){
	var config = {
		columnHeaders:"groups",
		rowGroups:true,
		columnCalcs:true,
	};

	if(typeof this.table.options.clipboardCopyHeader !== "undefined"){
		config.columnHeaders = this.table.options.clipboardCopyHeader;
		console.warn("DEPRECATION WARNING - clipboardCopyHeader option has been deprecated, please use the columnHeaders property on the clipboardCopyConfig option");
	}

	if(this.table.options.clipboardCopyConfig){
		for(var key in this.table.options.clipboardCopyConfig){
			config[key] = this.table.options.clipboardCopyConfig[key];
		}
	}

	if (config.rowGroups && this.table.options.groupBy && this.table.modExists("groupRows")){
		this.config.rowGroups = true;
	}

	if(config.columnHeaders){
		if((config.columnHeaders === "groups" || config === true)  && this.table.columnManager.columns.length != this.table.columnManager.columnsByIndex.length){
			this.config.columnHeaders = "groups";
		}else{
			this.config.columnHeaders = "columns";
		}
	}else{
		this.config.columnHeaders = false;
	}

	if (config.columnCalcs && this.table.modExists("columnCalcs")){
		this.config.columnCalcs = true;
	}
};


Clipboard.prototype.reset = function(){
	this.blocked = false;
	this.originalSelectionText = "";
};


Clipboard.prototype.setPasteAction = function(action){

	switch(typeof action){
		case "string":
		this.pasteAction = this.pasteActions[action];

		if(!this.pasteAction){
			console.warn("Clipboard Error - No such paste action found:", action);
		}
		break;

		case "function":
		this.pasteAction = action;
		break;
	}
};

Clipboard.prototype.setPasteParser = function(parser){
	switch(typeof parser){
		case "string":
		this.pasteParser = this.pasteParsers[parser];

		if(!this.pasteParser){
			console.warn("Clipboard Error - No such paste parser found:", parser);
		}
		break;

		case "function":
		this.pasteParser = parser;
		break;
	}
};


Clipboard.prototype.paste = function(e){
	var data, rowData, rows;

	if(this.checkPaseOrigin(e)){

		data = this.getPasteData(e);

		rowData = this.pasteParser.call(this, data);

		if(rowData){
			e.preventDefault();

			if(this.table.modExists("mutator")){
				rowData = this.mutateData(rowData);
			}

			rows = this.pasteAction.call(this, rowData);
			this.table.options.clipboardPasted.call(this.table, data, rowData, rows);
		}else{
			this.table.options.clipboardPasteError.call(this.table, data);
		}
	}
};

Clipboard.prototype.mutateData = function(data){
	var self = this,
	output = [];

	if(Array.isArray(data)){
		data.forEach(function(row){
			output.push(self.table.modules.mutator.transformRow(row, "clipboard"));
		});
	}else{
		output = data;
	}

	return output;
};


Clipboard.prototype.checkPaseOrigin = function(e){
	var valid = true;

	if(e.target.tagName != "DIV" || this.table.modules.edit.currentCell){
		valid = false;
	}

	return valid;
};

Clipboard.prototype.getPasteData = function(e){
	var data;

	if (window.clipboardData && window.clipboardData.getData) {
		data = window.clipboardData.getData('Text');
	} else if (e.clipboardData && e.clipboardData.getData) {
		data = e.clipboardData.getData('text/plain');
	} else if (e.originalEvent && e.originalEvent.clipboardData.getData) {
		data = e.originalEvent.clipboardData.getData('text/plain');
	}

	return data;
};


Clipboard.prototype.copy = function(selector, selectorParams, formatter, formatterParams, internal){
	var range, sel, textRange;
	this.blocked = false;

	if(this.mode === true || this.mode === "copy"){

		if (typeof window.getSelection != "undefined" && typeof document.createRange != "undefined") {
			range = document.createRange();
			range.selectNodeContents(this.table.element);
			sel = window.getSelection();

			if(sel.toString() && internal){
				selector = "userSelection";
				formatter = "raw";
				selectorParams = sel.toString();
			}

			sel.removeAllRanges();
			sel.addRange(range);
		} else if (typeof document.selection != "undefined" && typeof document.body.createTextRange != "undefined") {
			textRange = document.body.createTextRange();
			textRange.moveToElementText(this.table.element);
			textRange.select();
		}

		this.setSelector(selector);
		this.copySelectorParams = typeof selectorParams != "undefined" && selectorParams != null ? selectorParams : this.config.columnHeaders;
		this.setFormatter(formatter);
		this.copyFormatterParams = typeof formatterParams != "undefined" && formatterParams != null ? formatterParams : {};

		document.execCommand('copy');

		if(sel){
			sel.removeAllRanges();
		}
	}
};

Clipboard.prototype.setSelector = function(selector){
	selector = selector || this.table.options.clipboardCopySelector;

	switch(typeof selector){
		case "string":
		if(this.copySelectors[selector]){
			this.copySelector = this.copySelectors[selector];
		}else{
			console.warn("Clipboard Error - No such selector found:", selector);
		}
		break;

		case "function":
		this.copySelector = selector;
		break;
	}
};

Clipboard.prototype.setFormatter = function(formatter){

	formatter = formatter || this.table.options.clipboardCopyFormatter;

	switch(typeof formatter){
		case "string":
		if(this.copyFormatters[formatter]){
			this.copyFormatter = this.copyFormatters[formatter];
		}else{
			console.warn("Clipboard Error - No such formatter found:", formatter);
		}
		break;

		case "function":
		this.copyFormatter = formatter;
		break;
	}
};


Clipboard.prototype.generateContent = function(){
	var data;

	this.htmlElement = false;
	data = this.copySelector.call(this, this.config, this.copySelectorParams);

	return this.copyFormatter.call(this, data, this.config, this.copyFormatterParams);
};

Clipboard.prototype.generateSimpleHeaders = function(columns){
	var headers = [];

	columns.forEach(function(column){
		headers.push(column.definition.title);
	});

	return headers;
};

Clipboard.prototype.generateColumnGroupHeaders = function(columns){
	var output = [];

	this.table.columnManager.columns.forEach((column) => {
		var colData = this.processColumnGroup(column);

		if(colData){
			output.push(colData);
		}
	});

	return output;
};

Clipboard.prototype.processColumnGroup = function(column){
	var subGroups = column.columns;

	var groupData = {
		type:"group",
		title:column.definition.title,
		column:column,
	};

	if(subGroups.length){
		groupData.subGroups = [];
		groupData.width = 0;

		subGroups.forEach((subGroup) => {
			var subGroupData = this.processColumnGroup(subGroup);

			if(subGroupData){
				groupData.width += subGroupData.width;
				groupData.subGroups.push(subGroupData);
			}
		});

		if(!groupData.width){
			return false;
		}
	}else{
		if(column.field && (column.definition.clipboard || (column.visible && column.definition.clipboard !== false))){
			groupData.width = 1;
		}else{
			return false;
		}
	}

	return groupData;
};

Clipboard.prototype.groupHeadersToRows = function(columns){

	var headers = [];

	function parseColumnGroup(column, level){

		if(typeof headers[level] === "undefined"){
			headers[level] = [];
		}

		headers[level].push(column.title);

		if(column.subGroups){
			column.subGroups.forEach(function(subGroup){
				parseColumnGroup(subGroup, level+1);
			});
		}else{
			padColumnheaders();
		}
	}

	function padColumnheaders(){
		var max = 0;

		headers.forEach(function(title){
			var len = title.length;
			if(len > max){
				max = len;
			}
		});

		headers.forEach(function(title){
			var len = title.length;
			if(len < max){
				for(var i = len; i < max; i++){
					title.push("");
				}
			}
		});
	}

	columns.forEach(function(column){
		parseColumnGroup(column,0);
	});

	return headers;
};

Clipboard.prototype.rowsToData = function(rows, columns, config, params){
	var data = [];

	rows.forEach(function(row){
		var rowArray = [],
		rowData = row instanceof RowComponent ? row.getData("clipboard") : row;

		columns.forEach(function(column){
			var value = column.getFieldValue(rowData);

			switch(typeof value){
				case "object":
				value = JSON.stringify(value);
				break;

				case "undefined":
				case "null":
				value = "";
				break;

				default:
				value = value;
			}

			rowArray.push(value);
		});

		data.push(rowArray);
	});

	return data;
};

Clipboard.prototype.buildComplexRows = function(config){
	var output = [],
	groups = this.table.modules.groupRows.getGroups();

	groups.forEach((group) => {
		output.push(this.processGroupData(group));
	});

	return output;
};



Clipboard.prototype.processGroupData = function(group){
	var subGroups = group.getSubGroups();

	var groupData = {
		type:"group",
		key:group.key
	};

	if(subGroups.length){
		groupData.subGroups = [];

		subGroups.forEach((subGroup) => {
			groupData.subGroups.push(this.processGroupData(subGroup));
		});
	}else{
		groupData.rows = group.getRows(true);
	}

	return groupData;
};

Clipboard.prototype.getCalcRow = function (calcs, columns, selector, pos){
	var calcData = calcs[selector];

	if(calcData){
		if(pos){
			calcData = calcData[pos];
		}

		if(Object.keys(calcData).length){
			return this.rowsToData([calcData], columns);
		}
	}

	return [];
};


Clipboard.prototype.buildOutput = function(rows, config, params){
	var output = [],
	calcs,
	columns = [];

	if(config.columnHeaders == "groups"){
		columns = this.generateColumnGroupHeaders(this.table.columnManager.columns);

		output = output.concat(this.groupHeadersToRows(columns));
	}else{
		this.table.columnManager.columnsByIndex.forEach(function(column){
			if(column.definition.clipboard || (column.visible && column.definition.clipboard !== false)){
				columns.push(column);
			}
		})

		output.push(this.generateSimpleHeaders(columns));
	}

	if(this.config.columnCalcs){
		calcs = this.table.getCalcResults();
	}

	//generate styled content
	if(this.table.options.clipboardCopyStyled){
		this.generateHTML(rows, columns, calcs, config, params);
	}

	//generate unstyled content
	if(config.rowGroups){
		rows.forEach((row) => {
			output = output.concat(this.parseRowGroupData(row, columns, config, params, calcs || {}));
		});
	}else{
		if(config.columnCalcs){
			output = output.concat(this.getCalcRow(calcs, columns, "top"));
		}

		output = output.concat(this.rowsToData(rows, columns, config, params));

		if(config.columnCalcs){
			output = output.concat(this.getCalcRow(calcs, columns, "bottom"));
		}
	}

	return output;
};


Clipboard.prototype.parseRowGroupData = function (group, columns, config, params, calcObj){
	var groupData = [];

	groupData.push([group.key]);

	if(group.subGroups){
		group.subGroups.forEach((subGroup) => {
			groupData = groupData.concat(this.parseRowGroupData(subGroup, config, params, calcObj[group.key] ? calcObj[group.key].groups || {} : {}));
		});
	}else{
		if(config.columnCalcs){
			groupData = groupData.concat(this.getCalcRow(calcObj, columns, group.key, "top"));
		}

		groupData = groupData.concat(this.rowsToData(group.rows, columns, config, params));

		if(config.columnCalcs){
			groupData = groupData.concat(this.getCalcRow(calcObj, columns, group.key, "bottom"));
		}
	}

	return groupData;
};


Clipboard.prototype.generateHTML = function (rows, columns, calcs, config, params){
	var self = this,
	data = [],
	headers = [], body, oddRow, evenRow, calcRow, firstRow, firstCell, firstGroup, lastCell, styleCells;

	//create table element
	this.htmlElement = document.createElement("table");
	self.mapElementStyles(this.table.element, this.htmlElement, ["border-top", "border-left", "border-right", "border-bottom"]);

	function generateSimpleHeaders(){
		var headerEl = document.createElement("tr");

		columns.forEach(function(column){
			var columnEl = document.createElement("th");
			columnEl.innerHTML = column.definition.title;

			self.mapElementStyles(column.getElement(), columnEl, ["border-top", "border-left", "border-right", "border-bottom", "background-color", "color", "font-weight", "font-family", "font-size"]);

			headerEl.appendChild(columnEl);
		});

		self.mapElementStyles(self.table.columnManager.getHeadersElement(), headerEl, ["border-top", "border-left", "border-right", "border-bottom", "background-color", "color", "font-weight", "font-family", "font-size"]);

		self.htmlElement.appendChild(document.createElement("thead").appendChild(headerEl));
	}


	function generateHeaders(headers){

		var headerHolderEl = document.createElement("thead");

		headers.forEach(function(columns){
			var headerEl =  document.createElement("tr");

			columns.forEach(function(column){
				var columnEl = document.createElement("th");

				if(column.width > 1){
					columnEl.colSpan = column.width;
				}

				if(column.height > 1){
					columnEl.rowSpan = column.height;
				}

				columnEl.innerHTML = column.title;

				self.mapElementStyles(column.element, columnEl, ["border-top", "border-left", "border-right", "border-bottom", "background-color", "color", "font-weight", "font-family", "font-size"]);

				headerEl.appendChild(columnEl);
			});

			self.mapElementStyles(self.table.columnManager.getHeadersElement(), headerEl, ["border-top", "border-left", "border-right", "border-bottom", "background-color", "color", "font-weight", "font-family", "font-size"]);

			headerHolderEl.appendChild(headerEl);
		});

		self.htmlElement.appendChild(headerHolderEl);

	}

	function parseColumnGroup(column, level){

		if(typeof headers[level] === "undefined"){
			headers[level] = [];
		}

		headers[level].push({
			title:column.title,
			width:column.width,
			height:1,
			children:!!column.subGroups,
			element:column.column.getElement(),
		});

		if(column.subGroups){
			column.subGroups.forEach(function(subGroup){
				parseColumnGroup(subGroup, level+1);
			});
		}
	}

	function padVerticalColumnheaders(){
		headers.forEach(function(row, index){
			row.forEach(function(header){
				if(!header.children){
					header.height = headers.length - index;
				}
			});
		});
	}

	function addCalcRow(calcs, selector, pos){
		var calcData = calcs[selector];

		if(calcData){
			if(pos){
				calcData = calcData[pos];
			}

			if(Object.keys(calcData).length){
				// calcRowIndexs.push(body.length);
				processRows([calcData]);
			}
		}
	}

	//create headers if needed
	if(config.columnHeaders){
		if(config.columnHeaders == "groups"){
			columns.forEach(function(column){
				parseColumnGroup(column,0);
			});

			padVerticalColumnheaders();
			generateHeaders(headers);
		}else{
			generateSimpleHeaders();
		}
	}

	// columns = this.table.columnManager.columnsByIndex;

	//create table body
	body = document.createElement("tbody");

	//lookup row styles
	if(window.getComputedStyle){
		oddRow = this.table.element.querySelector(".tabulator-row-odd:not(.tabulator-group):not(.tabulator-calcs)");
		evenRow = this.table.element.querySelector(".tabulator-row-even:not(.tabulator-group):not(.tabulator-calcs)");
		calcRow = this.table.element.querySelector(".tabulator-row.tabulator-calcs");
		firstRow = this.table.element.querySelector(".tabulator-row:not(.tabulator-group):not(.tabulator-calcs)");
		firstGroup = this.table.element.getElementsByClassName("tabulator-group")[0];

		if(firstRow){
			styleCells = firstRow.getElementsByClassName("tabulator-cell");
			firstCell = styleCells[0];
			lastCell = styleCells[styleCells.length - 1];
		}
	}

	function processRows(rowArray){
		//add rows to table
		rowArray.forEach(function(row, i){
			var rowEl = document.createElement("tr"),
			styleRow = firstRow,
			isCalc = false,
			rowData;

			if(row instanceof RowComponent){
				rowData = row.getData("clipboard");
			}else{
				rowData = row;
				isCalc = true;
			}


			columns.forEach(function(column, j){
				var cellEl = document.createElement("td"),
				value = column.getFieldValue(rowData);

				switch(typeof value){
					case "object":
					value = JSON.stringify(value);
					break;

					case "undefined":
					case "null":
					value = "";
					break;

					default:
					value = value;
				}

				cellEl.innerHTML = value;

				if(column.definition.align){
					cellEl.style.textAlign = column.definition.align;
				}

				if(j < columns.length - 1){
					if(firstCell){
						self.mapElementStyles(firstCell, cellEl, ["border-top", "border-left", "border-right", "border-bottom", "color", "font-weight", "font-family", "font-size"]);
					}
				}else{
					if(firstCell){
						self.mapElementStyles(firstCell, cellEl, ["border-top", "border-left", "border-right", "border-bottom", "color", "font-weight", "font-family", "font-size"]);
					}
				}

				rowEl.appendChild(cellEl);
			});

			if(isCalc){
				styleRow = calcRow;
			}else{
				if(!(i % 2) && oddRow){
					styleRow = oddRow;
				}

				if((i % 2) && evenRow){
					styleRow = evenRow;
				}
			}

			if(styleRow){
				self.mapElementStyles(styleRow, rowEl, ["border-top", "border-left", "border-right", "border-bottom", "color", "font-weight", "font-family", "font-size", "background-color"]);
			}

			body.appendChild(rowEl);
		});
	}

	function processGroup(group, calcObj){
		var groupEl = document.createElement("tr"),
		groupCellEl = document.createElement("td");

		groupCellEl.colSpan = columns.length;

		groupCellEl.innerHTML = group.key;

		groupEl.appendChild(groupCellEl);
		body.appendChild(groupEl);

		self.mapElementStyles(firstGroup, groupEl, ["border-top", "border-left", "border-right", "border-bottom", "color", "font-weight", "font-family", "font-size", "background-color"]);

		if(group.subGroups){
			group.subGroups.forEach((subGroup) => {
				processGroup(subGroup, calcObj[group.key] ? calcObj[group.key].groups || {} : {});
			});
		}else{
			if(config.columnCalcs){
				addCalcRow(calcObj, group.key, "top");
			}

			processRows(group.rows);

			if(config.columnCalcs){
				addCalcRow(calcObj, group.key, "bottom");
			}
		}

	}

	if(config.rowGroups){
		rows.forEach((group) => {
			processGroup(group, calcs || {});
		});
	}else{
		if(config.columnCalcs){
			addCalcRow(calcs, "top");
		}

		processRows(rows);

		if(config.columnCalcs){
			addCalcRow(calcs, "bottom");
		}
	}

	this.htmlElement.appendChild(body);
};

Clipboard.prototype.mapElementStyles = function(from, to, props){

	var lookup = {
		"background-color" : "backgroundColor",
		"color" : "fontColor",
		"font-weight" : "fontWeight",
		"font-family" : "fontFamily",
		"font-size" : "fontSize",
		"border-top" : "borderTop",
		"border-left" : "borderLeft",
		"border-right" : "borderRight",
		"border-bottom" : "borderBottom",
	};

	if(window.getComputedStyle){
		var fromStyle = window.getComputedStyle(from);

		props.forEach(function(prop){
			to.style[lookup[prop]] = fromStyle.getPropertyValue(prop);
		});
	}

	// return window.getComputedStyle ? window.getComputedStyle(element, null).getPropertyValue(property) : element.style[property.replace(/-([a-z])/g, function (g) { return g[1].toUpperCase(); })];
};


Clipboard.prototype.copySelectors = {
	userSelection: function(config, params){
		return params;
	},
	selected: function(config, params){
		var rows = [];

		if(this.table.modExists("selectRow", true)){
			rows = this.table.modules.selectRow.getSelectedRows();
		}

		if(config.rowGroups){
			console.warn("Clipboard Warning - select coptSelector does not support row groups");
		}

		return this.buildOutput(rows, config, params)
	},
	table: function(config, params){
		if(config.rowGroups){
			console.warn("Clipboard Warning - table coptSelector does not support row groups");
		}

		return this.buildOutput(this.table.rowManager.getComponents(), config, params);
	},
	active: function(config, params){
		var rows;

		if(config.rowGroups){
			rows = this.buildComplexRows(config);
		}else{
			rows = this.table.rowManager.getComponents(true);
		}

		return this.buildOutput(rows, config, params);
	},
};

Clipboard.prototype.copyFormatters = {
	raw: function(data, params){
		return data;
	},
	table: function(data, params){
		var output = [];

		data.forEach(function(row){
			row.forEach(function(value){
				if(typeof value == "undefined"){
					value = "";
				}

				value = typeof value == "undefined" || value === null ? "" : value.toString();

				if(value.match(/\r|\n/)){
					value = value.split('"').join('""');
					value = '"' + value + '"';
				}
			});

			output.push(row.join("\t"));
		});

		return output.join("\n");
	},
};

Clipboard.prototype.pasteParsers = {
	table:function(clipboard){
		var data = [],
		success = false,
		headerFindSuccess = true,
		columns = this.table.columnManager.columns,
		columnMap = [],
		rows = [];

		//get data from clipboard into array of columns and rows.
		clipboard = clipboard.split("\n");

		clipboard.forEach(function(row){
			data.push(row.split("\t"));
		});

		if(data.length && !(data.length === 1 && data[0].length < 2)){
			success = true;

			//check if headers are present by title
			data[0].forEach(function(value){
				var column = columns.find(function(column){
					return value && column.definition.title && value.trim() && column.definition.title.trim() === value.trim();
				});

				if(column){
					columnMap.push(column);
				}else{
					headerFindSuccess = false;
				}
			});

			//check if column headers are present by field
			if(!headerFindSuccess){
				headerFindSuccess = true;
				columnMap = [];

				data[0].forEach(function(value){
					var column = columns.find(function(column){
						return value && column.field && value.trim() && column.field.trim() === value.trim();
					});

					if(column){
						columnMap.push(column);
					}else{
						headerFindSuccess = false;
					}
				});

				if(!headerFindSuccess){
					columnMap = this.table.columnManager.columnsByIndex;
				}
			}

			//remove header row if found
			if(headerFindSuccess){
				data.shift();
			}

			data.forEach(function(item){
				var row = {};

				item.forEach(function(value, i){
					if(columnMap[i]){
						row[columnMap[i].field] = value;
					}
				});

				rows.push(row);
			});

			return rows;
		}else{
			return false;
		}
	}
};

Clipboard.prototype.pasteActions = {
	replace:function(rows){
		return this.table.setData(rows);
	},
	update:function(rows){
		return this.table.updateOrAddData(rows);
	},
	insert:function(rows){
		return this.table.addData(rows);
	},
};



Tabulator.prototype.registerModule("clipboard", Clipboard);
