import {ipcRenderer, clipboard} from 'electron';
import * as $ from "../../third_party/js/jquery-3.5.1.js";
import * as Sortable from "../../third_party/js/Sortable.js"
import * as Selectable from "../../third_party/js/selectable.js"
import * as util from "util";
let json_data;
let tables_data;
let current_table;
let is_positioning = false;
let undo_history = {};
let nav = $("nav");
let tables = $("#tables");
let nav_scroll = nav.scrollLeft();
let detail_tab_name = $("#detail-tab-name");
let detail_table_name = $("#detail-table-name");
let detail_table_description = $("#detail-table-description");
let detail_table_hidden = $("#detail-table-hidden");
let detail_column_name = $("#detail-column-name");
let detail_column_selectable = $("#detail-column-selectable");
let detail_column_sortable = $("#detail-column-sortable");
let detail_cell_text = $("#detail-cell-text");
let position_edit_btn = $("#position-edit");

function openFile(json) {
	json_data = json;
	tables_data = json["tables"];
	current_table = null;
	refreshPage();
}

function saveFile(create_new_file) {
	ipcRenderer.send("save", json_data, create_new_file);
}

function clearPage() {
	tables.empty();
	nav.empty();
	$("#details input, #details textarea").val("");
}

function refreshPage() {
	toggleMovement();
	clearPage();
	generateTables();
	showTab(current_table ? current_table: tables_data[0]["id"]);
	toggleMovement()
}

function generateTable(table_data) {
	const table_id = table_data["id"];
	let a = $("<div>")
		.attr("id", table_id + "-nav")
		.addClass("btn nav-link table-nav")
		.text(table_data["name"])
		.on("click", () => {
			showTab(table_id);
		})
		.on("contextmenu", (event) => {
			navContextMenu(event, table_id);
		});
	let add_table_btn = nav.find("[id='btn-add-table']");
	if (add_table_btn.length !== 0) {
		add_table_btn.before(a);
	} else {
		nav.append(a);
	}

	let table_div = $("<div></div>")
		.attr("id", table_id + "-tab")
		.addClass("tab-pane table-responsive text-nowrap")
		.css({"margin-right": "100px"});

	let table = $("<table></table>")
		.attr("id", table_id)
		.addClass("table table-bordered")
		.append($("<thead></thead>")
			.append($("<tr></tr>")
				.addClass("text-nowrap")))
		.append("<tbody>");

	$.each(table_data["columns"], (i, col) => {
		const col_id = col["field"];
		let header_id = table_id + "-" + col_id;
		table.find("thead > tr")
			.append(makeHeader(header_id, i, col["title"]));
	})

	$.each(table_data["rows"], (i, row) => {
		const row_id = row["uid"];
		let tr = $("<tr></tr>")
			.attr("id", table_id + "-" + row_id);
		$.each(table_data["columns"], (j, col) => {
			const col_id = col["field"];
			let cell_id = table_id + "-" + col_id + "-" + row_id;
			let td = makeCell(cell_id, row[col_id]);
			tr.append(td);
		})
		table.find("tbody").append(tr);
	})

	table_div.append(table);
	let table_container = tables;
	let add_row_div = table_container.find("div[id='add-row-div']");
	if (add_row_div.length !== 0) {
		add_row_div.before(table_div);
	} else {
		table_container.append(table_div);
	}
	createUndoHistory(table_id);
}

function makeCell(cell_id, cell_text) {
	return $("<td></td>")
		.attr("id", cell_id)
		.addClass("ui-selectable")
		.text(cell_text)
		.on("contextmenu" , (event) => {
			cellContextMenu(event, cell_id);
		})
		.on("dblclick", (event) => {
			cellInlineEdit(event);
		});
}

function makeHeader(header_id, header_index, col_title) {
	return $("<th>")
		.attr("id", header_id)
		.css("user-select", "none")
		.addClass("ui-selectable")
		.text(col_title)
		.on("contextmenu", (event) => {
			headerContextMenu(event, header_id);
		})
		.on("dblclick", (event) => {
			headerInlineEdit(event);
		});
}

function generateTables() {
	tables.append($("<div>")
		.attr("id", "add-col-div")
		.css({"position": "absolute", "top": "46px", "right": "0"})
		.append($("<button>")
			.attr("id", "btn-add-column")
			.addClass("btn btn-primary")
			.text("add column")
			.on("click", () => {
				insertColumn(current_table, -1)
			})))
	$.each(tables_data, (i, table_data) => {
		generateTable(table_data);
	})
	tables.append($("<div>")
		.attr("id", "add-row-div")
		.append($("<button>")
			.attr("id", "btn-add-row")
			.addClass("btn btn-primary")
			.text("add row")
			.on("click", () => {
				insertRow(current_table, -1)
			})))
	nav.append($("<button>")
		.attr("id", "btn-add-table")
		.addClass("nav-link btn btn-success")
		.html("<i class='fa fa-plus'></i>")
		.attr({"data-bs-toggle": "tooltip", "data-bs-placement": "bottom", "title": "add table"})
		.on("click", () => {
			addTable();
		}))
}

