// #region Imports
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
import { Emit } from '../../../../Events/Decorators/EmitDecorator';
import { HTMLElementEventEmitter } from '@breadstone/mosaik-elements';
import { IntlController } from '../../../../Intl/IntlController';
import { Appearanceable } from '../../../Behaviors/Appearanceable';
import { Disableable } from '../../../Behaviors/Disableable';
import { Fitable } from '../../../Behaviors/Fitable';
import { Invalidable } from '../../../Behaviors/Invalidable';
import { Localeable } from '../../../Behaviors/Localeable';
import { Orientable } from '../../../Behaviors/Orientable';
import { Valueable } from '../../../Behaviors/Valueable';
import { Variantable } from '../../../Behaviors/Variantable';
import { Attribute } from '../../../Decorators/AttributeDecorator';
import { Component } from '../../../Decorators/ComponentDecorator';
import { Property } from '../../../Decorators/PropertyDecorator';
import { Watch } from '../../../Decorators/WatchDecorator';
import { CalendarView } from '../../../Types/CalendarView';
import { DayOfWeek } from '../../../Types/DayOfWeek';
import { RangeSelectionMode } from '../../../Types/RangeSelectionMode';
import { SelectionMode } from '../../../Types/SelectionMode';
import { CustomElement } from '../../Abstracts/CustomElement';
import { ButtonElement } from '../../Buttons/Button/ButtonElement';
import { ButtonGroupElement } from '../../Buttons/Button/ButtonGroupElement';
import { TextElement } from '../../Primitives/Text/TextElement';
import { calendarElementTemplate } from './CalendarElementTemplate';
import { CalendarHeaderElement } from './CalendarHeaderElement';
import { CalendarItemElement } from './CalendarItemElement';
import { CalendarSubHeaderElement } from './CalendarSubHeaderElement';
import { CalendarElementIntl } from './Intl/CalendarElementIntl';
import { getDateOnly } from './Utils';
import { CalendarDaysViewElement } from './Views/CalendarDaysViewElement';
import { CalendarMonthsViewElement } from './Views/CalendarMonthsViewElement';
import { CalendarYearsViewElement } from './Views/CalendarYearsViewElement';
import { calendarElementJoyStyle } from './Themes/CalendarElement.Joy';
import { calendarElementRetroStyle } from './Themes/CalendarElement.Retro';
import { calendarElementCosmopolitanStyle } from './Themes/CalendarElement.Cosmopolitan';
import { Themeable } from '../../../Behaviors/Themeable';
// #endregion
/**
 * Calendar - A user interface component for displaying dates and events in a chronological order.
 *
 * @element mosaik-calendar
 *
 * @description
 * The Calendar component provides a visual representation of dates and events in a structured grid layout,
 * typically organized by months and days. It allows users to view and interact with events, appointments,
 * and other time-based information. Calendar components often support features like navigation between months,
 * week or day views, event creation, and event details.
 *
 * @category User Interface
 *
 * @slot footer - The footer slot.
 *
 * @csspart header - The header part.
 * @csspart content - The content part.
 * @csspart navigation - The navigation part.
 * @csspart previous - The navigation previous button part.
 * @csspart next - The navigation next button part.
 * @csspart today - The navigation today button part.
 * @csspart yearsView - The years view part.
 * @csspart monthView - The month view part.
 * @csspart dayView - The day view part.
 *
 * @fires dateChanged - Fired when the date has changed.
 *
 * @public
 */
