<script lang="ts">
    import {locals} from '../../ts/stores.js';
    import {FileSize} from "../../../common/FileSize.js";
    import Message from "../components/Message.svelte";
    import Icon from "./Icon.svelte";
    import {getContext} from "svelte";
    import {dateToDatetimeLocal, dateToIsoString} from "../../ts/datetime-local.js";

    export let type: string;
    export let name: string;
    type FieldValue = string | number | boolean | Record<string, FieldValue>;
    export let value: FieldValue | undefined = undefined;
    export let initialValue: FieldValue | undefined = undefined;
    export let placeholder: string | undefined = undefined;
    export let hint: string | undefined = undefined;
    export let extraData: string[] | undefined = undefined;
    export let icon: string | undefined = undefined;
    export let validation = $locals.validation?.[name] as { message: string, value?: string } | undefined;

    const formId = getContext('formId');
    const fieldId = `${formId}-${name}-field`;

    const previousFormData = $locals.previousFormData as FieldValue | undefined || [];
    let previousFieldData = previousFormData[name];
    if (typeof value === 'number' && previousFieldData) previousFieldData = Number(previousFieldData);

    value = type !== 'hidden' && previousFieldData || value || initialValue || validation?.value || '';

    $: initialDatetimeLocalValue = type === 'datetime-local' && typeof value === 'string' ? dateToDatetimeLocal(new Date(value)) : undefined;

    function durationValue(f: string): number {
        if (previousFormData[name]) {
            return value[f];
        }

        switch (f) {
            case 's':
                return value % 60;
            case 'm':
                return (value - value % 60) / 60 % 60;
            case 'h':
                return (value - value % 3600) / 3600;
            default:
                return 0;
        }
    }

    function focusInput(e) {
        if (input) {
            if (input.type === 'checkbox') {
                if (e.target !== label && e.target !== input) {
                    input.click();
                }
            } else if (['file', 'color'].indexOf(input.type) >= 0) {
                input.click();
            } else {
                input.focus();
            }
        } else {
            this.querySelector('input')?.focus();
        }
    }

    function handleInput() {
        switch (this.type) {
            case 'number':
            case 'range':
                value = +this.value;
                break;
            case 'file':
                handleFileInput();
                break;
            case 'datetime-local':
                value = dateToIsoString(new Date(this.value));
                break;
            case 'checkbox':
                value = !!this.checked;
                break;
            default:
                value = this.value;
                break;
        }
    }

    let input: HTMLInputElement;
    let label: HTMLLabelElement;

    function chooseFile() {
        input.click();
    }

    let files: FileList | undefined;

    function handleFileInput() {
        files = input.files;
    }

    let focused = false;
</script>

<style lang="scss">
	@import "../../scss/helpers";

	.form-field:not(.hidden) {
		display: flex;
		flex-direction: column;
		margin: 16px auto;

		.control {
			position: relative;
			display: flex;
			align-items: start;
			flex-direction: row;

			color: var(--on-input);
			background-color: var(--input);
			border-radius: 5px;

			> :global(.icon) {
				--icon-size: 24px;
				margin: 18px;
				opacity: 0.75;
			}
		}

		label {
			position: absolute;
			z-index: 1;
			left: 8px;
			top: 22px;
			user-select: none;
			font-size: 16px;

			transition-property: top, font-size;
			transition-duration: 150ms;
			transition-timing-function: ease-out;

			cursor: text;
		}

        .has-icon label {
            left: 68px;
        }

		&.disabled {
			opacity: 0.5;

            &, * {
				cursor: not-allowed;
            }
		}

		input, select, textarea {
			z-index: 1;
			border: 0;
			color: inherit;
			background: transparent;
			font-size: 16px;
			border-radius: 5px;

			outline-offset: 0;
		}

		&:not(.empty),
        select ~,
        [type="file"] ~,
        [type="color"] ~,
        [type="number"] ~,
        .focused ~,
        :focus ~,
        fieldset {
			.sections label, legend, .time-input label {
				top: 8px;
				font-size: 14px;
			}
		}

		input,
        select,
        select,
        textarea,
        .form-display,
		.textarea-growing-wrapper,
		.textarea-growing-wrapper:after {
			display: block;
			padding: 32px 8px 8px;
			width: 100%;
			height: 60px;
		}

		select {
            position: relative;
			-webkit-appearance: none;
			-moz-appearance: none;
			appearance: none;
            cursor: pointer;


			&::-ms-expand {
				display: none;
			}

			& + :global(.icon) {
				position: absolute;
				pointer-events: none;
				right: 0;
				top: 0;

				transition: transform 150ms ease-out;
			}

			// TODO: Temporary
			&:focus + :global(.icon) {
				transform: rotateX(180deg);
			}
		}

		.textarea-growing-wrapper {
			display: grid;
			flex-grow: 1;
			height: revert;
			padding: 0;

			&:after {
				content: attr(data-value) " ";
				color: red;
				box-sizing: border-box;
				font: inherit;
				visibility: hidden;
			}

			textarea {
				height: auto;
				resize: none;
				font-family: inherit;
				overflow: hidden;
			}

			&:after, textarea {
				grid-area: 1 / 1 / 2 / 2;
				margin-left: revert;
				min-height: 100px;
				height: revert;
				white-space: pre-wrap;
				word-wrap: anywhere;
			}
		}

		.has-icon > {
			input,
			select,
			select,
            fieldset,
			.form-display,
			.textarea-growing-wrapper,
			.textarea-growing-wrapper:after,
			.textarea-growing-wrapper > textarea {
				margin-left: -60px;
				padding-left: 68px;
			}

			.textarea-growing-wrapper > textarea {
                margin-left: -68px;
                width: calc(100% + 68px);
			}
		}

		input[type=color] {
			height: calc(32px + 8px + 32px);
		}

		&.checkbox, &.color, &.file {
			input, label {
				cursor: pointer;
			}
		}

		&.checkbox {
			.control {
				display: flex;
				flex-direction: row;
				align-items: stretch;
				flex-grow: 1;
			}

			input {
				width: 20px;
				height: 20px;
				margin: auto 8px;
				text-align: left;

				& ~ .sections {
					flex-grow: 1;
					display: flex;
					flex-direction: column;
					justify-content: center;

					label {
						position: static;
						font-size: 16px;
					}
				}
			}
		}

		&.file {
			input {
				@include fake-hide;
			}

			label {
				position: static;
				display: block;
				padding: 8px;
				font-size: revert !important;
			}
		}

		.files {
			display: flex;
			flex-direction: column;
			align-items: start;

			.file {
				margin: 8px;
				padding: 8px;

				color: var(--on-primary);
				background: var(--primary);
				border-radius: 5px;

				> div {
					display: inline-block;
					padding: 8px;
				}

				.name {
					font-size: 20px;

					:global(.icon) {
						--icon-size: 24px;
						margin-right: 8px;
					}
				}
			}
		}

		fieldset {
			display: flex;
			flex-direction: row;
			align-items: start;
            width: 100%;
            margin: 0;
            padding-top: 8px;
            border: 0;

			legend {
				padding: 8px;
			}

			.time-input {
				position: relative;
				flex: 1;
				margin: 0;

				label {
					left: 8px;
				}
			}

			+ {
				.error, .hint {
					margin-top: -16px;
					margin-bottom: 16px;
				}
			}
		}
	}

	.form-field, fieldset + {
		.error, .hint {
			padding: 2px 2px 2px 4px;
			text-align: left;
			font-size: 14px;
			color: var(--color);
		}
	}