function updateDetailsPanel() {
	if (!is_positioning) {
		let data = getSelectionData(true);
		detail_tab_name.val(data["tab_name"]);
		detail_table_name.val(data["table_name"]);
		detail_table_description.val(data["table_description"]);
		detail_table_hidden.prop("checked", data["table_hidden"]);
		if (data["col_id"]) {
			detail_column_name.removeAttr("disabled");
			detail_column_selectable.removeAttr("disabled");
			detail_column_sortable.removeAttr("disabled");
			detail_column_name.val(data["col_name"]);
			detail_column_selectable.prop("checked", data["col_selectable"]);
			detail_column_sortable.prop("checked", data["col_sortable"]);
		} else {
			detail_column_name.attr("disabled", "true");
			detail_column_selectable.attr("disabled", "true");
			detail_column_sortable.attr("disabled", "true");
			detail_column_name.val("");
			detail_column_selectable.prop("checked", "");
			detail_column_sortable.prop("checked", "");
		}
		if (data["row_id"]) {
			detail_cell_text.removeAttr("disabled");
			detail_cell_text.val(data["cell_text"]);
		} else {
			detail_cell_text.attr("disabled", "true");
			detail_cell_text.val("");
		}
	}
}

function showTab(table_name) {
	disableSelection();
	current_table = table_name;
	let tab_name = table_name+"-tab";
	tables.children().removeClass("show active");
	nav.children().removeClass("active");
	$("#"+tab_name).addClass("show active");
	$("#"+table_name+"-nav").addClass("active");
	if (!is_positioning) {
		enableSelection();
	}
}

function selectCells(cell_ids) {
	if (!Array.isArray(cell_ids)) {
		cell_ids = [cell_ids];
	}
	updateDetailsPanel();
}

function getSelectionData(force_update=false) {
	let selected_cells = getSelectedCells();
	let cell_data = {};
	$.each(tables_data, (i, table_data) => {
		if (table_data["id"] === current_table) {
			cell_data["selection_count"] = selected_cells.length;
			cell_data["table_index"] = i;
			cell_data["table_name"] = table_data["name"];
			cell_data["tab_name"] = table_data["tab_name"];
			cell_data["table_description"] = table_data["description"];
			cell_data["table_hidden"] = table_data["hidden"];
			cell_data["num_rows"] = table_data["rows"].length;
			cell_data["num_cols"] = table_data["columns"].length;
			if (selected_cells.length === 1) {
				let column_pattern = /(column\d+)/;
				let row_pattern = /(row\d+)/;
				let column_match = column_pattern.exec(selected_cells[0]);
				let row_match = row_pattern.exec(selected_cells[0]);
				cell_data["col_id"] = current_table+"-"+column_match[0];
				$.each(table_data["columns"], (j, col) => {
					if (col["field"] === column_match[0]) {
						cell_data["col_index"] = j;
						cell_data["col_name"] = col["title"];
						cell_data["col_selectable"] = col["selectable"];
						cell_data["col_sortable"] = col["sortable"];
						return false;
					}
				})
				if (row_match !== null) {
					cell_data["row_id"] = current_table+"-"+row_match[0];
					cell_data["cell_id"] = current_table+"-"+column_match[0]+"-"+row_match[0];
					$.each(table_data["rows"], (j, row) => {
						if (row["uid"] === row_match[0]) {
							cell_data["row_index"] = j;
							cell_data["cell_text"] = row[column_match[0]];
							cell_data["row_key"] = column_match[0];
							return false;
						}
					})
				} else {
					cell_data["row_index"] = -1;
				}
			}
			return false;
		}
	})
	return cell_data;
}

function addTable() {
	let table_ids = getTableIDs();
	let table_num = 0;
	for (let id of table_ids) {
		if (table_ids.includes(`table${table_num}`)) {
			table_num++;
		} else {break;}
	}
	let table_id = `table${table_num}`;
	let table_data = {
		"id": table_id,
		"name": "New Table",
		"filter": false,
		"hidden": false,
		"tab_name": "",
		"description": "",
		"columns": [
			{
				"field": "column0",
				"title": "Column 1",
				"sortable": true,
				"selectable": false
			}
		],
		"rows": [
			{
				"uid": "row0",
				"column0": "",
			}
		]
	}
	tables_data.push(table_data);
	getSelectionData(true);
	generateTable(table_data);
}

