import { __decorate, __metadata } from "tslib";
import { Host, h, } from "@stencil/core";
import { BEM } from "../../../utils/bem";
import { ListenToConfig, defaultConfig } from "../../../utils/config";
import { BalDate } from "../../../utils/date";
import { waitAfterFramePaint } from "../../../utils/helpers";
import { Logger } from "../../../utils/log";
import { ListenToSwipe } from "../../../utils/swipe";
import { generateCalendarGrid, generateMonths, generateWeekDays, generateYears, getFirstWeekdayOfMonth, } from "../utils/calendar";
import { CalendarGrid } from "./components/bal-date-calendar__gird";
import { CalendarList } from "./components/bal-date-calendar__list";
import { CalendarNav } from "./components/bal-date-calendar__nav";
export class DateCalendar {
    constructor() {
        this.oldMin = '';
        this.oldMax = '';
        this.selectedDate = '';
        this.monthFullNames = [];
        this.weekdays = [];
        this.months = [];
        this.years = [];
        this.calendarGrid = [];
        this.firstDayOfWeek = 0;
        this.month = 0;
        this.year = 0;
        this.language = defaultConfig.language;
        this.isCalendarVisible = true;
        this.isYearListVisible = false;
        this.isMonthListVisible = false;
        /**
         * Callback to determine which date in the datepicker should be selectable.
         */
        this.allowedDates = undefined;
        /**
         * EVENT BINDING
         * ------------------------------------------------------
         */
        this.onSelectDay = (isoDate) => {
            this.valueChanged(isoDate, this.selectedDate);
            this.balChange.emit(this.selectedDate);
        };
        this.onClickNextMonth = () => {
            let nextYear = this.year;
            let nextMonth = this.month + 1;
            if (nextMonth > 12) {
                nextMonth = 1;
                nextYear = nextYear + 1;
            }
            this.generateGridByMonthAndYear(nextMonth, nextYear);
        };
        this.onClickPreviousMonth = () => {
            let nextYear = this.year;
            let nextMonth = this.month - 1;
            if (nextMonth < 1) {
                nextMonth = 12;
                nextYear = nextYear - 1;
            }
            this.generateGridByMonthAndYear(nextMonth, nextYear);
        };
        this.onClickSelectMonthAndYear = async () => {
            this.updateSelections();
            if (this.isCalendarVisible === true) {
                this.isCalendarVisible = false;
                this.isYearListVisible = true;
                this.isMonthListVisible = false;
                await waitAfterFramePaint();
                if (this.yearListEl) {
                    const selectedYearEl = this.yearListEl.querySelector(`#year-${this.year}`);
                    if (selectedYearEl) {
                        const rowHeight = 26 + 8 + 4;
                        this.yearListEl.scrollTop = selectedYearEl.offsetTop - rowHeight * 2;
                    }
                    else {
                        this.yearListEl.scrollTop = 0;
                    }
                }
            }
            else {
                this.isCalendarVisible = true;
                this.isYearListVisible = false;
                this.isMonthListVisible = false;
            }
        };
        this.onClickYear = (newYear) => {
            this.isYearListVisible = false;
            this.isMonthListVisible = true;
            this.generateGridByMonthAndYear(this.month, newYear);
            this.months = generateMonths(this.language, newYear, this.selectedDate, this.min, this.max);
        };
        this.onClickMonth = (newMonth) => {
            this.isYearListVisible = false;
            this.isMonthListVisible = false;
            this.isCalendarVisible = true;
            this.generateGridByMonthAndYear(newMonth, this.year);
        };
    }
    createLogger(log) {
        this.log = log;
    }
    valueChanged(newValue, oldValue) {
        if (newValue !== oldValue) {
            const date = BalDate.fromISO(newValue);
            if (!date.isValid) {
                this.generateGridByDefaultDate();
                this.selectedDate = '';
            }
            else {
                this.generateGridByDate(date);
                this.selectedDate = date.toISODate();
            }
            this.updateSelections();
        }
    }
    rangePropChanged() {
        this.updateSelections();
        this.generateGrid();
    }
    yearRangePropChanged() {
        this.updateSelections();
    }
    /**
     * LIFECYCLE
     * ------------------------------------------------------
     */
    connectedCallback() {
        this.generateGridByDefaultDate();
        this.valueChanged(this.value, undefined);
    }
    /**
     * LISTENERS
     * ------------------------------------------------------
     */
    /**
     * @internal define config for the component
     */
    async configChanged(state) {
        this.language = state.language;
        this.monthFullNames = BalDate.infoMonths({ format: 'long', locale: this.language });
        this.weekdays = generateWeekDays(this.language);
        this.updateSelections();
    }
    swipeListener({ left, right }) {
        if (left) {
            this.onClickNextMonth();
        }
        else if (right) {
            this.onClickPreviousMonth();
        }
    }
    /**
     * GETTERS
     * ------------------------------------------------------
     */
    get minYear() {
        if (this.minYearProp === undefined) {
            const today = new Date();
            const year = today.getFullYear();
            const defaultMinYear = year - 100;
            if (this.min) {
                const date = BalDate.fromISO(this.min);
                return date.year || defaultMinYear;
            }
            else {
                return defaultMinYear;
            }
        }
        return this.minYearProp;
    }
    get maxYear() {
        if (this.maxYearProp === undefined) {
            const today = new Date();
            const year = today.getFullYear();
            const defaultMaxYear = year + 50;
            if (this.max) {
                const date = BalDate.fromISO(this.max);
                return date.year || defaultMaxYear;
            }
            else {
                return defaultMaxYear;
            }
        }
        return this.maxYearProp;
    }
    /**
     * PRIVATE METHODS
     * ------------------------------------------------------
     */
    updateSelections() {
        this.years = generateYears(this.year, this.minYear, this.maxYear);
        this.months = generateMonths(this.language, this.year, this.selectedDate, this.min, this.max);
    }
    generateGrid() {
        const date = BalDate.fromISO(this.value);
        if (date.isValid) {
            this.generateGridByDate(date);
        }
        else {
            this.generateGridByDefaultDate();
        }
    }
    generateGridByDate(date) {
        if (date.year !== undefined && date.month !== undefined) {
            this.generateGridByMonthAndYear(date.month, date.year);
        }
    }
    generateGridByDefaultDate() {
        const date = BalDate.fromISO(this.defaultDate);
        if (date.isValid) {
            this.generateGridByDate(date);
        }
        else {
            const today = new Date();
            this.month = today.getMonth() + 1;
            this.year = today.getFullYear();
            this.calendarGrid = generateCalendarGrid(this.year, this.month, this.min, this.max, this.allowedDates);
            this.firstDayOfWeek = getFirstWeekdayOfMonth(this.year, this.month);
        }
    }
    generateGridByMonthAndYear(month, year) {
        // Only generate when mont and year has changed
        if (this.month !== month || this.year !== year || this.oldMin !== this.min || this.oldMax !== this.max) {
            this.calendarGrid = generateCalendarGrid(year, month, this.min, this.max, this.allowedDates);
            this.firstDayOfWeek = getFirstWeekdayOfMonth(year, month);
        }
        this.month = month;
        this.year = year;
        this.oldMin = this.min;
        this.oldMax = this.max;
    }
    /**
     * RENDER
     * ------------------------------------------------------
     */
    render() {
        var _a;
        const girdHeight = ((_a = this.gridEl) === null || _a === void 0 ? void 0 : _a.clientHeight) || 0;
        const block = BEM.block('date-calendar');
        const blockBody = block.element('body');
        const blockFoot = block.element('foot');
        return (h(Host, { key: '34efddf3c88e5470dcefd22c5cab262873d07de8', class: Object.assign({}, block.class()) }, h(CalendarNav, { key: '9c8942a95ff9f1c8fdc8b1e153a29632145d1c2b', language: this.language, monthFullNames: this.monthFullNames, month: this.month, year: this.year, isCalendarVisible: this.isCalendarVisible, isListVisible: this.isMonthListVisible || this.isYearListVisible, onClickSelectMonthAndYear: this.onClickSelectMonthAndYear, onClickPreviousMonth: this.onClickPreviousMonth, onClickNextMonth: this.onClickNextMonth }), h("div", { key: '6a2cb7bba9c0568c1dc31d8b1c811b90c5845dce', class: Object.assign({}, blockBody.class()) }, h(CalendarGrid, { key: 'a687e1e1f80bc8ea21bdc7ad4709537829005843', isVisible: this.isCalendarVisible, grid: this.calendarGrid, weekdays: this.weekdays, firstDayOfWeek: this.firstDayOfWeek, selectedDate: this.selectedDate, ref: el => (this.gridEl = el), onSelectDay: isoDate => this.onSelectDay(isoDate) }), h(CalendarList, { key: 'cba1e282fb92f5edfa04e97eefcf9b38b57b8428', name: "year", isVisible: this.isYearListVisible, girdHeight: girdHeight, list: this.years, ref: el => (this.yearListEl = el), onSelect: item => this.onClickYear(item.value) }), h(CalendarList, { key: 'ae7cb47aaffec3d3a91d1aab96f1fbdde34b7e6f', name: "month", isVisible: this.isMonthListVisible, girdHeight: girdHeight, list: this.months, onSelect: item => this.onClickMonth(item.value) })), h("div", { key: '6e455964845a4e57abc793cb78916ed51fa0bdb3', class: Object.assign({}, blockFoot.class()) }, h("slot", { key: '277a09ef4cb10f6f051c84b1ba9f9ae79f0d3479' }))));
    }
    static get is() { return "bal-date-calendar"; }
    static get encapsulation() { return "shadow"; }
    static get originalStyleUrls() {
        return {
            "$": ["bal-date-calendar.sass"]
        };
    }
    static get styleUrls() {
        return {
            "$": ["bal-date-calendar.css"]
        };
    }
    static get properties() {
        return {
            "value": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "The value of selected date, which accepts ISO 8601 date strings (YYYY-MM-DD)."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "value"
            },
            "defaultDate": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "The date to defines where the calendar starts. The prop accepts ISO 8601 date strings (YYYY-MM-DD). Default is today."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "default-date"
            },
            "allowedDates": {
                "type": "unknown",
                "mutable": false,
                "complexType": {
                    "original": "BalProps.BalDateCalendarAllowedDatesCallback | undefined",
                    "resolved": "(isoDate: string) => boolean",
                    "references": {
                        "BalProps": {
                            "location": "global",
                            "id": "global::BalProps"
                        }
                    }
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Callback to determine which date in the datepicker should be selectable."
                },
                "getter": false,
                "setter": false,
                "defaultValue": "undefined"
            },
            "min": {
                "type": "string",
                "mutable": true,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "The minimum datetime allowed. Value must be a date string\nfollowing the\n[ISO 8601 datetime format standard](https://www.w3.org/TR/NOTE-datetime),\nsuch as `1996-12-19`. The format does not have to be specific to an exact\ndatetime. For example, the minimum could just be the year, such as `1994`.\nDefaults to the beginning of the year, 100 years ago from today."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "min"
            },
            "max": {
                "type": "string",
                "mutable": true,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "The maximum datetime allowed. Value must be a date string\nfollowing the\n[ISO 8601 datetime format standard](https://www.w3.org/TR/NOTE-datetime),\n`1996-12-19`. The format does not have to be specific to an exact\ndatetime. For example, the maximum could just be the year, such as `1994`.\nDefaults to the end of this year."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "max"
            },
            "minYearProp": {
                "type": "number",
                "mutable": false,
                "complexType": {
                    "original": "number",
                    "resolved": "number",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "Earliest year available for selection"
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "min-year"
            },
            "maxYearProp": {
                "type": "number",
                "mutable": false,
                "complexType": {
                    "original": "number",
                    "resolved": "number",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "Latest year available for selection"
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "max-year"
            }
        };
    }
    static get states() {
        return {
            "selectedDate": {},
            "monthFullNames": {},
            "weekdays": {},
            "months": {},
            "years": {},
            "calendarGrid": {},
            "firstDayOfWeek": {},
            "month": {},
            "year": {},
            "language": {},
            "isCalendarVisible": {},
            "isYearListVisible": {},
            "isMonthListVisible": {}
        };
    }
    static get events() {
        return [{
                "method": "balChange",
                "name": "balChange",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Emitted when a option got selected."
                },
                "complexType": {
                    "original": "BalEvents.BalDateCalendarChangeDetail",
                    "resolved": "string",
                    "references": {
                        "BalEvents": {
                            "location": "global",
                            "id": "global::BalEvents"
                        }
                    }
                }
            }];
    }
    static get methods() {
        return {
            "configChanged": {
                "complexType": {
                    "signature": "(state: BalConfigState) => Promise<void>",
                    "parameters": [{
                            "name": "state",
                            "type": "BalConfigState",
                            "docs": ""
                        }],
                    "references": {
                        "Promise": {
                            "location": "global",
                            "id": "global::Promise"
                        },
                        "BalConfigState": {
                            "location": "import",
                            "path": "../../../utils/config",
                            "id": "src/utils/config/index.ts::BalConfigState"
                        }
                    },
                    "return": "Promise<void>"
                },
                "docs": {
                    "text": "",
                    "tags": [{
                            "name": "internal",
                            "text": "define config for the component"
                        }]
                }
            }
        };
    }
    static get elementRef() { return "el"; }
    static get watchers() {
        return [{
                "propName": "value",
                "methodName": "valueChanged"
            }, {
                "propName": "min",
                "methodName": "rangePropChanged"
            }, {
                "propName": "max",
                "methodName": "rangePropChanged"
            }, {
                "propName": "minYearProp",
                "methodName": "yearRangePropChanged"
            }, {
                "propName": "maxYearProp",
                "methodName": "yearRangePropChanged"
            }];
    }
}
__decorate([
    Logger('bal-date-calendar'),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Function]),
    __metadata("design:returntype", void 0)
], DateCalendar.prototype, "createLogger", null);
__decorate([
    ListenToConfig(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], DateCalendar.prototype, "configChanged", null);
__decorate([
    ListenToSwipe({ mobileOnly: true }),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", void 0)
], DateCalendar.prototype, "swipeListener", null);