</style>

{#if type === 'hidden'}
    {#if validation}
        <Message type="error" content={validation.message}/>
    {/if}
    <input type="hidden" name={name} value={value}>
{:else}
    <div class="form-field"
         class:checkbox={type === 'checkbox'}
         class:color={type === 'color'}
         class:file={type === 'file'}
         class:empty={value === ''}
         class:disabled={Object.keys($$restProps).indexOf('disabled') >= 0}>
        <div class="control" class:has-icon={icon} on:click={focusInput}>
            {#if icon}
                <Icon name={icon}/>
            {/if}

            {#if type === 'duration'}
                <fieldset>
                    <legend>{placeholder}</legend>
                    {#each Object.keys(extraData) as f}
                        <div class="time-input">
                            <input type="number" name="{name}[{f}]" id="{fieldId}-{f}"
                                   value={durationValue(f)}
                                   min="0" max={(f === 's' || f === 'm') && '60' || undefined}
                                   {...$$restProps} on:click={e => e.stopPropagation()}>
                            <label for="{fieldId}-{f}" on:click={e => e.stopPropagation()}>{extraData[f] || f}</label>
                        </div>
                    {/each}
                </fieldset>
            {:else if type === 'select'}
                <select name={name} id={fieldId} {...$$restProps} bind:this={input} bind:value={value} on:input={handleInput}>
                    {#each extraData as option}
                        <option value={(option.display === undefined || option.value !== undefined) && (option.value || option)}
                        >{option.display || option}</option>
                    {/each}
                </select>
                <Icon name="chevron-down"/>
            {:else if type === 'textarea'}
                <div class="textarea-growing-wrapper" class:focused={focused} data-value={value}>
                    <textarea {name} id={fieldId} {value} {...$$restProps} bind:this={input}
                              on:input={handleInput}
                              on:focusin={() => focused = true}
                              on:focusout={() => focused = false}></textarea>
                </div>
            {:else if type === 'checkbox'}
                <input {type} {name} id={fieldId} checked={!!value} {...$$restProps} bind:this={input}
                       on:change={handleInput}>
            {:else if type === 'datetime-local'}
                <input {type} bind:this={input} on:input={handleInput} value={initialDatetimeLocalValue}>
                <input type="hidden" {name} {value}>
            {:else}
                <input {type} {name} id={fieldId} {value} {...$$restProps} bind:this={input} on:input={handleInput}
                       tabindex={type === 'file' ? '-1' : undefined}>
            {/if}

            <div class="sections">
                {#if type !== 'duration'}
                    <label for={fieldId} bind:this={label}>{@html placeholder || ''}
                        <slot/>
                    </label>
                {/if}

                {#if type === 'file'}
                    {#if files}
                        <div class="files">
                            {#each files as file}
                                <div class="file">
                                    <div class="name" title="Type: {file.type}">
                                        <Icon name="file"/> {file.name}
                                    </div>
                                    <div class="size" title="{file.size} bytes">
                                        {FileSize.humanizeFileSize(file.size, true)}
                                    </div>
                                </div>
                            {/each}
                        </div>
                    {/if}
                    <button type="button" on:click={chooseFile}>Browse...</button>
                {/if}
            </div>
        </div>

        {#if validation}
            <div class="error">
                <Icon name="alert-circle"/> {validation.message}</div>
        {/if}
        {#if hint}
            <div class="hint">
                <Icon name="info"/> {hint}</div>
        {/if}
    </div>
{/if}