function duplicateTable(table_id) {
	for (let i=0; i < tables_data.length; i++) {
		if (tables_data[i]["id"] === table_id) {
			let new_table = JSON.parse(JSON.stringify(tables_data[i]));
			let table_ids = getTableIDs();
			let table_num = 0;
			for (let id of table_ids) {
				if (table_ids.includes(`table${table_num}`)) {
					table_num++;
				} else {break;}
			}
			new_table["id"] = `table${table_num}`;
			new_table["name"] = "copy of "+new_table["name"];
			tables_data.splice(i+1, 0, new_table);
			break;
		}
	}
	refreshPage();
}

function insertColumn(table_id, index=-1) {
	let cells_to_add = [];
	$.each(tables_data, (i, table_data) => {
		if (table_data["id"] === table_id) {
			if (index === -1) {index = table_data["columns"].length}
			let col_ids = getColumnIDs(table_data["columns"]);
			let col_num = 0;
			for (let id of col_ids) {
				if (col_ids.includes(`column${col_num}`)) {
					col_num++;
				} else {break;}
			}
			let col_id = `column${col_num}`
			table_data["columns"].splice(index, 0,{
				"field": col_id,
				"title": "New Column",
				"sortable": false,
				"selectable": false,
			})

			let num_cols = $(`#${table_id} > thead > tr`).children().length;
			if (index >= num_cols) {
				index = num_cols-1;
				$.fn.insert = function (data) {
					this.after(data);
				};
			} else {
				$.fn.insert = function (data) {
					this.before(data);
				};
			}
			let header_id = table_id + "-" + col_id;
			let header = makeHeader(header_id, index, "New Column");
			cells_to_add.push(header[0])
			$(`#${table_id} > thead > tr > th:nth-child(${index + 1})`)
				.insert(header);

			$(`#${table_id} > tbody > tr`).each((j, tr) => {
				tr = $(tr);
				let cell_id = table_id+"-"+col_id+"-"+table_data["rows"][j]["uid"];
				let cell = makeCell(cell_id, "");
				cells_to_add.push(cell[0])
				tr.children(`td:nth-child(${index + 1})`)
					.insert(cell)
			});
			getSelectionData(true);
			return false;
		}
	})
	let selectable = getSelectable();
	selectable.add(cells_to_add);
	addUndoHistory(current_table);
}

function insertRow(table_id, index=-1) {
	let cells_to_add = [];
	$.each(tables_data, (i, table_data) => {
		if (table_data["id"] === table_id) {
			if (index === -1) {index = table_data["rows"].length}
			let row_ids = getRowIDs(table_data["rows"]);
			let row_num = 0;
			for (let id of row_ids) {
				if (row_ids.includes(`row${row_num}`)) {
					row_num++;
				} else {break;}
			}
			let row_id = `row${row_num}`
			let col_ids = getColumnIDs(table_data["columns"]);
			let row_data = {"uid": row_id};
			for (let id of col_ids) {
				row_data[id] = "";
			}
			table_data["rows"].splice(index, 0, row_data);

			let num_rows = $(`#${table_id} > tbody > tr`).length;
			if (index >= num_rows) {
				index = num_rows-1;
				$.fn.insert = function (data) {
					this.after(data);
				};
			} else {
				$.fn.insert = function (data) {
					this.before(data);
				};
			}
			let tr = $("<tr></tr>")
				.attr("id", table_id+"-"+row_id);
			for (let col_id of col_ids) {
				let cell_id = table_id+"-"+col_id+"-"+row_id;
				let cell = makeCell(cell_id, "");
				cells_to_add.push(cell[0])
				tr.append(cell);
			}
			$(`#${table_id} > tbody > tr:nth-child(${index+1})`)
				.insert(tr);
			getSelectionData(true);
			return false;
		}
	})
	let selectable = getSelectable();
	selectable.add(cells_to_add);
	addUndoHistory(current_table);
}

function deleteTable(table_id) {
	for (let i=0; i<tables_data.length; i++) {
		if (tables_data[i]["id"] === table_id) {
			tables_data.splice(i, 1);
			if (table_id === current_table) {
				current_table = null;
			}
		}
	}
	deleteUndoHistory(table_id);
	refreshPage();
}

function deleteColumn(table_id, col_index) {
	for (let table_data of tables_data) {
		if (table_data["id"] === table_id) {
			let field = table_data["columns"][col_index]["field"];
			table_data["columns"].splice(col_index, 1);

			for (let row of table_data["rows"]) {
				delete row[field];
			}
			break;
		}
	}
	refreshPage();
}

function deleteRow(table_id, row_index) {
	for (let table_data of tables_data) {
		if (table_data["id"] === table_id) {
			table_data["rows"].splice(row_index, 1);
			break;
		}
	}
	refreshPage();
}

