<script module lang="ts">
	export interface RowState {
		date:	{ valid: boolean, value: string}, 
		start:	{ valid: boolean, value: string}, 
		end:	{ valid: boolean, value: string}, 
		comment: { value: string }
	}
</script>
<script lang="ts">
	import type { Snippet } from "svelte";

	import { toInt, parseDate, calculateDuration, weekday_of } from "$lib/util";

	interface Props {
		targetForm: string,
		enabled: boolean,
		autofocus: boolean,
		states: RowState,
		children: Snippet
	}
	let { targetForm, enabled, autofocus = false, states = $bindable(), children }: Props = $props();

	const TODAY: Date			= new Date();
	const CENTURY_PREF: number	= Math.floor(TODAY.getFullYear() / 100);
	const CENTURY_YEAR: number	= CENTURY_PREF * 100;

	let dateInput: HTMLInputElement;
	let startInput: HTMLInputElement;
	let endInput: HTMLInputElement;

	states = {
		...states,
		...(states?.date ? states.date : {}),
		...(states?.start ? states.start : {}),
		...(states?.end ? states.end : {}),
		...(states?.comment ? states.comment : {}),
		date: {
			...states.date,
			...(states.date?.valid == null && { valid: true }),
			...(states.date?.value == null && { value: "" }),
		},
		start: {
			...states.start,
			...(states.start?.valid == null && { valid: true }),
			...(states.start?.value == null && { value: "" }),
		},
		end: {
			...states.end,
			...(states.end?.valid == null && { valid: true }),
			...(states.end?.value == null && { value: "" }),
		},
		comment: {
			...states.comment,
			...(states.comment?.value == null && { value: "" }),
		},
	};

	let inWeekDay: string = $derived.by(() => {
		let date = null;
		if (states?.date?.valid && states?.date?.value != null) {
			date = parseDate(states.date.value); 
		}
		return weekday_of(date);
	})
	let inDuration: string = $state("");
	
	$effect(() => {
		states;
		if ((states?.start?.valid && states?.end?.valid)
			&& (states?.start?.value != null && states?.end?.value != null)) {
				let duration = calculateDuration(states.start.value, states.end.value);
				inDuration = isNaN(duration) ? "" : duration.toFixed(2);
		}
	})

	function validateDate(element: HTMLInputElement) {
		
		/*
		todo: trailing . after month
		supports:
			D.M

			DDMM
			D.MM
			DD.M
			
			DD.MM
			
			DDMMYY
			D.M.YY

			DD.M.YY
			D.MM.YY
			
			D.M.YYYY
			DD.MM.YY
			DDMMYYYY

			DD.M.YYYY
			D.MM.YYYY
			
			DD.MM.YYYY
		*/

		switch (element.value.length) {
			case 0: return true;
			case 3: {
					/*
					 * D.M
					 */

					let day		= toInt(element.value.charAt(0));
					let month	= toInt(element.value.charAt(2));

					if (isNaN(day) || isNaN(month) || element.value.charAt(1) !== '.') {
						return false;
					}

					if (day > 0 && month > 0) {
						element.value = "0" + day + ".0" + month + "." + TODAY.getFullYear();
						return true;
					}
				} break;
			case 4: if (
				(() =>{
					/*
					 * DDMM
					 */
					
					let day		= toInt(element.value.slice(0, 2));
					let month	= toInt(element.value.slice(2, 4));

					if (isNaN(day) || isNaN(month)) {
						return false;
					}

					if (!(month < 1 || month > 12
						|| day < 1 || day > new Date(TODAY.getFullYear(), month, 0).getDate())) {
						element.value = element.value.slice(0, 2) + "." + element.value.slice(2, 4) + "." + TODAY.getFullYear();
						return true;
					}

					return false;
				})() === true
				|| (() => {
					/*
					 * DD.M
					 */

					let day		= toInt(element.value.slice(0, 2));
					let month	= toInt(element.value.charAt(3));

					if (isNaN(day) || isNaN(month) || element.value.charAt(2) !== '.') {
						return false;
					}

					if (!(month < 1 || month > 12
						|| day < 1 || day > new Date(TODAY.getFullYear(), month, 0).getDate())) {
						element.value = element.value.slice(0, 2) + ".0" + month + "." + TODAY.getFullYear();
						return true;
					}

					return false;
				})() === true
				|| (() => {
					/*
					 * D.MM
					 */
					
					let day		= toInt(element.value.charAt(0));
					let month	= toInt(element.value.slice(2, 4));

					if (isNaN(day) || isNaN(month) || element.value.charAt(1) !== '.') {
						return false;
					}

					if (!(month < 1 || month > 12
						|| day < 1 || day > new Date(TODAY.getFullYear(), month, 0).getDate())) {
						element.value = "0" + day + "." + element.value.slice(2, 4) + "." + TODAY.getFullYear();
						return true;
					}

					return false;
				})() === true) {
					return true;
				} break;
			case 5: {
					/*
					 * DD.MM
					 */

					let day		= toInt(element.value.slice(0, 2));
					let month	= toInt(element.value.slice(3, 5));

					if (isNaN(day) || isNaN(month) || element.value.charAt(2) !== '.') {
						return false;
					}

					if (!((month < 1 || month > 12)
						|| (day < 1 || day > new Date(TODAY.getFullYear(), month, 0).getDate()))) {
						element.value = element.value + "." + TODAY.getFullYear();
						return true;
					}
				} break;
			case 6: if ((() => {
					/*
					 * DDMMYY
					 */

					let day		= toInt(element.value.slice(0, 2));
					let month	= toInt(element.value.slice(2, 4));
					let year	= toInt(element.value.slice(4, 6));

					if (isNaN(day) || isNaN(month) || isNaN(year)) {
						return false;
					}

					year += CENTURY_YEAR;

					if (!(month < 1 || month > 12
						|| day < 1 || day > new Date(year, month, 0).getDate())) {
						element.value = element.value.slice(0, 2) + "." + element.value.slice(2, 4) + "." + year;
						return true;
					}
					return false;
				})() === true
				|| (() => {
					/*
					 * D.M.YY
					 */
					
					let day		= toInt(element.value.charAt(0));
					let month	= toInt(element.value.charAt(2));
					let year	= toInt(element.value.slice(4, 6));

					if (isNaN(day) || isNaN(month) || isNaN(year) || element.value.charAt(1) !== '.' || element.value.charAt(3) !== '.') {
						return false;
					}

					year += CENTURY_YEAR;

					if (!(month < 1 || month > 12
						|| day < 1 || day > new Date(year, month, 0).getDate())) {
						element.value = "0" + day + ".0" + month + "." + year;
						return true;
					}

					return false;
				})() === true) {
					return true;
				} break;
			case 7: if ((() => {
					/*
					 * D.MM.YY
					 */
					let day		= toInt(element.value.charAt(0));
					let month	= toInt(element.value.slice(2, 4));
					let year	= toInt(element.value.slice(5, 7));

					if (isNaN(day) || isNaN(month) || element.value.charAt(1) !== '.' || element.value.charAt(4) !== '.') {
						return false;
					}

					year += CENTURY_YEAR;

					if (!(month < 1 || month > 12
						|| day < 1 || day > new Date(year, month, 0).getDate())) {
						element.value = "0" + element.value.slice(0, 5) + year;
						return true;
					}

					return false;
				})() === true
				|| (() => {	
					/*
					 * D.MM.YY
					 */
					let day		= toInt(element.value.slice(0, 2));
					let month	= toInt(element.value.charAt(3));
					let year	= toInt(element.value.slice(5, 7));

					if (isNaN(day) || isNaN(month) || element.value.charAt(2) !== '.' || element.value.charAt(4) !== '.') {
						return false;
					}

					year += CENTURY_YEAR;

					if (!(day < 1 || day > new Date(year, month, 0).getDate())) {
						element.value = element.value.slice(0, 3) + "0" + month + "." + year;
						return true;
					}

					return false;
				})() === true) {
					return true;
				} break;
			case 8: if (
				(() => {
					/*
					 * D.M.YYYY
					 */

					let day		= toInt(element.value.charAt(0));
					let month	= toInt(element.value.charAt(2));
					let year	= toInt(element.value.slice(4, 8));

					if (isNaN(day) || isNaN(month) || isNaN(year) || element.value.charAt(1) !== '.' || element.value.charAt(3) !== '.') {
						return false;
					}

					year += CENTURY_YEAR;

					if (!(month < 1 || month > 12
						|| day < 1 || day > new Date(year, month, 0).getDate())) {
						element.value = "0" + day + ".0" + month + "." + element.value.slice(4, 8);
						return true;
					}

					return false;
				})() === true
				|| (() => {
					/*
					 * DD.MM.YY
					 */

					let day		= toInt(element.value.slice(0, 2));
					let month	= toInt(element.value.slice(3, 5));
					let year	= toInt(element.value.slice(6, 8));

					if (isNaN(day) || isNaN(month) || isNaN(year) || element.value.charAt(2) !== '.' || element.value.charAt(5) !== '.') {
						return false;
					}

					year += CENTURY_YEAR;

					if (!(month < 1 || month > 12
						|| day < 1 || day > new Date(year, month, 0).getDate())) {
						element.value = element.value.slice(0, 2) + "." + element.value.slice(3, 5) + "." + CENTURY_PREF + element.value.slice(6, 8);
						return true;
					}
					return false;
				})() === true
				|| (() => {
					/*
					 * DDMMYYYY
					 */
					
					let day		= toInt(element.value.slice(0, 2));
					let month	= toInt(element.value.slice(2, 4));
					let year	= toInt(element.value.slice(4, 8));

					if (isNaN(day) || isNaN(month) || isNaN(year)) {
						return false;
					}

					if (!(month < 1 || month > 12
						|| day < 1 || day > new Date(year, month, 0).getDate())) {
						element.value = element.value.slice(0, 2) + "." + element.value.slice(2, 4) + "." + element.value.slice(4, 8);
						return true;
					}

					return false;
				})() === true) {
					return true;
				} break;
			case 9: if ((() => {
					/*
					 * D.MM.YYYY
					 */
					let day		= toInt(element.value.charAt(0));
					let month	= toInt(element.value.slice(2, 4));
					let year	= toInt(element.value.slice(5, 9));

					if (isNaN(day) || isNaN(month) || element.value.charAt(1) !== '.' || element.value.charAt(4) !== '.') {
						return false;
					}

					if (!(month < 1 || month > 12
						|| day < 1 || day > new Date(year, month, 0).getDate())) {
						element.value = "0" + element.value;
						return true;
					}

					return false;
				})() === true
				|| (() => {	
					/*
					 * D.MM.YYYY
					 */
					let day		= toInt(element.value.slice(0, 2));
					let month	= toInt(element.value.charAt(3));
					let year	= toInt(element.value.slice(5, 9));

					if (isNaN(day) || isNaN(month) || element.value.charAt(2) !== '.' || element.value.charAt(4) !== '.') {
						return false;
					}

					if (!(day < 1 || day > new Date(year, month, 0).getDate())) {
						element.value = element.value.slice(0, 3) + "0" + month + element.value.slice(4, 9);
						return true;
					}

					return false;
				})() === true) {
					return true;
				} break;
			case 10: {
					/*
					 * DD.MM.YYYY
					 */

					let day		= toInt(element.value.slice(0, 2));
					let month	= toInt(element.value.slice(3, 5));
					let year	= toInt(element.value.slice(6, 10));

					if (isNaN(day) || isNaN(month) || isNaN(year) || element.value.charAt(2) !== '.' || element.value.charAt(5) !== '.') {
						return false;
					}

					if (!(month < 1 || month > 12
						|| day < 1 || day > new Date(year, month, 0).getDate())) {
						return true;
					}
			} break;
		}
		
		return false;
	}

	function validateTime(element: HTMLInputElement): boolean {
		
		/*
		supports:
			HH
			H:MM
			HHMM
			HH:MM
		 */

		switch(element.value.length) {
			case 0: return true;
			case 2: {
				let h = toInt(element.value);

				if (!isNaN(h) && 0 < h && h <= 24) {
					element.value = element.value + ":00";
					return true;
				}
			} break;
			case 4: if (
				(() => {
					let h = toInt(element.value.slice(0, 2));
					let m = toInt(element.value.slice(2, 4));

					if (isNaN(h) || isNaN(m)) {
						return false;
					}

					if (0 <= h && h <= 24 && 0 <= m && m <= 59) {
						element.value = element.value.slice(0, 2) + ":" + element.value.slice(2, 4);
						return true;
					}
					return false;
				})() === true
				|| (() => {
					let h = toInt(element.value.charAt(0));
					let m = toInt(element.value.slice(2, 4));

					if (isNaN(h) || isNaN(m) || element.value.charAt(1) !== ':') {
						return false;
					}

					if (0 <= m && m <= 59) {
						element.value = "0" + element.value;
						return true;
					}
					return false;
				})() === true) {
					return true;
				}
			case 5: {	
				let h = toInt(element.value.slice(0, 2));
				let m = toInt(element.value.slice(3, 5));

				if (isNaN(h) || isNaN(m) || element.value.charAt(2) !== ':') {
					return false;
				}

				if (0 <= h && h <= 24 && 0 <= m && m <= 59) {
					return true;
				}
			} break;
		}

		return false;
	}

