<script>
	export let rows;
	export let columns;
	export let rowKey;  //Must only be null/undefined if the row is a new object (not associated with a row in the table).  Should not change.
	export let edited;
	export let actions;
	export let selection;  //TODO: allow multiple selections?  Make selections optional?

	// Setup a width for each column.
	columns.forEach(column => {
		let min = column.minWidth ? Math.max(10, column.minWidth) : 10;
		let weight = column.weight ? Math.max(1, column.weight) : 1;
		column.width = 'minmax(' + min + 'px, ' + weight + 'fr)';
		column.isActions = false;
	});
	// Add the actions column to the end.
	// TODO: Allow it to be positioned.
	if(actions) {
		// TODO: make this fixed width based on the possible widget widths.
		actions.width = 'minmax(10px, 1fr)';
		actions.isActions = true;
		columns[columns.length] = actions;
	}
	// Collect the column widths for the layout.
	let gridTemplateColumns = columns.map(({width}) => width).join(' ');

	// Resize column code.
	let headerBeingResized = null;
	let horizontalScrollOffset = 0;
	const initResize = ({target}) => {
		headerBeingResized = target.parentNode;
		window.addEventListener('mousemove', onMouseMove);
		window.addEventListener('mouseup', completeResize);
		headerBeingResized.classList.add('header--being-resized');
	};
	const completeResize = () => {
		window.removeEventListener('mousemove', onMouseMove);
		window.removeEventListener('mouseup', completeResize);
		headerBeingResized.classList.remove('header--being-resized');
		headerBeingResized = null;
	};
	const onMouseMove = e => {
		try {
			// Calculate the desired width.
			horizontalScrollOffset = document.documentElement.scrollLeft;
			let parentX = Math.round(headerBeingResized.getBoundingClientRect().x);
			const width = horizontalScrollOffset + (e.clientX - parentX);
			// Update the column object with the new size value.
			const column = columns.find(({element}) => element === headerBeingResized);
			column.width = Math.max(column.minWidth, width) + "px";
			// Ensure all the column widths are converted to fixed sizes.
			columns.forEach((column, index) => {
				if((index < columns.length - 1) && (column.width.startsWith('minmax'))) {
					column.width = parseInt(column.element.clientWidth, 10) + 'px';
				}
			});
			// Render the new column sizes.
			gridTemplateColumns = columns.map(({width}) => width).join(' ');
		} catch(e) {console.log(e);}
	}

	// Select row code.
	let selectedRowElement = null;
	const selectRow = (e, row) => {
		let element = e.target;

		while(element && element.nodeName !== "TR") element = element.parentNode;

		if(selectedRowElement) {
			selectedRowElement.classList.remove('selected');
		}

		selectedRowElement = element;
		element.classList.add('selected');
	}

	// Edit row code.
	let editorContainer;
	let editorTable;
	// Listen for changes to the edited variable.  Move the editor row and display it when there is a value.
	// Relies on the table row being hidden when the edited value matches the row's value.
	$: {
		if(editorContainer) {
			if($edited) {
				let id = rowKey($edited);
				let hiddenRow = editorTable.querySelector('tbody tr[data-key="' + id + '"]');

				if(!hiddenRow) {
					let body = editorTable.querySelector('tbody');
					body.firstChild ? body.insertBefore(editorContainer, body.firstChild) : body.appendChild(editorContainer);
					editorContainer.classList.remove('hidden');
				}
				else {
					//let editor = hiddenRow.querySelector('.editor');
					let body = editorTable.querySelector('tbody');
					let next = hiddenRow.nextSibling;
					//editor.appendChild(editorContainer);
					next ? body.insertBefore(editorContainer, next) : body.appendChild(editorContainer);
					editorContainer.classList.remove('hidden');
				}
			}
			else {
				console.log("Edited cleared");
				editorContainer.classList.add('hidden');
			}
		}
	}
</script>

<div bind:this={editorContainer} class="editor2 hidden"><slot>Slot</slot></div>
<table bind:this={editorTable} style="--grid-template-columns: {gridTemplateColumns}">
	<thead>
	<tr>
		{#each columns as column}
			{#if column.isActions}
				<th bind:this={column.element}>{column.title}
					{#each column.headerWidgets as widget}
						<span class="material-icons material-symbols-outlined" on:click={widget.action} title="{widget.tooltip}">{widget.icon}</span>
					{/each}
				</th>
			{:else}
				<th bind:this={column.element}>{column.title} <span class="resize-handle" on:mousedown={initResize}></span></th>
			{/if}
		{/each}
	</tr>
	</thead>
	<tbody>
	{#each $rows as row (rowKey(row))}
		<tr data-key="{rowKey(row)}" class:hidden={row === $edited} on:mousedown={(e) => selectRow(e, row)} on:dblclick={(e) => {$edited = row}}>
			{#each columns as column}
				{#if column.isActions}
					<td>
						{#each column.rowWidgets as widget}
							<span class="material-icons material-symbols-outlined" on:click={widget.action(row)}>{widget.icon}</span>
						{/each}
					</td>
				{:else}
					<td>{column.value(row)}</td>
				{/if}
			{/each}
		</tr>
	{/each}
	</tbody>
</table>
<!--<button on:click={() => {$edited = null}} type="button">Stop Editing</button>-->

<style>
	.material-icons {
		cursor: pointer;
	}
	thead .material-icons {
		color: white;
	}
    table {
		width: auto;
		-webkit-box-flex: 1;
		flex: 1;
		display: grid;
		border-collapse: collapse;
		grid-template-columns: var(--grid-template-columns);
    }
    thead, tbody, tr {
        display: contents;
    }
    th, td {
		padding: 15px;
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
		user-select: none;
    }
    th {
		position: -webkit-sticky;
		position: sticky;
		top: 0;
		background: #3f4f3f;
		text-align: left;
		font-weight: normal;
		font-size: 1rem;
		color: white;
		position: relative;
    }
    th:last-child {
		border: 0;
    }
    .resize-handle {
		position: absolute;
		top: 0;
		right: 0;
		bottom: 0;
		background: black;
		opacity: 0;
		width: 3px;
		cursor: col-resize;
    }
    th:last-child .resize-handle {
        display: none;
    }
    .resize-handle:hover, .header--being-resized .resize-handle {
        opacity: 0.5;
    }
    th:hover .resize-handle {
        opacity: 0.3;
    }
    td {
        padding-top: 10px;
        padding-bottom: 10px;
        color: #808080;
    }
    tr:nth-child(even) {
        background: #f8f6ff;
    }
    :global(.selected), :global(.selected) > td {
        background-color: yellow;
    }
    .editor {
        grid-column: 1 / -1;
        display: none;
    }
    /*:global(td.hidden) {*/
    /*    display: none;*/
    /*}*/
    :global(tr.hidden) > td:not(.editor) {
        display: none !important;
    }
    :global(tr.hidden) > td.editor {
        display: block !important;
    }
    :global(div.hidden) {
        display: none !important;
    }

    .editor2 {
	    grid-column: 1/-1;
    }
</style>