function cellContextMenu(event, cell_id) {
	if (is_positioning) {return;}
	if (!getSelectedCells().includes(cell_id)) {
		let selectable = getSelectable();
		selectable.selectAll();
		selectable.invert();
		selectable.select($("#"+cell_id)[0]);
		getSelectionData(true);
	}
	let menu = $("<div>")
		.attr("id", "context-menu-"+cell_id)
		.addClass("border context-menu text-nowrap list-group")
		.on("mouseleave", () => {
			menu.remove();
		})
		.append($("<button>")
			.addClass("list-group-item list-group-item-action")
			.on("click", () => {
				let data = getSelectionData();
				insertRow(current_table, data["row_index"]);
			})
			.text("Insert row above"))
		.append($("<button>")
			.addClass("list-group-item list-group-item-action")
			.on("click", () => {
				let data = getSelectionData();
				insertRow(current_table, data["row_index"]+1);
			})
			.text("Insert row below"))
		.append($("<button>")
			.addClass("list-group-item list-group-item-action")
			.on("click", () => {
				let data = getSelectionData();
				insertColumn(current_table, data["col_index"]);
			})
			.text("Insert column left"))
		.append($("<button>")
			.addClass("list-group-item list-group-item-action")
			.on("click", () => {
				let data = getSelectionData();
				insertColumn(current_table, data["col_index"]+1);
			})
			.text("Insert column right"))
	let data = getSelectionData();
	if (data["num_cols"] > 1) {
		menu.append($("<button>")
			.addClass("list-group-item list-group-item-action list-group-item-danger")
			.on("click", () => {
				let data = getSelectionData();
				deleteColumn(current_table, data["col_index"]);
				menu.remove();
			})
			.text("Delete column"))
	}
	if (data["num_rows"] > 1) {
		menu.append($("<button>")
			.addClass("list-group-item list-group-item-action list-group-item-danger")
			.on("click", function() {
				let data = getSelectionData();
				deleteRow(current_table, data["row_index"]);
				menu.remove();
			})
			.text("Delete row"));
	}

	let max_height = $(document).height();
	let max_width = $(document).width();
	let x_pos = event.pageX-5;
	let y_pos = event.pageY-5;
	$("body").append(menu);
	y_pos = Math.min(y_pos, max_height-menu.height());
	x_pos = Math.min(x_pos, max_width-menu.width());
	menu.css({"top": y_pos, "left": x_pos});
}

function headerContextMenu(event, header_id) {
	if (is_positioning) {return;}
	if (!getSelectedCells().includes(header_id)) {
		let selectable = getSelectable();
		selectable.selectAll();
		selectable.invert();
		selectable.select($("#"+header_id)[0]);
		getSelectionData(true);
	}
	let menu = $("<div>")
		.attr("id", "context-menu-"+header_id)
		.addClass("border context-menu text-nowrap list-group")
		.on("mouseleave", () => {
			menu.remove();
		})
		.append($("<button>")
			.addClass("list-group-item list-group-item-action")
			.on("click", () => {
				let data = getSelectionData();
				insertColumn(current_table, data["col_index"]);
			})
			.text("Insert column left"))
		.append($("<button>")
			.addClass("list-group-item list-group-item-action")
			.on("click", () => {
				let data = getSelectionData();
				insertColumn(current_table, data["col_index"]+1);
			})
			.text("Insert column right"));

	let data = getSelectionData();
	if (data["num_cols"] > 1) {
		menu.append($("<button>")
			.addClass("list-group-item list-group-item-action list-group-item-danger")
			.on("click", () => {
				let data = getSelectionData();
				deleteColumn(current_table, data["col_index"]);
				menu.remove();
			})
			.text("Delete column"))
	}

	let max_height = $(document).height();
	let max_width = $(document).width();
	let x_pos = event.pageX-5;
	let y_pos = event.pageY-5;
	$("body").append(menu);
	y_pos = Math.min(y_pos, max_height-menu.height());
	x_pos = Math.min(x_pos, max_width-menu.width());
	menu.css({"top": y_pos, "left": x_pos});
}

function navContextMenu(event, table_id) {
	if (is_positioning) {return;}
	let menu = $("<div>")
		.attr("id", "context-menu-"+table_id)
		.addClass("border context-menu text-nowrap list-group")
		.on("mouseleave", () => {
			menu.remove();
		})
		.append($("<button>")
			.addClass("list-group-item list-group-item-action")
			.text("duplicate table")
			.on("click", () => {
				duplicateTable(table_id);
			}));
	if (tables_data.length > 1) {
		menu.append($("<button>")
			.addClass("list-group-item list-group-item-action list-group-item-danger")
			.text("Delete Table")
			.attr({"data-bs-toggle": "modal", "data-bs-target": "#delete-table"})
			.on("click", () => {
				$("#confirm-delete-table").one("click", () => {
					deleteTable(table_id);
				})
				menu.remove();
			}));
	}
	let max_height = $(document).height();
	let max_width = $(document).width();
	let x_pos = event.pageX-5;
	let y_pos = event.pageY-5;
	$("body").append(menu);
	y_pos = Math.min(y_pos, max_height-menu.height());
	x_pos = Math.min(x_pos, max_width-menu.width());
	menu.css({"top": y_pos, "left": x_pos});
}

