// #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 { HTMLElementEventEmitter } from '@breadstone/mosaik-elements';
import { Emit } from '../../../../../events/Decorators/EmitDecorator';
import { getDateOnly, isDateEqual, isDateInDates } from '../../../../../Extensions/DateExtensions';
import { Appearanceable } from '../../../../Behaviors/Appearanceable';
import { Themeable } from '../../../../Behaviors/Themeable';
import { Variantable } from '../../../../Behaviors/Variantable';
import { Attribute } from '../../../../Decorators/AttributeDecorator';
import { Component } from '../../../../Decorators/ComponentDecorator';
import { Property } from '../../../../Decorators/PropertyDecorator';
import { DayOfWeek } from '../../../../Types/DayOfWeek';
import { RangeSelectionMode } from '../../../../Types/RangeSelectionMode';
import { ButtonElement } from '../../../Buttons/Button/ButtonElement';
import { ButtonGroupElement } from '../../../Buttons/Button/ButtonGroupElement';
import { TextElement } from '../../../Primitives/Text/TextElement';
import { CalendarItemElement } from '../CalendarItemElement';
import { CalendarViewBaseElement } from './Abstracts/CalendarViewBase';
import { calendarDaysViewElementTemplate } from './CalendarDaysViewElementTemplate';
import { calendarDaysViewElementCosmopolitanStyle } from './Themes/CalendarDaysViewElement.Cosmopolitan';
import { calendarDaysViewElementJoyStyle } from './Themes/CalendarDaysViewElement.Joy';
import { calendarDaysViewElementMemphisStyle } from './Themes/CalendarDaysViewElement.Memphis';
// #endregion
/**
 * Calendar Days View - A component that displays a view of days within a month, allowing users to select a specific day.
 *
 * @description
 * The Calendar Days View component shows a grid of days within a given month. Users can navigate through the days and
 * select a specific day for date selection or scheduling purposes. This view is commonly used within a date picker or
 * calendar component to provide a detailed daily view within a selected month.
 *
 * @name Calendar Days View
 * @element mosaik-calendar-days-view
 * @category Calendar
 *
 * @slot header - The header slot.
 *
 * @csspart dayLabel - The day label part.
 * @csspart dayView - The day view part.
 * @csspart heading - The heading part.
 * @csspart item - The item part.
 * @csspart labels - The labels part.
 * @csspart month - The month part.
 * @csspart navigation - The navigation part.
 * @csspart weekNumber - The week number part.
 * @csspart weekNumberLabel - The week number label part.
 * @csspart year - The year part.
 *
 * @cssprop {String} --calendar-days-view-font-family - The days view font family CSS custom property.
 * @cssprop {String} --calendar-days-view-font-letter-spacing - The days view font letter spacing CSS custom property.
 * @cssprop {String} --calendar-days-view-font-line-height - The days view font line height CSS custom property.
 * @cssprop {String} --calendar-days-view-font-size - The days view font size CSS custom property.
 * @cssprop {String} --calendar-days-view-font-text-decoration - The days view font text decoration CSS custom property.
 * @cssprop {String} --calendar-days-view-font-text-transform - The days view font text transform CSS custom property.
 * @cssprop {String} --calendar-days-view-font-weight - The days view font weight CSS custom property.
 * @cssprop {String} --calendar-days-view-gap
 * @cssprop {String} --calendar-days-view-padding-bottom
 * @cssprop {String} --calendar-days-view-padding-left
 * @cssprop {String} --calendar-days-view-padding-right
 * @cssprop {String} --calendar-days-view-padding-top
 * @cssprop {String} --calendar-days-view-shadow - The days view shadow CSS custom property.
 * @cssprop {String} --calendar-days-view-shadow-blur - The days view shadow blur CSS custom property.
 * @cssprop {String} --calendar-days-view-shadow-color - The days view shadow color CSS custom property.
 * @cssprop {String} --calendar-days-view-shadow-offset-x - The days view shadow offset x CSS custom property.
 * @cssprop {String} --calendar-days-view-shadow-offset-y - The days view shadow offset y CSS custom property.
 * @cssprop {String} --calendar-days-view-shadow-spread - The days view shadow spread CSS custom property.
 * @cssprop {String} --calendar-days-view-transition-duration - The days view transition duration CSS custom property.
 * @cssprop {String} --calendar-days-view-transition-mode - The days view transition mode CSS custom property.
 * @cssprop {String} --calendar-days-view-transition-property - The days view transition property CSS custom property.
 * @cssprop {String} --calendar-days-view-translate - The days view translate CSS custom property.
 *
 * @dependency mosaik-button - The Button element.
 * @dependency mosaik-button-group - The Button Group element.
 * @dependency mosaik-calendar-item - The Calendar Item element.
 * @dependency mosaik-text - The Text element.
 *
 * @example
 * Basic calendar days view:
 * ```html
 * <mosaik-calendar-days-view></mosaik-calendar-days-view>
 * ```
 *
 * @example
 * Calendar days view with today and weekend highlighting:
 * ```html
 * <mosaik-calendar-days-view
 *   is-today-highlighted
 *   is-weekend-highlighted
 *   show-week-numbers>
 * </mosaik-calendar-days-view>
 * ```
 *
 * @public
 */