</script>

<!--<tr>
	{#each { length: prepend_td }, n}
		<td></td>
	{/each}-->
	<td>
		<input 
			bind:this={dateInput}
			bind:value={states.date.value}
			class:form-invalid={!states.date.valid}
			name="date"
			type="text"
			form={targetForm} 
			onfocusin={
				(_) => {
					dateInput.select();
					states.date.valid = true;
				}
			}
			onfocusout={
				(_) => {
					states.date.valid = validateDate(dateInput);
					states.date.value = dateInput.value;
				}
			}
			onkeydown={
				(event) => {
					if (event.key == "Enter") {
						states.date.valid = validateDate(dateInput);
						states.date.value = dateInput.value;
					}
				}
			}
			disabled={!enabled}
			{autofocus}
		required>
	</td>

	<td>
		{inWeekDay}
	</td>

	<td>
		<input
			bind:this={startInput}
			bind:value={states.start.value}
			class:form-invalid={!states.start.valid}
			name="start"
			type="text"
			form={targetForm} 
			onfocusin={
				(_) => {
					startInput.select();
					states.start.valid = true;
				}
			}
			onfocusout={
				(_) => {
					states.start.valid = validateTime(startInput);
					states.start.value = startInput.value;
				}
			}
			onkeydown={
				(event) => {
					if (event.key == "Enter") {
						states.start.valid = validateTime(startInput);
						states.start.value = startInput.value;
					}
				}
			}
			disabled={!enabled}
		required>
	</td>

	<td>
		<input 
			bind:this={endInput}
			bind:value={states.end.value}
			class:form-invalid={!states.end.valid}
			name="end"
			type="text"
			form={targetForm} 
			onfocusin={
				(_) => {
					endInput.select();
					states.end.valid = true;
				}
			}
			onfocusout={
				(_) => {
					states.end.valid = validateTime(endInput);
					states.end.value = endInput.value;
				}
			}
			onkeydown={
				(event) => {
					if (event.key == "Enter") {
						states.end.valid = validateTime(endInput);
						states.end.value = endInput.value;
					}
				}
			}
			disabled={!enabled}
		required>
	</td>

	<td>
		{inDuration}
	</td>

	<td>
		<input
			name="comment"
			type="text"
			form={targetForm}
			value={states.comment.value}
			disabled={!enabled}>
	</td>

	<td class="action">
		{@render children?.()}
	</td>

<!--</tr>-->

<style>

/* * {
	border: 1px solid;
}*/

td input {
	box-sizing: border-box;
	width: 100%;

	vertical-align: middle;

	border: 1px solid gray;
}

.action {
	display: inline-flex;
}

.form-invalid {
	background: #FF4444;
}

</style>