function cellInlineEdit(event) {
	if (is_positioning) {return;}
	let data = getSelectionData()
	let target = $(event.target);
	let text_area = $("<textarea>")
		.on("focusout", () => {
			closeCellInlineEdit(true);
		})
		.on("keydown", (event) => {
			if (event.key === "Escape") {
				closeCellInlineEdit(false);
			} else if (event.key === "Enter" && !event.shiftKey) {
				closeCellInlineEdit(true);
			}
		})
		.val(data["cell_text"]);
	target.html(text_area)
		.addClass("inline-edit");
	text_area.focus();
}

function closeCellInlineEdit(save=false) {
	if (save) {
		let val = $("td.inline-edit > textarea").val()
		$("td.inline-edit").html(val)
			.removeClass("inline-edit");
		let data = getSelectionData();
		tables_data[data["table_index"]]["rows"][data["row_index"]][data["row_key"]] = val;
		detail_cell_text.val(val);
		if (data["cell_text"] != val) {
			addUndoHistory(current_table);
		}
	} else {
		let data = getSelectionData();
		$("td.inline-edit").removeClass("inline-edit")
			.html(data["cell_text"]);
	}
}

function headerInlineEdit(event) {
	if (is_positioning) {return;}
	let data = getSelectionData()
	let target = $(event.target);
	let text_area = $("<textarea>")
		.on("focusout", () => {
			closeHeaderInlineEdit(true);
		})
		.on("keydown", (event) => {
			if (event.key === "Escape") {
				closeHeaderInlineEdit(false);
			} else if (event.key === "Enter" && !event.shiftKey) {
				closeHeaderInlineEdit(true);
			}
		})
		.val(data["col_name"]);
	target.html(text_area)
		.addClass("inline-edit");
	text_area.focus();
}

function closeHeaderInlineEdit(save=false) {
	if (save) {
		let val = $("th.inline-edit > textarea").val()
		$("th.inline-edit").html(val)
			.removeClass("inline-edit");
		let data = getSelectionData();
		tables_data[data["table_index"]]["columns"][data["col_index"]]["title"] = val;
		detail_column_name.val(val);
		if (data["col_name"] != val) {
			addUndoHistory(current_table);
		}
	} else {
		let data = getSelectionData();
		$("th.inline-edit").removeClass("inline-edit")
			.html(data["col_name"]);
	}
}

function moveTable(old_index, new_index) {
	if (old_index != new_index) {
		let table_data = tables_data[old_index];
		tables_data.splice(old_index, 1);
		tables_data.splice(new_index, 0, table_data);
		getSelectionData(true);
		addUndoHistory(current_table);
	}
}

function moveRow(table_id, old_index, new_index) {
	if (old_index != new_index) {
		for (let table_data of tables_data) {
			if (table_data["id"] === table_id) {
				let row = table_data["rows"][old_index];

				table_data["rows"].splice(old_index, 1);
				table_data["rows"].splice(new_index, 0, row);
			}
		}
		getSelectionData(true);
		addUndoHistory(current_table);
	}
}

function moveCol(table_id, old_index, new_index) {
	if (old_index != new_index) {
		for (let table_data of tables_data) {
			if (table_data["id"] === table_id) {
				let col = table_data["columns"][old_index];
				table_data["columns"].splice(old_index, 1);
				table_data["columns"].splice(new_index, 0, col);
			}
		}
		getSelectionData(true)
		addUndoHistory(current_table);
		refreshPage();
	}
}

function enableMovement() {
	is_positioning = true;
	$("table > tbody").each((i, elm) => {
		Sortable.create(elm, {
			animation: 50,
			easing: "cubic-bezier(1, 0, 0, 1)",
			onEnd: function (event) {
				moveRow(current_table, event.oldIndex, event.newIndex);
			}
		});
	})
	$("table > thead > tr").each((i, elm) => {
		Sortable.create(elm, {
			animation: 50,
			easing: "cubic-bezier(1, 0, 0, 1)",
			onEnd: function (event) {
				moveCol(current_table, event.oldIndex, event.newIndex);
			}
		});
	})
	Sortable.create(nav[0], {
		animation: 50,
		easing: "cubic-bezier(1, 0, 0, 1)",
		draggable: ".table-nav",
		onEnd: function (event) {
			moveTable(event.oldIndex, event.newIndex);
		}
	})
	updateDetailsPanel();
	position_edit_btn.text("Stop Edit Position");
}