let CalendarElement = class CalendarElement extends Themeable(Localeable(Appearanceable(Disableable(Invalidable(Valueable(Orientable(Variantable(Fitable(CustomElement))))))))) {
    // #region Fields
    _dateChanged;
    _intl;
    _displayDate;
    _displayDateStart;
    _displayDateEnd;
    _header;
    _value;
    _values;
    _view;
    _selectionMode;
    _blackoutDates;
    _specialDates;
    _minDate;
    _maxDate;
    _firstDayOfWeek;
    _isTodayHighlighted;
    _isWeekendHighlighted;
    _showWeekNumbers;
    _markerHandler;
    _showAdjacent;
    // #endregion
    // #region Ctor
    /**
     * @public
     */
    constructor() {
        super();
        this._intl = new IntlController(this, {
            factory: () => new CalendarElementIntl(this.locale)
        });
        this._displayDate = null;
        this._displayDateStart = null;
        this._displayDateEnd = null;
        this._header = '';
        this._value = null;
        this._values = new Array();
        this._view = CalendarView.Day;
        this._selectionMode = RangeSelectionMode.Single;
        this._blackoutDates = new Array();
        this._specialDates = new Array();
        this._minDate = null;
        this._maxDate = null;
        this._firstDayOfWeek = DayOfWeek.Sunday;
        this._isTodayHighlighted = false;
        this._isWeekendHighlighted = false;
        this._showWeekNumbers = false;
        this._markerHandler = (_item) => [];
        this._showAdjacent = false;
        this._dateChanged = new HTMLElementEventEmitter(this, 'dateChanged');
    }
    // #endregion
    // #region Properties
    /**
     * Returns the `{@link is}` property.
     * The `{@link is}` property represents natural name of this element.
     *
     * @public
     * @static
     * @readonly
     */
    static get is() {
        return 'mosaik-calendar';
    }
    /**
     * Returns the `displayDate` property.
     * The `displayDate` property represents the initial date to display in the calendar.
     *
     * @public
     * @attr
     */
    get displayDate() {
        return this._displayDate;
    }
    set displayDate(value) {
        if (this._displayDate !== value) {
            this._displayDate = value;
            this.requestUpdate('displayDate');
        }
    }
    /**
     * Returns the `displayDateStart` property.
     * The `displayDateStart` property represents the start date to display in the calendar.
     *
     * @public
     * @attr
     */
    get displayDateStart() {
        return this._displayDateStart;
    }
    set displayDateStart(value) {
        if (this._displayDateStart !== value) {
            this._displayDateStart = value;
            this.requestUpdate('displayDateStart');
        }
    }
    /**
     * Returns the `displayDateEnd` property.
     * The `displayDateEnd` property represents the end date to display in the calendar.
     *
     * @public
     * @attr
     */
    get displayDateEnd() {
        return this._displayDateEnd;
    }
    set displayDateEnd(value) {
        if (this._displayDateEnd !== value) {
            this._displayDateEnd = value;
            this.requestUpdate('displayDateEnd');
        }
    }
    /**
     * Gets or sets the `header` property.
     *
     * @public
     * @attr
     */
    get header() {
        return this._header;
    }
    set header(value) {
        if (this._header !== value) {
            this._header = value;
            this.requestUpdate('header');
        }
    }
    /**
     * Gets or sets the `value` property.
     *
     * @public
     * @override
     * @attr
     */
    get value() {
        return this._value;
    }
    set value(value) {
        if (this._value !== value) {
            this._value = value;
            this.requestUpdate('value');
        }
    }
    /**
     * Gets or sets the `values` property.
     *
     * @public
     * @attr
     */
    get values() {
        return this._values;
    }
    set values(value) {
        if (this._values !== value) {
            this._values = value;
            this.requestUpdate('values');
        }
    }
    /**
     * Gets or sets the `view` property.
     *
     * @public
     * @attr
     */
    get view() {
        return this._view;
    }
    set view(value) {
        if (this._view !== value) {
            this._view = value;
            this.requestUpdate('view');
        }
    }
    /**
     * Gets or sets the `selectionMode` property.
     *
     * @public
     * @attr
     */
    get selectionMode() {
        return this._selectionMode;
    }
    set selectionMode(value) {
        if (this._selectionMode !== value) {
            this._selectionMode = value;
            this.requestUpdate('selectionMode');
        }
    }
    /**
     * Gets or sets the `blackoutDates` property.
     *
     * @public
     * @attr
     */
    get blackoutDates() {
        return this._blackoutDates;
    }
    set blackoutDates(value) {
        if (this._blackoutDates !== value) {
            this._blackoutDates = value;
            this.requestUpdate('blackoutDates');
        }
    }
    /**
     * Gets or sets the `specialDates` property.
     *
     * @public
     * @attr
     */
    get specialDates() {
        return this._specialDates;
    }
    set specialDates(value) {
        if (this._specialDates !== value) {
            this._specialDates = value;
            this.requestUpdate('specialDates');
        }
    }
    /**
     * Gets or sets the `minDate` property.
     *
     * @public
     * @attr
     */
    get minDate() {
        return this._minDate;
    }
    set minDate(value) {
        if (this._minDate !== value) {
            this._minDate = value;
            this.requestUpdate('minDate');
        }
    }
    /**
     * Gets or sets the `maxDate` property.
     *
     * @public
     * @attr
     */
    get maxDate() {
        return this._maxDate;
    }
    set maxDate(value) {
        if (this._maxDate !== value) {
            this._maxDate = value;
            this.requestUpdate('maxDate');
        }
    }
    /**
     * Gets or sets the `firstDayOfWeek` property.
     *
     * @public
     * @attr
     */
    get firstDayOfWeek() {
        return this._firstDayOfWeek;
    }
    set firstDayOfWeek(value) {
        if (this._firstDayOfWeek !== value) {
            this._firstDayOfWeek = value;
            this.requestUpdate('firstDayOfWeek');
        }
    }
    /**
     * Gets or sets the `isTodayHighlighted` property.
     *
     * @public
     * @attr
     */
    get isTodayHighlighted() {
        return this._isTodayHighlighted;
    }
    set isTodayHighlighted(value) {
        if (this._isTodayHighlighted !== value) {
            this._isTodayHighlighted = value;
            this.requestUpdate('isTodayHighlighted');
        }
    }
    /**
     * Gets or sets the `isWeekendHighlighted` property.
     *
     * @public
     * @attr
     */
    get isWeekendHighlighted() {
        return this._isWeekendHighlighted;
    }
    set isWeekendHighlighted(value) {
        if (this._isWeekendHighlighted !== value) {
            this._isWeekendHighlighted = value;
            this.requestUpdate('isWeekendHighlighted');
        }
    }
    /**
     * Gets or sets the `showWeekNumbers` property.
     *
     * @public
     * @attr
     */
    get showWeekNumbers() {
        return this._showWeekNumbers;
    }
    set showWeekNumbers(value) {
        if (this._showWeekNumbers !== value) {
            this._showWeekNumbers = value;
            this.requestUpdate('showWeekNumbers');
        }
    }
    /**
     * A handler that gets date and returns null or a tuple with circled marker data. See {@link CalendarMarkerData}.
     *
     * @public
     * @attr
     */
    get markerHandler() {
        return this._markerHandler;
    }
    set markerHandler(value) {
        if (this._markerHandler !== value) {
            this._markerHandler = value;
            this.requestUpdate('markerHandler');
        }
    }
    /**
     * Gets or sets the `showAdjacent` property.
     *
     * @public
     * @attr
     */
    get showAdjacent() {
        return this._showAdjacent;
    }
    set showAdjacent(value) {
        if (this._showAdjacent !== value) {
            this._showAdjacent = value;
            this.requestUpdate('showAdjacent');
        }
    }
    /**
     * Returns the `intl` property.
     *
     * @public
     * @readonly
     */
    get intl() {
        return this._intl.intl;
    }
    /**
     * Called when the date is changed.
     * Provides reference to `{@link IEventDetail}` as event detail.
     *
     * @public
     * @readonly
     * @eventProperty
     */
    get dateChanged() {
        return this._dateChanged;
    }
    // #endregion
    // #region Methods
    /**
     * Resets the value, all kinds of validation and errors.
     *
     * @public
     */
    reset() {
        this.value = null;
        this.invalid = false;
    }
    /**
     * Checks the validity of the element and returns `true` if it is valid; otherwise, `false`.
     *
     * @public
     * @override
     */
    checkValidity() {
        // TODO: Implement validation
        // Is required is necessary?
        // Or check if the value in null?
        return true;
    }
    /**
     * @public
     */
    navigatePrevious() {
        if (this.displayDate) {
            const clone = new Date(this.displayDate);
            if (this.view === CalendarView.Year) {
                const previous = clone.getFullYear() - 13;
                clone.setFullYear(previous);
            }
            else if (this.view === CalendarView.Month) {
                const previous = clone.getFullYear() - 1;
                clone.setFullYear(previous);
            }
            else if (this.view === CalendarView.Day) {
                const previous = clone.getMonth() - 1;
                clone.setMonth(previous);
            }
            this.displayDate = clone;
        }
    }
    /**
     * @public
     */
    navigateNext() {
        if (this.displayDate) {
            const clone = new Date(this.displayDate);
            if (this.view === CalendarView.Year) {
                const next = clone.getFullYear() + 13;
                clone.setFullYear(next);
            }
            else if (this.view === CalendarView.Month) {
                const next = clone.getFullYear() + 1;
                clone.setFullYear(next);
            }
            else if (this.view === CalendarView.Day) {
                const next = clone.getMonth() + 1;
                clone.setMonth(next);
            }
            this.displayDate = clone;
        }
    }
    /**
     * @public
     */
    navigateToday() {
        this.view = CalendarView.Day;
        this.displayDate = getDateOnly(new Date());
    }
    /**
     * @protected
     */
    onLocalePropertyChanged(prev, next) {
        this._intl.setLocale(next);
    }
    onValuePropertyChanged(prev, next) {
        if (next) {
            // Check if value in values
            if (this.values.length > 0 && !this.values.some((v) => v.getTime() === next.getTime())) {
                this.values.push(next);
            }
        }
    }
    /**
     * @protected
     * @template
     */
    onValueChanged(detail) {
        // Switch (detail.view) {
        //     Case 'years':
        //         This.view = CalendarView.Month;
        //         Break;
        //     Case 'months':
        //         This.view = CalendarView.Day;
        //         Break;
        //     Case 'days':
        //         Break;
        // }
        this.value = detail.value?.date ?? null;
        this.values = detail.values?.map((x) => x.date) ?? [];
        this.onDateChanged(detail);
    }
    /**
     * @protected
     * @template
     */
    onViewChanged(detail) {
        this.view = detail.view;
    }
    /**
     * Emits the {@link dateChanged} event.
     *
     * @protected
     */
    onDateChanged(args) {
        this._dateChanged.emit(args);
    }
};
__decorate([
    Attribute({
        type: Date,
        converter: {
            fromAttribute: (value) => value ? new Date(value) : new Date(),
            toAttribute: (value) => value ? value.toISOString() : ''
        }
    }),
    __metadata("design:type", Object),
    __metadata("design:paramtypes", [Object])
], CalendarElement.prototype, "displayDate", null);
__decorate([
    Attribute({
        type: Date,
        converter: {
            fromAttribute: (value) => value ? new Date(value) : new Date(),
            toAttribute: (value) => value ? value.toISOString() : ''
        }
    }),
    __metadata("design:type", Object),
    __metadata("design:paramtypes", [Object])
], CalendarElement.prototype, "displayDateStart", null);
__decorate([
    Attribute({
        type: Date,
        converter: {
            fromAttribute: (value) => value ? new Date(value) : new Date(),
            toAttribute: (value) => value ? value.toISOString() : ''
        }
    }),
    __metadata("design:type", Object),
    __metadata("design:paramtypes", [Object])
], CalendarElement.prototype, "displayDateEnd", null);
__decorate([
    Attribute({ type: String }),
    __metadata("design:type", String),
    __metadata("design:paramtypes", [String])
], CalendarElement.prototype, "header", null);
__decorate([
    Attribute({
        type: Date,
        converter: {
            fromAttribute: (value) => value ? new Date(value) : undefined,
            toAttribute: (value) => value?.toISOString()
        }
    }),
    __metadata("design:type", Object),
    __metadata("design:paramtypes", [Object])
], CalendarElement.prototype, "value", null);
__decorate([
    Attribute({
        type: (Array),
        converter: {
            fromAttribute: (value) => !value
                ? undefined
                : value
                    .split(' ')
                    .map((v) => v.trim())
                    .filter((v) => v)
                    .map((v) => new Date(v)),
            toAttribute: (value) => value.map((v) => v.toISOString()).join(' ')
        }
    }),
    __metadata("design:type", Array),
    __metadata("design:paramtypes", [Array])
], CalendarElement.prototype, "values", null);
__decorate([
    Attribute({ type: CalendarView }),
    __metadata("design:type", String),
    __metadata("design:paramtypes", [String])
], CalendarElement.prototype, "view", null);
__decorate([
    Attribute({ type: SelectionMode }),
    __metadata("design:type", String),
    __metadata("design:paramtypes", [String])
], CalendarElement.prototype, "selectionMode", null);
__decorate([
    Attribute({ type: (Array) }),
    __metadata("design:type", Array),
    __metadata("design:paramtypes", [Array])
], CalendarElement.prototype, "blackoutDates", null);
__decorate([
    Attribute({ type: (Array) }),
    __metadata("design:type", Array),
    __metadata("design:paramtypes", [Array])
], CalendarElement.prototype, "specialDates", null);
__decorate([
    Attribute({
        type: Date,
        converter: {
            fromAttribute: (value) => value ? new Date(value) : new Date(),
            toAttribute: (value) => value ? value.toISOString() : ''
        }
    }),
    __metadata("design:type", Object),
    __metadata("design:paramtypes", [Object])
], CalendarElement.prototype, "minDate", null);
__decorate([
    Attribute({
        type: Date,
        converter: {
            fromAttribute: (value) => value ? new Date(value) : new Date(),
            toAttribute: (value) => value ? value.toISOString() : ''
        }
    }),
    __metadata("design:type", Object),
    __metadata("design:paramtypes", [Object])
], CalendarElement.prototype, "maxDate", null);
__decorate([
    Attribute({ type: DayOfWeek }),
    __metadata("design:type", Number),
    __metadata("design:paramtypes", [Number])
], CalendarElement.prototype, "firstDayOfWeek", null);
__decorate([
    Attribute({
        type: Boolean,
        useDefault: true
    }),
    __metadata("design:type", Boolean),
    __metadata("design:paramtypes", [Boolean])
], CalendarElement.prototype, "isTodayHighlighted", null);
__decorate([
    Attribute({
        type: Boolean,
        useDefault: true
    }),
    __metadata("design:type", Boolean),
    __metadata("design:paramtypes", [Boolean])
], CalendarElement.prototype, "isWeekendHighlighted", null);
__decorate([
    Attribute({
        type: Boolean,
        useDefault: true
    }),
    __metadata("design:type", Boolean),
    __metadata("design:paramtypes", [Boolean])
], CalendarElement.prototype, "showWeekNumbers", null);
__decorate([
    Property({ type: Function }),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Function])
], CalendarElement.prototype, "markerHandler", null);
__decorate([
    Attribute({
        type: Boolean,
        useDefault: true
    }),
    __metadata("design:type", Boolean),
    __metadata("design:paramtypes", [Boolean])
], CalendarElement.prototype, "showAdjacent", null);
__decorate([
    Emit({ eventName: 'dateChanged' }),
    __metadata("design:type", Object),
    __metadata("design:paramtypes", [])
], CalendarElement.prototype, "dateChanged", null);
__decorate([
    Watch('locale'),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, String]),
    __metadata("design:returntype", void 0)
], CalendarElement.prototype, "onLocalePropertyChanged", null);
__decorate([
    Watch('value'),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Date, Date]),
    __metadata("design:returntype", void 0)
], CalendarElement.prototype, "onValuePropertyChanged", null);
CalendarElement = __decorate([
    Component({
        selector: 'mosaik-calendar',
        template: calendarElementTemplate,
        themes: {
            joy: calendarElementJoyStyle,
            retro: calendarElementRetroStyle,
            cosmopolitan: calendarElementCosmopolitanStyle
        },
        imports: [
            CalendarHeaderElement,
            CalendarSubHeaderElement,
            CalendarItemElement,
            CalendarYearsViewElement,
            CalendarMonthsViewElement,
            CalendarDaysViewElement,
            ButtonElement,
            ButtonGroupElement,
            TextElement
        ]
    }),
    __metadata("design:paramtypes", [])
], CalendarElement);
export { CalendarElement };
//# sourceMappingURL=CalendarElement.js.map