import { h } from "@stencil/core";
import { getValidator } from '../../validators/validatorFactory';
import { MeridiemTimeDisplay, MilitaryTimeDisplay } from '../time-picker/time-picker.service';
import { DatePickerService } from '../date-picker/date-picker.service';
import { DatetimePickerService } from './datetime-picker.service';
import Tunnel from '../locale/i18n.provider';
let inputId = 0;
export class DatetimePicker {
    constructor() {
        this.clockListener = ({ detail }) => this.handleTimeChange(detail);
        this.calendarListener = ({ detail }) => this.handleDateChange(detail);
        this.inputBlurListener = (e) => this.handleBlur(e);
        this.overlayListener = () => this.close();
        this.toggleClickListener = () => this.open();
        /**
         * Bool to display time in meridiem or military
         */
        this.meridiem = true;
        /**
         * The date picker id
         */
        this.inputId = `anj-datetime-picker-${inputId++}`;
        /**
         * Passthrough, allows for targeted handling of nested overlays
         */
        this.nested = false;
        /**
         * How to align the overlay when it opens.
         * Options are `"bottom-end"` or `"bottom-start"` which is the default.
         */
        this.placement = 'bottom-end';
    }
    injectedPropWatcher() {
        this.assignAdapter();
    }
    meridiemHandler(value) {
        this.strategy = (value) ? new MeridiemTimeDisplay(this.locale, this.language) : new MilitaryTimeDisplay(this.locale, this.language);
    }
    async connectedCallback() {
        this.assignAdapter();
        this.input = this.element.parentElement;
        this.input.addEventListener('anjBlur', this.inputBlurListener);
        this.meridiemHandler(this.meridiem);
        this.connectToggle(this.input.querySelector('anj-datetime-picker-toggle'));
        this.configureValidationParameters();
    }
    componentDidLoad() {
        this.anjInputControlDidLoad.emit();
    }
    disconnectedCallback() {
        this.unbindCalendar();
        this.unbindClock();
        if (this.overlay) {
            this.overlay.removeEventListener('anjWillDismiss', this.overlayListener);
            this.overlay = null;
        }
        if (this.input) {
            this.input.removeEventListener('anjBlur', this.inputBlurListener);
        }
        const toggle = this.input.querySelector('anj-datetime-picker-toggle');
        if (toggle) {
            toggle.removeEventListener('click', this.toggleClickListener);
        }
    }
    /**
     * open the date picker
     */
    async open() {
        if (this.calendar)
            return;
        if (!this.overlay) {
            this.bindOverlay(this.element.querySelector('anj-overlay'));
        }
        this.bindClock();
        this.bindCalendar();
        const popper = await this.overlay.popper();
        if (!popper) {
            this.configureValidationParameters();
            this.setInputValue((this.input.value) ? this.input.value : '');
            this.calendar.monthMode = true;
            this.calendar.date = this.adapter.getCalendarDate(this.noTimeDate, this.rangeParams);
            this.calendar.rangeParams = this.rangeParams;
            this.clock.value = this.timeString;
            await this.overlay.open(this.element);
        }
        this.previouslyActiveElement = this.input.querySelector('anj-datetime-picker-toggle button');
        return this.calendar.focusControls();
    }
    /**
     * close the date picker
     */
    async close() {
        if (this.previouslyActiveElement) {
            this.previouslyActiveElement.focus();
            this.previouslyActiveElement = undefined;
        }
        if (await this.overlay.popper()) {
            this.anjDatetimeChange.emit({ inner: this.timeDate, outer: this.outerString, refreshOuter: true });
        }
        await this.overlay.close();
        this.unbindCalendar();
        this.unbindClock();
    }
    /**
     * handle changes from the angular directive
     */
    async setInputValue(inputValue) {
        if (!this.strategy) {
            this.strategy = (this.meridiem) ? new MeridiemTimeDisplay(this.locale, this.language) : new MilitaryTimeDisplay(this.locale, this.language);
        }
        const { shortDate, time } = (typeof inputValue === 'string')
            ? this.parseInputString(inputValue)
            : this.parseInputString(DatetimePickerService.localizeForOuterValue(inputValue, this.locale, this.meridiem));
        this.processTimeInput(time);
        this.timeDate = this.processDateInput(shortDate);
        this.noTimeDate = DatetimePickerService.getNoTimeDate(this.timeDate);
        this.outerString = DatetimePickerService.localizeForOuterValue(this.timeDate, this.locale, this.meridiem);
        if (inputValue) {
            this.anjDatetimeChange.emit({ inner: this.timeDate, outer: this.outerString, refreshOuter: true });
        }
        else {
            this.anjDatetimeChange.emit({ inner: null, outer: null, refreshOuter: true });
        }
    }
    /**
     * formatter, passed to anj-input, currently a pass-through
     */
    inputFormatter(inputValue) {
        return Promise.resolve(inputValue);
    }
    /**
     * comparator, passed to anj-input, which compares two datetime strings for parity
     */
    hasValueChanged(newVal, oldVal) {
        return Promise.resolve(newVal !== oldVal);
    }
    async innerValue() {
        return this.timeDate;
    }
    // VALIDATION BLOCK
    async remoteCustomValidator(valueString) {
        const valueDate = this.adapter.processDateInput(valueString);
        return this.updateCustomValidity(valueDate);
    }
    configureValidationParameters() {
        this.rangeParams = {};
        if (this.input.min) {
            this.min = this.adapter.processDateInput(this.input.min);
            this.rangeParams.minDate = this.min;
        }
        if (this.input.max) {
            this.max = this.adapter.processDateInput(this.input.max);
            this.rangeParams.maxDate = this.max;
        }
        this.setCustomValidator();
    }
    setCustomValidator() {
        this.validator = getValidator([
            { name: 'min', options: { min: this.min } },
            { name: 'max', options: { max: this.max } }
        ]);
    }
    updateCustomValidity(value) {
        let response = {};
        if (this.validator) {
            response = this.validator.validate(value);
        }
        return response;
    }
    assignAdapter() {
        this.adapter = new DatePickerService(this.locale, this.language, undefined, this.dateFormat);
    }
    // TOGGLE BLOCK
    connectToggle(toggle) {
        if (toggle) {
            toggle.addEventListener('click', this.toggleClickListener);
        }
    }
    // DATETIME BLOCK
    getDisplayMeridiem() {
        return (this.timeString) ? this.timeString.trim().split(' ')[1] : '';
    }
    getDisplayTime() {
        return (this.timeString) ? this.timeString.trim().split(' ')[0] : '';
    }
    handleBlur({ target }) {
        if (target.tagName === 'ANJ-INPUT') {
            this.setInputValue(target.value);
        }
    }
    handleDateChange(dateSelection) {
        this.noTimeDate = dateSelection;
        this.manageDateRepresentations();
    }
    handleTimeChange({ parsedValue }) {
        this.time = parsedValue;
        this.timeString = (this.time) ? ` ${this.strategy.stringifyParsedTime(this.time)}` : '';
        this.manageDateRepresentations();
    }
    manageDateRepresentations() {
        this.timeDate = DatetimePickerService.addTime(this.noTimeDate, this.time);
        this.outerString = DatetimePickerService.localizeForOuterValue(this.timeDate, this.locale, this.meridiem);
        this.input.value = this.outerString;
    }
    parseInputString(inputValue) {
        if (!inputValue) {
            inputValue = DatetimePickerService.localizeForOuterValue(new Date(), this.locale, this.meridiem);
        }
        const inputSplit = DatetimePickerService.replacer(inputValue.toString()).split(/ (.*)/).filter(Boolean);
        const shortDate = inputSplit[0];
        const time = inputSplit[1];
        return { shortDate, time };
    }
    processDateInput(shortDate) {
        const noTimeDate = this.adapter.processDateInput(shortDate);
        return DatetimePickerService.addTime(noTimeDate, this.time);
    }
    processTimeInput(time) {
        this.time = this.strategy.parseToTimeFields(new Date(`1/1/2007 ${time}`).toLocaleString(this.locale, { hour12: false, hour: '2-digit', minute: '2-digit' }));
        this.timeString = (this.time) ? ` ${this.strategy.stringifyParsedTime(this.time)}` : '';
    }
    bindOverlay(el) {
        if (el) {
            this.overlay = el;
            this.overlay.addEventListener('anjWillDismiss', this.overlayListener);
        }
    }
    bindCalendar() {
        this.calendar = document.createElement('anj-calendar');
        this.overlay.querySelector('.pickers-container').prepend(this.calendar);
        this.calendar.date = this.adapter.getCalendarDate(this.noTimeDate, this.rangeParams);
        this.calendar.adapter = this.adapter;
        this.calendar.rangeParams = this.rangeParams;
        this.calendar.addEventListener('anjCalendarDaySelect', this.calendarListener);
    }
    unbindCalendar() {
        if (this.calendar) {
            this.calendar.removeEventListener('anjCalendarDaySelect', this.calendarListener);
            this.calendar.remove();
            this.calendar = null;
        }
    }
    bindClock() {
        this.clock = document.createElement('anj-time-picker-clock');
        this.overlay.querySelector('.time-picker-container').append(this.clock);
        this.clock.context = 'vertical';
        this.clock.locale = this.locale;
        this.clock.meridiem = this.meridiem;
        this.clock.value = this.timeString;
        this.clock.addEventListener('anjTimeChanged', this.clockListener);
    }
    unbindClock() {
        if (this.clock) {
            this.clock.removeEventListener('anjTimeChanged', this.clockListener);
            this.clock.remove();
            this.clock = null;
        }
    }
    render() {
        return (h("anj-overlay", { class: "anj-datetime-picker-menu anj-dropdown-menu", placement: this.placement, nested: this.nested, trapFocus: true },
            h("div", { class: "pickers-container" },
                h("div", { class: "time-picker-container" },
                    h("div", { class: "anj-picker-header" },
                        h("p", { class: "anj-display-meridiem" }, this.getDisplayMeridiem()),
                        h("p", { class: "anj-display-time" }, this.getDisplayTime()))))));
    }
    static get is() { return "anj-datetime-picker"; }
    static get originalStyleUrls() { return {
        "$": ["datetime-picker.scss"]
    }; }
    static get styleUrls() { return {
        "$": ["datetime-picker.css"]
    }; }
    static get properties() { return {
        "locale": {
            "type": "string",
            "mutable": false,
            "complexType": {
                "original": "string",
                "resolved": "string",
                "references": {}
            },
            "required": false,
            "optional": false,
            "docs": {
                "tags": [{
                        "text": "Fed by anj-locale",
                        "name": "internal"
                    }],
                "text": ""
            },
            "attribute": "locale",
            "reflect": false
        },
        "language": {
            "type": "string",
            "mutable": false,
            "complexType": {
                "original": "string",
                "resolved": "string",
                "references": {}
            },
            "required": false,
            "optional": false,
            "docs": {
                "tags": [],
                "text": ""
            },
            "attribute": "language",
            "reflect": false
        },
        "dateFormat": {
            "type": "string",
            "mutable": false,
            "complexType": {
                "original": "DateFormat",
                "resolved": "\"M/d/yyyy\" | \"dd/MM/yyyy\" | \"yyyy/M/d\"",
                "references": {
                    "DateFormat": {
                        "location": "import",
                        "path": "../locale/i18n.provider"
                    }
                }
            },
            "required": false,
            "optional": false,
            "docs": {
                "tags": [],
                "text": ""
            },
            "attribute": "date-format",
            "reflect": false
        },
        "meridiem": {
            "type": "boolean",
            "mutable": false,
            "complexType": {
                "original": "boolean",
                "resolved": "boolean",
                "references": {}
            },
            "required": false,
            "optional": false,
            "docs": {
                "tags": [],
                "text": "Bool to display time in meridiem or military"
            },
            "attribute": "meridiem",
            "reflect": false,
            "defaultValue": "true"
        },
        "adapter": {
            "type": "any",
            "mutable": true,
            "complexType": {
                "original": "any",
                "resolved": "any",
                "references": {}
            },
            "required": false,
            "optional": false,
            "docs": {
                "tags": [],
                "text": "The dateAdapter is the brains of the datetime-picker."
            },
            "attribute": "adapter",
            "reflect": false
        },
        "inputId": {
            "type": "string",
            "mutable": false,
            "complexType": {
                "original": "string",
                "resolved": "string",
                "references": {}
            },
            "required": false,
            "optional": false,
            "docs": {
                "tags": [],
                "text": "The date picker id"
            },
            "attribute": "input-id",
            "reflect": false,
            "defaultValue": "`anj-datetime-picker-${inputId++}`"
        },
        "nested": {
            "type": "boolean",
            "mutable": false,
            "complexType": {
                "original": "boolean",
                "resolved": "boolean",
                "references": {}
            },
            "required": false,
            "optional": false,
            "docs": {
                "tags": [],
                "text": "Passthrough, allows for targeted handling of nested overlays"
            },
            "attribute": "nested",
            "reflect": false,
            "defaultValue": "false"
        },
        "placement": {
            "type": "string",
            "mutable": false,
            "complexType": {
                "original": "'bottom-start' | 'bottom-end'",
                "resolved": "\"bottom-end\" | \"bottom-start\"",
                "references": {}
            },
            "required": false,
            "optional": false,
            "docs": {
                "tags": [],
                "text": "How to align the overlay when it opens.\nOptions are `\"bottom-end\"` or `\"bottom-start\"` which is the default."
            },
            "attribute": "placement",
            "reflect": false,
            "defaultValue": "'bottom-end'"
        }
    }; }
    static get states() { return {
        "noTimeDate": {},
        "rangeParams": {},
        "time": {},
        "timeDate": {},
        "outerString": {}
    }; }
    static get events() { return [{
            "method": "anjDatetimeChange",
            "name": "anjDatetimeChange",
            "bubbles": true,
            "cancelable": true,
            "composed": true,
            "docs": {
                "tags": [],
                "text": "Fires when the user has selected a datetime"
            },
            "complexType": {
                "original": "DatetimeChangeEvent",
                "resolved": "DatetimeChangeEvent",
                "references": {
                    "DatetimeChangeEvent": {
                        "location": "import",
                        "path": "../../types/types"
                    }
                }
            }
        }, {
            "method": "anjInputControlDidLoad",
            "name": "anjInputControlDidLoad",
            "bubbles": true,
            "cancelable": true,
            "composed": true,
            "docs": {
                "tags": [],
                "text": "Fires when the control has loaded"
            },
            "complexType": {
                "original": "void",
                "resolved": "void",
                "references": {}
            }
        }]; }
    static get methods() { return {
        "open": {
            "complexType": {
                "signature": "() => Promise<void>",
                "parameters": [],
                "references": {
                    "Promise": {
                        "location": "global"
                    }
                },
                "return": "Promise<void>"
            },
            "docs": {
                "text": "open the date picker",
                "tags": []
            }
        },
        "close": {
            "complexType": {
                "signature": "() => Promise<void>",
                "parameters": [],
                "references": {
                    "Promise": {
                        "location": "global"
                    }
                },
                "return": "Promise<void>"
            },
            "docs": {
                "text": "close the date picker",
                "tags": []
            }
        },
        "setInputValue": {
            "complexType": {
                "signature": "(inputValue: any) => Promise<void>",
                "parameters": [{
                        "tags": [],
                        "text": ""
                    }],
                "references": {
                    "Promise": {
                        "location": "global"
                    }
                },
                "return": "Promise<void>"
            },
            "docs": {
                "text": "handle changes from the angular directive",
                "tags": []
            }
        },
        "inputFormatter": {
            "complexType": {
                "signature": "(inputValue: string) => Promise<string>",
                "parameters": [{
                        "tags": [],
                        "text": ""
                    }],
                "references": {
                    "Promise": {
                        "location": "global"
                    }
                },
                "return": "Promise<string>"
            },
            "docs": {
                "text": "formatter, passed to anj-input, currently a pass-through",
                "tags": []
            }
        },
        "hasValueChanged": {
            "complexType": {
                "signature": "(newVal: any, oldVal: any) => Promise<boolean>",
                "parameters": [{
                        "tags": [],
                        "text": ""
                    }, {
                        "tags": [],
                        "text": ""
                    }],
                "references": {
                    "Promise": {
                        "location": "global"
                    }
                },
                "return": "Promise<boolean>"
            },
            "docs": {
                "text": "comparator, passed to anj-input, which compares two datetime strings for parity",
                "tags": []
            }
        },
        "innerValue": {
            "complexType": {
                "signature": "() => Promise<any>",
                "parameters": [],
                "references": {
                    "Promise": {
                        "location": "global"
                    }
                },
                "return": "Promise<any>"
            },
            "docs": {
                "text": "",
                "tags": []
            }
        },
        "remoteCustomValidator": {
            "complexType": {
                "signature": "(valueString: string | number) => Promise<ValidatorResponse>",
                "parameters": [{
                        "tags": [],
                        "text": ""
                    }],
                "references": {
                    "Promise": {
                        "location": "global"
                    },
                    "ValidatorResponse": {
                        "location": "import",
                        "path": "../../types/types"
                    }
                },
                "return": "Promise<ValidatorResponse>"
            },
            "docs": {
                "text": "",
                "tags": []
            }
        }
    }; }
    static get elementRef() { return "element"; }
    static get watchers() { return [{
            "propName": "locale",
            "methodName": "injectedPropWatcher"
        }, {
            "propName": "language",
            "methodName": "injectedPropWatcher"
        }, {
            "propName": "dateFormat",
            "methodName": "injectedPropWatcher"
        }, {
            "propName": "meridiem",
            "methodName": "meridiemHandler"
        }]; }
}
Tunnel.injectProps(DatetimePicker, ['locale', 'language', 'dateFormat']);