function disableMovement() {
	is_positioning = false;
	$("table > tbody").each((i, elm) => {
		let sortable_tbody = Sortable.get(elm);
		if (sortable_tbody) {
			sortable_tbody.destroy();
		}
	})
	$("table > thead > tr").each((i, elm) => {
		let sortable_thead = Sortable.get(elm);
		if (sortable_thead) {
			sortable_thead.destroy();
		}
	})
	let sortable_nav = Sortable.get(nav[0]);
	if (sortable_nav) {
		sortable_nav.destroy();
	}
	updateDetailsPanel();
	position_edit_btn.text("Start Edit Position");
}

function toggleMovement() {
	if (is_positioning) {
		enableInputs();
		disableMovement();
		enableSelection();
	} else {
		disableInputs();
		enableMovement();
		disableSelection();
	}
}

function enableSelection() {
	let selectable = getSelectable();
	if (selectable) {
		selectable.enable();
	}
	updateDetailsPanel();
}

function disableSelection() {
	let selectable = getSelectable();
	if (selectable) {
		selectable.clear();
		selectable.disable();
	}
	updateDetailsPanel();
}

function getSelectable() {
	let table_elm = $("#"+current_table);
	if (table_elm.length > 0) {
		let selectable = table_elm[0]._selectable;
		if (!selectable) {
			selectable = new Selectable({
				"filter": `#${current_table} > ui-selectable`,
				"appendTo": table_elm[0],
				"ignore": ".inline-edit",
				"lasso": {
					"border": "0",
					"borderRadius": "0",
					"backgroundColor": "0",
				},
				"keys": [],
			});
			selectable.on("start", (event, item) => {
				if ($("td.inline-edit").length > 0) {
					closeCellInlineEdit(true);
				} else if ($("th.inline-edit").length > 0) {
					closeHeaderInlineEdit(true);
				}
				$("*").blur();
			})
			selectable.on("end", (event, selected, unselected) => {
				updateDetailsPanel();
			});
			if (is_positioning) {
				selectable.disable();
			}
		}
		return selectable;
	}
}

function disableInputs() {
	let inputs = $("input, textarea, #btn-add-column, #btn-add-row, #btn-add-table")
	inputs.attr("disabled", true);
	inputs.val("");
}

function enableInputs() {
	let inputs = $("input, textarea, #btn-add-column, #btn-add-row, #btn-add-table")
	inputs.attr("disabled", false);
}

function selectFirstCell() {
	getSelectable().select($("table:visible > tbody > tr:first > td:first")[0]);
}

function getDeselectingItems(invert) {
	return getSelectable().getItems().filter(function(item) {
		return invert ? !item.deselecting : item.deselecting;
	});
}

function getDeselectingNodes() {
	return getDeselectingItems(false).map(function(item) {
		return item.node;
	});
}

function getSelectedCells(): Array<string> {
	let selectable = getSelectable();
	let nodes = selectable.getSelectedNodes();
	if (nodes.length === 0) {
		nodes = getDeselectingNodes();
	}
	let selected = [];
	for (let node of nodes) {
		selected.push($(node).attr("id"));
	}
	return selected;
}

function copyToClipboard(cut=false) {
	let selectable = getSelectable();
	let items = selectable.getSelectedItems();
	let selection = "";
	let previous_row;
	let previous_row_elm = $("<tr>");
	let table = $("<table>");
	for (let item of items) {
		let el = $(item["node"]);
		let row_pattern = /(row\d+)/;
		let row_match = row_pattern.exec(el.attr("id"));
		let row;
		if (row_match) {
			row = row_match[0];
		} else {
			row = "header";
		}
		let item_text = el.text();
		if (!previous_row) {
			selection += item_text;
			previous_row_elm.append("<td>"+item_text+"</td>");
		} else if (previous_row === row) {
			selection += "\t"+item_text;
			previous_row_elm.append("<td>"+item_text+"</td>");
		} else {
			selection += "\r\n"+item_text;
			table.append(previous_row_elm);
			previous_row_elm = $("<tr>");
			previous_row_elm.append("<td>"+item_text+"</td>");
		}
		previous_row = row;
	}
	if (cut) {
		clearCells(getSelectedCells());
		addUndoHistory(current_table);
	}
	table.append(previous_row_elm);
	clipboard.clear();
	clipboard.write({
		"text": selection,
		"html": table.prop("outerHTML"),
	})
}

