<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->

<template>
<div
	ref="rootEl"
	class="mk_grid_th"
	:class="$style.cell"
	:style="[{ maxWidth: column.width, minWidth: column.width, width: column.width }]"
	data-grid-cell
	:data-grid-cell-row="-1"
	:data-grid-cell-col="column.index"
>
	<div :class="$style.root">
		<div :class="$style.left"/>
		<div :class="$style.wrapper">
			<div ref="contentEl" :class="$style.contentArea">
				<span v-if="column.setting.icon" class="ti" :class="column.setting.icon" style="line-height: normal"/>
				<span v-else>{{ text }}</span>
			</div>
		</div>
		<div
			:class="$style.right"
			@mousedown="onHandleMouseDown"
			@dblclick="onHandleDoubleClick"
		/>
	</div>
</div>
</template>

<script setup lang="ts">
import { computed, nextTick, onMounted, onUnmounted, ref, toRefs, watch } from 'vue';
import { GridEventEmitter, Size } from '@/components/grid/grid.js';
import { GridColumn } from '@/components/grid/column.js';

const emit = defineEmits<{
	(ev: 'operation:beginWidthChange', sender: GridColumn): void;
	(ev: 'operation:endWidthChange', sender: GridColumn): void;
	(ev: 'operation:widthLargest', sender: GridColumn): void;
	(ev: 'change:width', sender: GridColumn, width: string): void;
	(ev: 'change:contentSize', sender: GridColumn, newSize: Size): void;
}>();
const props = defineProps<{
	column: GridColumn,
	bus: GridEventEmitter,
}>();

const { column, bus } = toRefs(props);

const rootEl = ref<InstanceType<typeof HTMLTableCellElement>>();
const contentEl = ref<InstanceType<typeof HTMLDivElement>>();

const resizing = ref<boolean>(false);

const text = computed(() => {
	const result = column.value.setting.title ?? column.value.setting.bindTo;
	return result.length > 0 ? result : ' ';
});

watch(column, () => {
	// 中身がセットされた直後はサイズが分からないので、次のタイミングで更新する
	nextTick(emitContentSizeChanged);
}, { immediate: true });

function onHandleDoubleClick(ev: MouseEvent) {
	switch (ev.type) {
		case 'dblclick': {
			emit('operation:widthLargest', column.value);
			break;
		}
	}
}

function onHandleMouseDown(ev: MouseEvent) {
	switch (ev.type) {
		case 'mousedown': {
			if (!resizing.value) {
				registerHandleMouseUp();
				registerHandleMouseMove();
				resizing.value = true;
				emit('operation:beginWidthChange', column.value);
			}
			break;
		}
	}
}

function onHandleMouseMove(ev: MouseEvent) {
	if (!rootEl.value) {
		// 型ガード
		return;
	}

	switch (ev.type) {
		case 'mousemove': {
			if (resizing.value) {
				const bounds = rootEl.value.getBoundingClientRect();
				const clientWidth = rootEl.value.clientWidth;
				const clientRight = bounds.left + clientWidth;
				const nextWidth = clientWidth + (ev.clientX - clientRight);
				emit('change:width', column.value, `${nextWidth}px`);
			}
			break;
		}
	}
}

function onHandleMouseUp(ev: MouseEvent) {
	switch (ev.type) {
		case 'mouseup': {
			if (resizing.value) {
				unregisterHandleMouseUp();
				unregisterHandleMouseMove();
				resizing.value = false;
				emit('operation:endWidthChange', column.value);
			}
			break;
		}
	}
}

function onForceRefreshContentSize() {
	emitContentSizeChanged();
}

function registerHandleMouseMove() {
	unregisterHandleMouseMove();
	addEventListener('mousemove', onHandleMouseMove);
}

function unregisterHandleMouseMove() {
	removeEventListener('mousemove', onHandleMouseMove);
}

function registerHandleMouseUp() {
	unregisterHandleMouseUp();
	addEventListener('mouseup', onHandleMouseUp);
}

function unregisterHandleMouseUp() {
	removeEventListener('mouseup', onHandleMouseUp);
}

function emitContentSizeChanged() {
	const clientWidth = contentEl.value?.clientWidth ?? 0;
	const clientHeight = contentEl.value?.clientHeight ?? 0;
	emit('change:contentSize', column.value, {
		// バーの横幅も考慮したいので、+3px
		width: clientWidth + 3 + 3,
		height: clientHeight,
	});
}

onMounted(() => {
	bus.value.on('forceRefreshContentSize', onForceRefreshContentSize);
});

onUnmounted(() => {
	bus.value.off('forceRefreshContentSize', onForceRefreshContentSize);
});

</script>

<style module lang="scss">
$handleWidth: 5px;
$cellHeight: 28px;

.cell {
	cursor: pointer;
}

.root {
	display: flex;
	flex-direction: row;
	height: $cellHeight;
	max-height: $cellHeight;
	min-height: $cellHeight;

	.wrapper {
		flex: 1;
		display: flex;
		flex-direction: row;
		overflow: hidden;
		justify-content: center;
	}

	.contentArea {
		display: flex;
		padding: 6px 4px;
		box-sizing: border-box;
		overflow: hidden;
		white-space: nowrap;
		text-align: center;
	}

	.left {
		// rightのぶんだけズレるのでそれを相殺するためのネガティブマージン
		margin-left: -$handleWidth;
		margin-right: auto;
		width: $handleWidth;
		min-width: $handleWidth;
	}

	.right {
		margin-left: auto;
		// 判定を罫線の上に重ねたいのでネガティブマージンを使う
		margin-right: -$handleWidth;
		width: $handleWidth;
		min-width: $handleWidth;
		cursor: w-resize;
		z-index: 1;
	}
}
</style>