let CalendarDaysViewElement = class CalendarDaysViewElement extends Themeable(Appearanceable(Variantable(CalendarViewBaseElement))) {
    // #region Fields
    _previewDateChange;
    _showAdjacent;
    _showWeekNumbers;
    _firstDayOfWeek;
    _isTodayHighlighted;
    _isWeekendHighlighted;
    _rangePreviewDate;
    // #endregion
    // #region Ctor
    /**
     * @public
     */
    constructor() {
        super();
        this._showWeekNumbers = false;
        this._firstDayOfWeek = DayOfWeek.Sunday;
        this._showAdjacent = false;
        this._isTodayHighlighted = true;
        this._isWeekendHighlighted = true;
        this._rangePreviewDate = null;
        this._previewDateChange = new HTMLElementEventEmitter(this, 'previewDateChange');
    }
    // #endregion
    // #region Properties
    /**
     * Returns the `is` property.
     * The `is` property represents natural name of this element.
     *
     * @public
     * @static
     * @readonly
     */
    static get is() {
        return 'mosaik-calendar-days-view';
    }
    /**
     * 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');
        }
    }
    /**
     * 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');
        }
    }
    /**
     * 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 `rangePreviewDate` property.
     *
     * @public
     */
    get rangePreviewDate() {
        return this._rangePreviewDate;
    }
    set rangePreviewDate(value) {
        if (this._rangePreviewDate !== value) {
            this._rangePreviewDate = value;
            this.requestUpdate('rangePreviewDate');
        }
    }
    /**
     * Called when the rage preview date is changed.
     * Provides reference to `IEventDetail` as event detail.
     *
     * @public
     * @readonly
     * @eventProperty
     */
    get previewDateChange() {
        return this._previewDateChange;
    }
    isSelected(day) {
        if (this.value && this.value.getTime() === day.getTime()) {
            return true;
        }
        else if (this.values) {
            return this.values.some((x) => x.getTime() === day.getTime());
        }
        return false;
    }
    // #endregion
    // #region Methods
    /**
     * @protected
     * @template
     */
    selectAdjacentDay(direction, day) {
        const dateOnly = getDateOnly(day);
        this.selectDay(dateOnly);
        // handle the previous or next direction
        if (direction === 'previous') {
            // TODO: Implement the previous day selection
        }
        else {
            // TODO: Implement the next day selection
        }
    }
    /**
     * @protected
     * @template
     */
    selectDay(day) {
        const dateOnly = getDateOnly(day);
        if (this._rangePreviewDate) {
            this.setRangePreviewDate(null);
        }
        // Check if the date is in blackout dates
        if (isDateInDates(dateOnly, this.blackoutDates)) {
            return; // Don't select blackout dates
        }
        // Check if the selection mode is none
        if (this.selection === RangeSelectionMode.None) {
            return; // Don't select if the selection mode is none
        }
        // Check if the selection mode is single
        if (this.selection === RangeSelectionMode.Single) {
            this.value = dateOnly;
            this.values = [dateOnly];
        }
        // Check if the selection mode is multiple
        if (this.selection === RangeSelectionMode.Multiple) {
            // const index = this.values?.findIndex(d => isDateEqual(d, dateOnly));
            // if (index && index > -1) {
            //     this.values?.splice(index, 1);
            // } else {
            //     this.values?.push(dateOnly);
            // }
            // ///////
            let selectedDates = this.values ?? new Array();
            const newSelection = [];
            if (selectedDates.every((date) => date.getTime() !== dateOnly.getTime())) {
                newSelection.push(dateOnly);
            }
            else {
                selectedDates = selectedDates.filter((date) => date.getTime() !== dateOnly.getTime());
            }
            if (newSelection.length > 0) {
                selectedDates = selectedDates.concat(newSelection);
            }
            // selectedDates = selectedDates.filter((d) => !this.isDisabled(d));
            selectedDates.sort((a, b) => a.valueOf() - b.valueOf());
            this.values = [...selectedDates];
        }
        // Check if the selection mode is range
        if (this.selection === RangeSelectionMode.Range) {
            let selectedDates = this.values ?? new Array();
            if (selectedDates.length === 0) {
                // Start new range with first selection
                selectedDates = [dateOnly];
            }
            else if (selectedDates.length === 1) {
                // Second selection - complete the range if different from first
                if (selectedDates[0].getTime() === dateOnly.getTime()) {
                    // Same date clicked - clear selection
                    selectedDates = [];
                }
                else {
                    // Create continuous range between start and end
                    selectedDates.push(dateOnly);
                    selectedDates.sort((a, b) => a.valueOf() - b.valueOf());
                    const start = selectedDates[0];
                    const end = selectedDates[1];
                    // Generate all dates in between
                    const rangeArray = [start];
                    const currentDate = new Date(start);
                    while (currentDate.getTime() < end.getTime()) {
                        currentDate.setDate(currentDate.getDate() + 1);
                        rangeArray.push(new Date(currentDate));
                    }
                    selectedDates = rangeArray;
                }
            }
            else {
                // Range already selected - start new range
                selectedDates = [dateOnly];
            }
            // Filter out blackout dates from range
            selectedDates = selectedDates.filter((date) => !isDateInDates(date, this.blackoutDates));
            this.values = [...selectedDates];
        }
        this.onValueChanged({
            value: {
                date: dateOnly,
                isAdjacent: false, // TODO: Implement
                isBlackout: isDateInDates(dateOnly, this.blackoutDates),
                isSpecial: isDateInDates(dateOnly, this.specialDates),
                isToday: isDateEqual(dateOnly, new Date()),
                isWeekend: dateOnly.getDay() === 0 || dateOnly.getDay() === 6
            },
            values: this.values?.map((date) => ({
                date: date,
                isAdjacent: false, // TODO: Implement
                isBlackout: isDateInDates(date, this.blackoutDates),
                isSpecial: isDateInDates(date, this.specialDates),
                isToday: isDateEqual(date, new Date()),
                isWeekend: date.getDay() === 0 || date.getDay() === 6
            }))
        });
    }
    /**
     * @protected
     * @template
     */
    setView(view) {
        this.onViewChanged({ view: view });
    }
    /**
     * @protected
     * @template
     */
    changeRangePreview(date) {
        if (this.selection === RangeSelectionMode.Range && this.values && this.values.length === 1 && !isDateEqual(this.values[0], date)) {
            this.setRangePreviewDate(date);
        }
    }
    /**
     * @protected
     * @template
     */
    clearRangePreview() {
        if (this.rangePreviewDate) {
            this.setRangePreviewDate(null);
        }
    }
    /**
     * Emits the `previewDateChange` event.
     *
     * @protected
     */
    onPreviewDateChange(args) {
        this._previewDateChange.emit(args);
    }
    /**
     * @private
     */
    setRangePreviewDate(value) {
        this.rangePreviewDate = value;
        this.onPreviewDateChange(value);
    }
    /**
     * @private
     */
    generateDateRange(start, end) {
        const result = [];
        start = getDateOnly(start);
        end = getDateOnly(end);
        while (start.getTime() < end.getTime()) {
            start.setDate(start.getDate() + 1);
            result.push(start);
        }
        return result;
    }
};
__decorate([
    Attribute({
        type: Boolean,
        useDefault: true
    }),
    __metadata("design:type", Boolean),
    __metadata("design:paramtypes", [Boolean])
], CalendarDaysViewElement.prototype, "showAdjacent", null);
__decorate([
    Attribute({
        type: Boolean,
        useDefault: true
    }),
    __metadata("design:type", Boolean),
    __metadata("design:paramtypes", [Boolean])
], CalendarDaysViewElement.prototype, "showWeekNumbers", null);
__decorate([
    Attribute({ type: DayOfWeek }),
    __metadata("design:type", Number),
    __metadata("design:paramtypes", [Number])
], CalendarDaysViewElement.prototype, "firstDayOfWeek", null);
__decorate([
    Attribute({
        type: Boolean,
        useDefault: true
    }),
    __metadata("design:type", Boolean),
    __metadata("design:paramtypes", [Boolean])
], CalendarDaysViewElement.prototype, "isTodayHighlighted", null);
__decorate([
    Attribute({
        type: Boolean,
        useDefault: true
    }),
    __metadata("design:type", Boolean),
    __metadata("design:paramtypes", [Boolean])
], CalendarDaysViewElement.prototype, "isWeekendHighlighted", null);
__decorate([
    Property({ type: Date }),
    __metadata("design:type", Object),
    __metadata("design:paramtypes", [Object])
], CalendarDaysViewElement.prototype, "rangePreviewDate", null);
__decorate([
    Emit({ eventName: 'previewDateChange' }),
    __metadata("design:type", Object),
    __metadata("design:paramtypes", [])
], CalendarDaysViewElement.prototype, "previewDateChange", null);
CalendarDaysViewElement = __decorate([
    Component({
        selector: 'mosaik-calendar-days-view',
        template: calendarDaysViewElementTemplate,
        themes: {
            joy: calendarDaysViewElementJoyStyle,
            memphis: calendarDaysViewElementMemphisStyle,
            cosmopolitan: calendarDaysViewElementCosmopolitanStyle
        },
        imports: [
            TextElement,
            ButtonElement,
            ButtonGroupElement,
            CalendarItemElement
        ]
    }),
    __metadata("design:paramtypes", [])
], CalendarDaysViewElement);
export { CalendarDaysViewElement };
//# sourceMappingURL=CalendarDaysViewElement.js.map