function pasteFromClipboard() {
	let text = clipboard.readText();
	let rows = text.split("\r\n");
	let row_cols = [];
	for (let i=0; i < rows.length; i++) {
		row_cols[i] = rows[i].split("\t");
	}
	let selectable = getSelectable();
	let selected_items = selectable.getSelectedItems(false);
	selected_items.splice(0, 1);
	if(selected_items.length > 0) {
		selectable.deselect(selected_items);
	}
	let data = getSelectionData();
	let row_index = data["row_index"];
	let col_index = data["col_index"];
	let num_rows_needed = row_index+1+row_cols.length-(data["num_rows"]+1);
	let num_cols_needed = col_index+row_cols[0].length-data["num_cols"];
	for (let i=0; i < num_rows_needed; i++) {
		insertRow(current_table);
	}
	for (let i=0; i < num_cols_needed; i++) {
		insertColumn(current_table);
	}
	let x = 0;
	let y = 0;
	for (let i=row_index; i < row_index+row_cols.length; i++) {
		for (let j=col_index; j < col_index+row_cols[0].length; j++) {
			let cell_elm;
			if (i === -1) {
				cell_elm = $(`#${current_table} > thead > tr > th:nth-child(${j+1})`);
				tables_data[data["table_index"]]["columns"][j]["title"] = row_cols[y][x];
			} else {
				cell_elm = $(`#${current_table} > tbody > tr:nth-child(${i+1}) > td:nth-child(${j+1})`);
				let row_key = tables_data[data["table_index"]]["columns"][j]["field"];
				tables_data[data["table_index"]]["rows"][i][row_key] = row_cols[y][x];
			}
			cell_elm.text(row_cols[y][x]);
			x++;
		}
		y++;
		x = 0;
	}
	addUndoHistory(current_table);
}

function selectAll(event) {
	if (event.ctrlKey && event.key === "a") {
		event.preventDefault();
		let input_focus = $("input:focus, textarea:focus");
		if (input_focus.length === 0) {
			getSelectable().selectAll();
		} else {
			$(":focus").select();
		}
	}
}

function backspaceDelete() {
	let cells = getSelectedCells();
	if (cells.length > 0) {
		clearCells(cells);
	}
}

function clearCells(cells: Array<string>) {
	for (let cell of cells) {
		let el = $("#"+cell);
		el.text("");
		let data = getSelectionData();
		let table_index = data["table_index"];
		let col_index = el.index();
		if (el.prop("tagName") === "TH") {
			tables_data[table_index]["columns"][col_index]["title"] = "";
		} else {
			let row_key = tables_data[table_index]["columns"][col_index]["field"];
			let row_index = el.parent().index();
			tables_data[table_index]["rows"][row_index][row_key] = "";
		}
	}
	addUndoHistory(current_table);
}

function createUndoHistory(table_id) {
	for (let i=0; i < tables_data.length; i++) {
		if (tables_data[i]["id"] == table_id) {
			if (!undo_history.hasOwnProperty(table_id)) {
				undo_history[table_id] = {"index": 0, "history": []};
				undo_history[table_id]["history"].push(makeCopy(tables_data[i]));
			}
		}
	}
}

function addUndoHistory(table_id) {
	for (let i=0; i < tables_data.length; i++) {
		if (tables_data[i]["id"] === table_id) {
			let index = undo_history[table_id]["index"];
			if (!util.isDeepStrictEqual(tables_data[i], undo_history[table_id]["history"][index])) {
				undo_history[table_id]["history"].splice(0, index, makeCopy(tables_data[i]));
				undo_history[table_id]["index"] = 0;
			}
		}
	}
}

function undo() {
	let index = undo_history[current_table]["index"];
	if (undo_history[current_table]["history"].length > index+1) {
		index++;
		for (let i=0; i < tables_data.length; i++) {
			if (tables_data[i]["id"] == current_table) {
				tables_data[i] = makeCopy(undo_history[current_table]["history"][index]);
				undo_history[current_table]["index"] = index;
			}
		}
		refreshPage();
	}
}

function redo() {
	let index = undo_history[current_table]["index"];
	if (index > 0) {
		index--;
		for (let i=0; i < tables_data.length; i++) {
			if (tables_data[i]["id"] == current_table) {
				tables_data[i] = makeCopy(undo_history[current_table]["history"][index]);
				undo_history[current_table]["index"] = index;
			}
		}
		refreshPage();
	}
}

function deleteUndoHistory(table_id) {
	delete undo_history[table_id];
}

function getColumnIDs(columns): Array<string> {
	let ids = [];
	for (let col of columns) {
		ids.push(col["field"]);
	}
	return ids;
}

function getRowIDs(rows): Array<string> {
	let ids = [];
	for (let row of rows) {
		ids.push(row["uid"]);
	}
	return ids;
}

function getTableIDs(): Array<string> {
	let ids = [];
	for (let table of tables_data) {
		ids.push(table["id"]);
	}
	return ids;
}

function makeCopy(json) {
	return JSON.parse(JSON.stringify(json));
}

