import { h, } from "@stencil/core";
import { createRandomString } from "../../util/random-string";
import { isAndroidDevice, isIOSDevice } from "../../util/device";
import { DateFormatter } from "./date-formatter";
import { MDCTextField } from "@material/textfield";
// tslint:disable:no-duplicate-string
const nativeTypeForConsumerType = {
    date: 'date',
    time: 'time',
    // Mobile Safari feature detects as capable of input type `week`,
    // but it just displays a non-interactive input
    // TODO(ads): remove this when support is decent on iOS!
    week: isIOSDevice() ? 'date' : 'week',
    month: 'month',
    quarter: 'date',
    year: 'date',
    datetime: 'datetime-local',
    default: 'datetime-local',
};
const nativeFormatForType = {
    date: 'Y-MM-DD',
    time: 'HH:mm',
    week: 'GGGG-[W]WW',
    month: 'Y-MM',
    'datetime-local': 'Y-MM-DD[T]HH:mm',
};
// tslint:enable:no-duplicate-string
/**
 * @exampleComponent limel-example-date-picker-datetime
 * @exampleComponent limel-example-date-picker-date
 * @exampleComponent limel-example-date-picker-time
 * @exampleComponent limel-example-date-picker-week
 * @exampleComponent limel-example-date-picker-month
 * @exampleComponent limel-example-date-picker-quarter
 * @exampleComponent limel-example-date-picker-year
 * @exampleComponent limel-example-date-picker-formatted
 * @exampleComponent limel-example-date-picker-programmatic-change
 * @exampleComponent limel-example-date-picker-composite
 * @exampleComponent limel-example-date-picker-custom-formatter
 */