nav.on("wheel", (event) => {
	event.preventDefault();
	if (event.originalEvent.deltaY > 0) {
		nav_scroll += 100;
		nav_scroll = Math.min(nav_scroll, nav.prop("scrollWidth") - nav.width());
		let speed = nav_scroll - nav.scrollLeft();
		nav.stop();
		nav.animate({scrollLeft: nav_scroll}, {speed: speed, easing: "linear"});
	} else {
		nav_scroll -= 100;
		nav_scroll = Math.max(nav_scroll, 0);
		let speed = nav.scrollLeft() - nav_scroll;
		nav.stop();
		nav.animate({scrollLeft: nav_scroll}, {speed: speed, easing: "linear"});
	}
});

detail_tab_name.on("input", (event) => {
	let input = $(event.target);
	let new_tab_name = input.val();
	let data = getSelectionData();
	tables_data[data["table_index"]]["tab_name"] = new_tab_name;
})
	.on("focusout", (event) => {
		addUndoHistory(current_table);
	});

detail_table_name.on("input", (event) => {
	let input = $(event.target);
	let new_table_name = input.val();
	let data = getSelectionData();
	$(`#${current_table}-nav`).text(new_table_name);
	tables_data[data["table_index"]]["name"] = new_table_name;
})
	.on("focusout", (event) => {
		addUndoHistory(current_table);
	});

detail_table_description.on("input", (event) => {
	let input = $(event.target);
	let new_table_description = input.val();
	let data = getSelectionData();
	tables_data[data["table_index"]]["description"] = new_table_description;
})
	.on("focusout", (event) => {
		addUndoHistory(current_table);
	});

detail_table_hidden.on("input", (event) => {
	let input = $(event.target);
	let table_hidden = input.prop("checked");
	let data = getSelectionData();
	tables_data[data["table_index"]]["hidden"] = table_hidden;
	addUndoHistory(current_table);
});

detail_column_name.on("input", (event) => {
	let input = $(event.target);
	let new_col_name = input.val();
	let data = getSelectionData();
	$(`#${data["col_id"]}`).text(new_col_name);
	tables_data[data["table_index"]]["columns"][data["col_index"]]["title"] = new_col_name;
})
	.on("focusout", (event) => {
		addUndoHistory(current_table);
	});

detail_column_selectable.on("input", (event) => {
	let input = $(event.target);
	let column_selectable = input.prop("checked");
	let data = getSelectionData();
	tables_data[data["table_index"]]["columns"][data["col_index"]]["selectable"] = column_selectable;
	addUndoHistory(current_table);
});

detail_column_sortable.on("input", (event) => {
	let input = $(event.target);
	let column_sortable = input.prop("checked");
	let data = getSelectionData();
	tables_data[data["table_index"]]["columns"][data["col_index"]]["sortable"] = column_sortable;
	addUndoHistory(current_table);
});

detail_cell_text.on("input", (event) => {
	let input = $(event.target);
	let new_cell_text = input.val();
	let data = getSelectionData();
	$(`#${data["cell_id"]}`).text(new_cell_text);
	tables_data[data["table_index"]]["rows"][data["row_index"]][data["row_key"]] = new_cell_text;
})
	.on("focusout", (event) => {
		addUndoHistory(current_table);
	});

position_edit_btn.on("click", () => {
	toggleMovement();
})

$(document).on("copy", (event) => {
	let input_focus = $("input:focus, textarea:focus");
	if (input_focus.length === 0) {
		event.preventDefault();
		copyToClipboard();
	}
})

$(document).on("cut", (event) => {
	let input_focus = $("input:focus, textarea:focus");
	if (input_focus.length === 0) {
		event.preventDefault();
		copyToClipboard(true);
	}
})

$(document).on("paste", (event) => {
	let input_focus = $("input:focus, textarea:focus");
	if (input_focus.length === 0) {
		event.preventDefault();
		pasteFromClipboard();
	}
})

$(document).on("keydown", (event: JQuery.KeyDownEvent) => {
	selectAll(event);
	let input_focus = $("input:focus, textarea:focus");
	if (input_focus.length === 0) {
		event.preventDefault();
		if (!event.ctrlKey && !event.shiftKey && !event.altKey && (event.key == "Backspace" || event.key == "Delete")) {
			backspaceDelete();
		}
		if (!event.shiftKey && !event.altKey && event.ctrlKey && event.key == "z") {
			undo();
		}
		if (!event.shiftKey && !event.altKey && event.ctrlKey && event.key == "y") {
			redo();
		}
	}
})

ipcRenderer.on("open", (event, json) => {
	openFile(json);
})

ipcRenderer.on("save", (event, create_new_file) => {
	saveFile(create_new_file);
});

ipcRenderer.on("select-all", (event) => {
	let keydown_event = jQuery.Event( "keydown", {
		bubbles: true,
		cancelable: true,
		ctrlKey: true,
		key: "a",
		code: "KeyA",
	});
	$(document).trigger(keydown_event);
})

ipcRenderer.on("undo", (event) => {
	undo();
})

ipcRenderer.on("redo", (event) => {
	redo();
})