export class DatePicker {
    constructor() {
        /**
         * Set to `true` to disable the field.
         * Use `disabled` to indicate that the field can normally be interacted
         * with, but is currently disabled. This tells the user that if certain
         * requirements are met, the field may become enabled again.
         */
        this.disabled = false;
        /**
         * Set to `true` to make the field read-only.
         * Use `readonly` when the field is only there to present the data it holds,
         * and will not become possible for the current user to edit.
         */
        this.readonly = false;
        /**
         * Set to `true` to indicate that the current value of the date picker is
         * invalid.
         */
        this.invalid = false;
        /**
         * Set to `true` to indicate that the field is required.
         */
        this.required = false;
        /**
         * Type of date picker.
         */
        this.type = 'datetime';
        /**
         * Defines the localisation for translations and date formatting.
         * Property `format` customizes the localized date format.
         */
        this.language = 'en';
        this.showPortal = false;
        this.portalId = `date-picker-calendar-${createRandomString()}`;
        this.documentClickListener = (event) => {
            if (event.composedPath().includes(this.textField)) {
                return;
            }
            const element = document.querySelector(`#${this.portalId}`);
            if (!element.contains(event.target)) {
                this.hideCalendar();
            }
        };
        this.formatValue = (value) => this.dateFormatter.formatDate(value, this.internalFormat);
        this.handleCalendarChange = this.handleCalendarChange.bind(this);
        this.handleInputElementChange =
            this.handleInputElementChange.bind(this);
        this.showCalendar = this.showCalendar.bind(this);
        this.dateFormatter = new DateFormatter(this.language);
        this.clearValue = this.clearValue.bind(this);
        this.hideCalendar = this.hideCalendar.bind(this);
        this.onInputClick = this.onInputClick.bind(this);
        this.nativeChangeHandler = this.nativeChangeHandler.bind(this);
        this.preventBlurFromCalendarContainer =
            this.preventBlurFromCalendarContainer.bind(this);
    }
    componentWillLoad() {
        this.useNative = !this.readonly && (isIOSDevice() || isAndroidDevice());
        this.updateInternalFormatAndType();
    }
    componentWillUpdate() {
        this.updateInternalFormatAndType();
    }
    disconnectedCallback() {
        this.hideCalendar();
    }
    render() {
        const inputProps = {
            onAction: this.clearValue,
        };
        if (this.value && !this.readonly && !this.disabled) {
            inputProps.trailingIcon = 'clear_symbol';
        }
        const helperText = this.disabled || this.readonly ? undefined : this.helperText;
        if (this.useNative) {
            return (h("limel-input-field", { disabled: this.disabled, readonly: this.readonly, invalid: this.invalid, label: this.label, helperText: helperText, required: this.required, value: this.formatValue(this.value), type: this.nativeType, onChange: this.nativeChangeHandler }));
        }
        const dropdownZIndex = getComputedStyle(this.host).getPropertyValue('--dropdown-z-index');
        const formatter = this.formatter || this.formatValue;
        return [
            h("limel-input-field", Object.assign({ disabled: this.disabled, readonly: this.readonly, invalid: this.invalid, label: this.label, placeholder: this.placeholder, helperText: helperText, required: this.required, value: this.value ? formatter(this.value) : '', onFocus: this.showCalendar, onBlur: this.hideCalendar, onClick: this.onInputClick, onChange: this.handleInputElementChange, ref: (el) => (this.textField = el) }, inputProps)),
            h("limel-portal", { containerId: this.portalId, visible: this.showPortal, containerStyle: { 'z-index': dropdownZIndex } }, h("limel-flatpickr-adapter", { format: this.internalFormat, language: this.language, type: this.type, value: this.value, ref: (el) => (this.datePickerCalendar = el), isOpen: this.showPortal, formatter: formatter, onChange: this.handleCalendarChange })),
        ];
    }
    updateInternalFormatAndType() {
        this.nativeType = nativeTypeForConsumerType[this.type || 'default'];
        this.nativeFormat = nativeFormatForType[this.nativeType];
        if (this.useNative) {
            this.internalFormat = this.nativeFormat;
        }
        else if (this.formatter || this.format) {
            this.internalFormat = this.format;
        }
        else {
            this.internalFormat = this.dateFormatter.getDateFormat(this.type);
        }
    }
    nativeChangeHandler(event) {
        event.stopPropagation();
        const date = this.dateFormatter.parseDate(event.detail, this.internalFormat);
        this.change.emit(date);
    }
    showCalendar(event) {
        if (this.disabled || this.readonly) {
            event.stopPropagation();
            return;
        }
        this.showPortal = true;
        const inputElement = this.textField.shadowRoot.querySelector('input');
        setTimeout(() => {
            this.datePickerCalendar.inputElement = inputElement;
        });
        event.stopPropagation();
        document.addEventListener('mousedown', this.documentClickListener, {
            passive: true,
        });
        document.addEventListener('blur', this.preventBlurFromCalendarContainer, {
            capture: true,
        });
    }
    preventBlurFromCalendarContainer(event) {
        // We don't want the input element to lose focus when we pick
        // a date in the calendar container.
        // This is also required in order to not close the non
        // automatically closing pickers (type datetime and time)
        // when you pick a value.
        if (event.relatedTarget === this.datePickerCalendar) {
            event.stopPropagation();
        }
    }
    hideCalendar() {
        setTimeout(() => {
            this.showPortal = false;
        });
        document.removeEventListener('mousedown', this.documentClickListener);
        document.removeEventListener('blur', this.preventBlurFromCalendarContainer, { capture: true });
        if (!this.pickerIsAutoClosing()) {
            this.fixFlatpickrFocusBug();
        }
    }
    fixFlatpickrFocusBug() {
        // Flatpickr removes the focus from the input field
        // but the 'visual focus' is still there
        const mdcTextField = new MDCTextField(this.textField.shadowRoot.querySelector('.mdc-text-field'));
        mdcTextField.getDefaultFoundation().deactivateFocus();
        mdcTextField.valid = !this.invalid;
    }
    handleCalendarChange(event) {
        const date = event.detail;
        event.stopPropagation();
        if (this.pickerIsAutoClosing()) {
            this.hideCalendar();
        }
        this.change.emit(date);
    }
    onInputClick(event) {
        if (this.disabled || this.readonly) {
            return;
        }
        if (this.showPortal) {
            return;
        }
        this.showCalendar(event);
    }
    handleInputElementChange(event) {
        if (this.disabled || this.readonly) {
            event.stopPropagation();
            return;
        }
        if (event.detail === '') {
            this.clearValue();
        }
        event.stopPropagation();
    }
    pickerIsAutoClosing() {
        return this.type !== 'datetime' && this.type !== 'time';
    }
    clearValue() {
        this.change.emit(null);
    }
    static get is() { return "limel-date-picker"; }
    static get encapsulation() { return "shadow"; }
    static get originalStyleUrls() {
        return {
            "$": ["date-picker.scss"]
        };
    }
    static get styleUrls() {
        return {
            "$": ["date-picker.css"]
        };
    }
    static get properties() {
        return {
            "disabled": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Set to `true` to disable the field.\nUse `disabled` to indicate that the field can normally be interacted\nwith, but is currently disabled. This tells the user that if certain\nrequirements are met, the field may become enabled again."
                },
                "getter": false,
                "setter": false,
                "reflect": true,
                "attribute": "disabled",
                "defaultValue": "false"
            },
            "readonly": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Set to `true` to make the field read-only.\nUse `readonly` when the field is only there to present the data it holds,\nand will not become possible for the current user to edit."
                },
                "getter": false,
                "setter": false,
                "reflect": true,
                "attribute": "readonly",
                "defaultValue": "false"
            },
            "invalid": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Set to `true` to indicate that the current value of the date picker is\ninvalid."
                },
                "getter": false,
                "setter": false,
                "reflect": true,
                "attribute": "invalid",
                "defaultValue": "false"
            },
            "label": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Text to display next to the date picker"
                },
                "getter": false,
                "setter": false,
                "reflect": true,
                "attribute": "label"
            },
            "placeholder": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "The placeholder text shown inside the input field, when the field is focused and empty"
                },
                "getter": false,
                "setter": false,
                "reflect": true,
                "attribute": "placeholder"
            },
            "helperText": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Optional helper text to display below the input field when it has focus"
                },
                "getter": false,
                "setter": false,
                "reflect": true,
                "attribute": "helper-text"
            },
            "required": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Set to `true` to indicate that the field is required."
                },
                "getter": false,
                "setter": false,
                "reflect": true,
                "attribute": "required",
                "defaultValue": "false"
            },
            "value": {
                "type": "unknown",
                "mutable": false,
                "complexType": {
                    "original": "Date",
                    "resolved": "Date",
                    "references": {
                        "Date": {
                            "location": "global",
                            "id": "global::Date"
                        }
                    }
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "The value of the field."
                },
                "getter": false,
                "setter": false
            },
            "type": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "DateType",
                    "resolved": "\"date\" | \"datetime\" | \"month\" | \"quarter\" | \"time\" | \"week\" | \"year\"",
                    "references": {
                        "DateType": {
                            "location": "import",
                            "path": "../date-picker/date.types",
                            "id": "src/components/date-picker/date.types.ts::DateType",
                            "referenceLocation": "DateType"
                        }
                    }
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Type of date picker."
                },
                "getter": false,
                "setter": false,
                "reflect": true,
                "attribute": "type",
                "defaultValue": "'datetime'"
            },
            "format": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Format to display the selected date in."
                },
                "getter": false,
                "setter": false,
                "reflect": true,
                "attribute": "format"
            },
            "language": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "Languages",
                    "resolved": "\"da\" | \"de\" | \"en\" | \"fi\" | \"fr\" | \"nb\" | \"nl\" | \"no\" | \"sv\"",
                    "references": {
                        "Languages": {
                            "location": "import",
                            "path": "../date-picker/date.types",
                            "id": "src/components/date-picker/date.types.ts::Languages",
                            "referenceLocation": "Languages"
                        }
                    }
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Defines the localisation for translations and date formatting.\nProperty `format` customizes the localized date format."
                },
                "getter": false,
                "setter": false,
                "reflect": true,
                "attribute": "language",
                "defaultValue": "'en'"
            },
            "formatter": {
                "type": "unknown",
                "mutable": false,
                "complexType": {
                    "original": "(date: Date) => string",
                    "resolved": "(date: Date) => string",
                    "references": {
                        "Date": {
                            "location": "global",
                            "id": "global::Date"
                        }
                    }
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "Custom formatting function. Will be used for date formatting.\n\n:::note\noverrides `format` and `language`\n:::"
                },
                "getter": false,
                "setter": false
            }
        };
    }
    static get states() {
        return {
            "internalFormat": {},
            "showPortal": {}
        };
    }
    static get events() {
        return [{
                "method": "change",
                "name": "change",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Emitted when the date picker value is changed."
                },
                "complexType": {
                    "original": "Date",
                    "resolved": "Date",
                    "references": {
                        "Date": {
                            "location": "global",
                            "id": "global::Date"
                        }
                    }
                }
            }];
    }
    static get elementRef() { return "host"; }
}
