import { Component, ComponentFactoryResolver, EventEmitter, Inject, Input, Output, Type, Optional, } from '@angular/core';
import { takeUntil } from 'rxjs/operators';
import { ReplaySubject, Subject } from 'rxjs';
import { RCalendarSize, RCalendarViewMode, } from '../../extra/calendar-kit/model';
import { Overlay, OverlayPositionBuilder } from '@angular/cdk/overlay';
import { RTrigger, RTriggerStrategyBuilderService, } from '../../../cdk/overlay/overlay-trigger';
import { RDateService } from '../../extra/calendar-kit';
import { ComponentPortal } from '@angular/cdk/portal';
import { patch } from '../../../cdk/overlay/overlay-service';
import { DOCUMENT } from '@angular/common';
import { RCalendarComponent, RCalendarRangeComponent, } from '../../extra/calendar';
import { RDatepicker, R_DATE_SERVICE_OPTIONS } from './datepicker.directive';
/**
 * The `RBasePicker` component concentrates overlay manipulation logic.
 * */
export class RBasePicker extends RDatepicker {
    constructor(overlay, positionBuilder, triggerStrategyBuilder, cfr, dateService, dateServiceOptions) {
        super();
        this.overlay = overlay;
        this.positionBuilder = positionBuilder;
        this.triggerStrategyBuilder = triggerStrategyBuilder;
        this.cfr = cfr;
        this.dateService = dateService;
        this.dateServiceOptions = dateServiceOptions;
        /**
         * Size of the calendar and entire components.
         * Can be 'medium' which is default or 'large'.
         * */
        this.size = RCalendarSize.MEDIUM;
        this.init$ = new ReplaySubject();
        /**
         * Stream of picker changes. Required to be the subject because picker hides and shows and picker
         * change stream becomes recreated.
         * */
        this.onChange$ = new Subject();
        this.overlayOffset = 8;
        this.destroy$ = new Subject();
        this.blur$ = new Subject();
    }
    /**
     * Returns picker instance.
     * */
    get picker() {
        var _a;
        return (_a = this.pickerRef) === null || _a === void 0 ? void 0 : _a.instance;
    }
    /**
     * Stream of picker value changes.
     * */
    get valueChange() {
        return this.onChange$.asObservable();
    }
    get isShown() {
        return this.ref && this.ref.hasAttached();
    }
    get init() {
        return this.init$.asObservable();
    }
    /**
     * Emits when datepicker looses focus.
     */
    get blur() {
        return this.blur$.asObservable();
    }
    /**
     * Datepicker knows nothing about host html input element.
     * So, attach method attaches datepicker to the host input element.
     * */
    attach(hostRef) {
        this.hostRef = hostRef;
        this.subscribeOnTriggers();
    }
    getValidatorConfig() {
        return { min: this.min, max: this.max, filter: this.filter };
    }
    show() {
        if (!this.ref) {
            this.createOverlay();
        }
        this.openDatepicker();
    }
    shouldHide() {
        return this.hideOnSelect && !!this.value;
    }
    hide() {
        if (this.ref) {
            this.ref.detach();
            this.ref.dispose();
            this.ref = null;
        }
        // save current value if picker was rendered
        if (this.picker) {
            this.queue = this.value;
            this.pickerRef.destroy();
            this.pickerRef = null;
            this.container = null;
        }
    }
    createOverlay() {
        this.positionStrategy = this.createPositionStrategy();
        this.ref = this.overlay.create({
            positionStrategy: this.positionStrategy,
            scrollStrategy: this.overlay.scrollStrategies.block(),
            panelClass: 'datepicker-overlay-panel'
        });
        // this.subscribeOnPositionChange();
    }
    openDatepicker() {
        // this.container = this.ref.attach(
        //   new ComponentPortal(RDatepickerContainerComponent, null, null, this.cfr)
        // );
        this.instantiatePicker();
        this.subscribeOnValueChange();
        this.writeQueue();
        this.patchWithInputs();
        this.pickerRef.changeDetectorRef.markForCheck();
    }
    createPositionStrategy() {
        return this.overlay.position()
            .flexibleConnectedTo(this.hostRef.nativeElement)
            .withPush(false)
            .withPositions([{
                originX: 'start',
                originY: 'bottom',
                overlayX: 'start',
                overlayY: 'top'
            }, {
                originX: 'start',
                originY: 'top',
                overlayX: 'start',
                overlayY: 'bottom'
            }]);
    }
    // protected createPositionStrategy(): RAdjustableConnectedPositionStrategy {
    //   return this.positionBuilder
    //     .connectedTo(this.hostRef)
    //     .position(RPosition.BOTTOM)
    //     .offset(this.overlayOffset)
    //     .adjustment(RAdjustment.COUNTERCLOCKWISE);
    // }
    subscribeOnPositionChange() {
        this.positionStrategy.positionChange
            .pipe(takeUntil(this.destroy$))
            .subscribe((position) => patch(this.container, { position }));
    }
    createTriggerStrategy() {
        return this.triggerStrategyBuilder
            .trigger(RTrigger.FOCUS)
            .host(this.hostRef.nativeElement)
            .container(() => this.getContainer())
            .build();
    }
    getContainer() {
        return this.ref && this.ref.hasAttached() && {
            location: {
                nativeElement: this.ref.overlayElement,
            },
        };
    }
    subscribeOnTriggers() {
        this.triggerStrategy = this.createTriggerStrategy();
        this.triggerStrategy.show$.subscribe(() => this.show());
        this.triggerStrategy.hide$.subscribe(() => {
            this.blur$.next();
            this.hide();
        });
    }
    instantiatePicker() {
        if (!this.pickerRef) {
            this.pickerRef = this.ref.attach(new ComponentPortal(this.pickerClass, null, null, this.cfr));
            // this.ref.backdropClick().subscribe(() => this.hide());
        }
    }
    /**
     * Subscribes on picker value changes and emit data through this.onChange$ subject.
     * */
    subscribeOnValueChange() {
        this.pickerValueChange.subscribe((date) => {
            this.onChange$.next(date);
        });
    }
    patchWithInputs() {
        this.picker.boundingMonth = this.boundingMonth;
        this.picker.startView = this.startView;
        this.picker.min = this.min;
        this.picker.max = this.max;
        this.picker.filter = this.filter;
        this.picker._cellComponent = this.dayCellComponent;
        this.picker._monthCellComponent = this.monthCellComponent;
        this.picker._yearCellComponent = this.yearCellComponent;
        this.picker.size = this.size;
        this.picker.showNavigation = this.showNavigation;
        this.picker.visibleDate = this.visibleDate;
        this.picker.showWeekNumber = this.showWeekNumber;
        this.picker.weekNumberSymbol = this.weekNumberSymbol;
    }
    checkFormat() {
        if (this.dateService.getId() === 'native' && this.format) {
            throw new Error("Can't format native date.");
        }
        const isFormatSet = this.format ||
            (this.dateServiceOptions && this.dateServiceOptions.format);
        if (this.dateService.getId() === 'date-fns' && !isFormatSet) {
            throw new Error('format is required when using RDateFnsDateModule');
        }
    }
}
export class RBasePickerComponent extends RBasePicker {
    constructor(document, positionBuilder, triggerStrategyBuilder, overlay, cfr, dateService, dateServiceOptions) {
        super(overlay, positionBuilder, triggerStrategyBuilder, cfr, dateService, dateServiceOptions);
        /**
         * Defines if we should render previous and next months
         * in the current month view.
         * */
        this.boundingMonth = true;
        /**
         * Defines starting view for calendar.
         * */
        this.startView = RCalendarViewMode.DATE;
        /**
         * Size of the calendar and entire components.
         * Can be 'medium' which is default or 'large'.
         * */
        this.size = RCalendarSize.MEDIUM;
        /**
         * Hide picker when a date or a range is selected, `true` by default
         * @type {boolean}
         */
        this.hideOnSelect = true;
        /**
         * Determines should we show calendars navigation or not.
         * @type {boolean}
         */
        this.showNavigation = true;
        /**
         * Sets symbol used as a header for week numbers column
         * */
        this.weekNumberSymbol = '#';
        this._showWeekNumber = false;
        /**
         * Determines picker overlay offset (in pixels).
         * */
        this.overlayOffset = 8;
    }
    /**
     * Determines should we show week numbers column.
     * False by default.
     * */
    get showWeekNumber() {
        return this._showWeekNumber;
    }
    set showWeekNumber(value) {
        this._showWeekNumber = !!value;
    }
    ngOnInit() {
        this.checkFormat();
    }
    ngOnChanges(changes) {
        if (changes.format && !changes.format.isFirstChange()) {
            this.checkFormat();
        }
    }
    ngAfterViewInit() {
        this.init$.next();
    }
    ngOnDestroy() {
        this.destroy$.next();
        this.destroy$.complete();
        this.hide();
        this.init$.complete();
        if (this.ref) {
            this.ref.dispose();
        }
        if (this.triggerStrategy) {
            this.triggerStrategy.destroy();
        }
    }
    get pickerValueChange() {
        return;
    }
    get value() {
        return undefined;
    }
    set value(value) { }
    writeQueue() { }
}
RBasePickerComponent.decorators = [
    { type: Component, args: [{
                template: ''
            },] }
];
RBasePickerComponent.ctorParameters = () => [
    { type: undefined, decorators: [{ type: Inject, args: [DOCUMENT,] }] },
    { type: OverlayPositionBuilder },
    { type: RTriggerStrategyBuilderService },
    { type: Overlay },
    { type: ComponentFactoryResolver },
    { type: RDateService },
    { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [R_DATE_SERVICE_OPTIONS,] }] }
];
RBasePickerComponent.propDecorators = {
    format: [{ type: Input }],
    boundingMonth: [{ type: Input }],
    startView: [{ type: Input }],
    min: [{ type: Input }],
    max: [{ type: Input }],
    filter: [{ type: Input }],
    dayCellComponent: [{ type: Input }],
    monthCellComponent: [{ type: Input }],
    yearCellComponent: [{ type: Input }],
    size: [{ type: Input }],
    visibleDate: [{ type: Input }],
    hideOnSelect: [{ type: Input }],
    showNavigation: [{ type: Input }],
    weekNumberSymbol: [{ type: Input }],
    showWeekNumber: [{ type: Input }],
    overlayOffset: [{ type: Input }]
};
/**
 * The DatePicker components itself.
 * Provides a proxy to `RCalendar` options as well as custom picker options.
 */
export class RDatepickerComponent extends RBasePickerComponent {
    constructor() {
        super(...arguments);
        this.pickerClass = RCalendarComponent;
    }
    /**
     * Date which will be rendered as selected.
     * */
    set date(date) {
        this.value = date;
    }
    /**
     * Emits date when selected.
     * */
    get dateChange() {
        return this.valueChange;
    }
    get value() {
        return this.picker ? this.picker.date : undefined;
    }
    set value(date) {
        if (!this.picker) {
            this.queue = date;
            return;
        }
        if (date) {
            this.visibleDate = date;
            this.picker.visibleDate = date;
            this.picker.date = date;
        }
    }
    get pickerValueChange() {
        return this.picker.dateChange;
    }
    writeQueue() {
        if (this.queue) {
            const date = this.queue;
            this.queue = null;
            this.value = date;
        }
    }
}
RDatepickerComponent.decorators = [
    { type: Component, args: [{
                selector: 'r-datepicker',
                template: ''
            },] }
];
RDatepickerComponent.propDecorators = {
    date: [{ type: Input }],
    dateChange: [{ type: Output }]
};
/**
 * The RangeDatePicker components itself.
 * Provides a proxy to `RCalendarRange` options as well as custom picker options.
 */
export class RRangepickerComponent extends RBasePickerComponent {
    constructor() {
        super(...arguments);
        this.pickerClass = RCalendarRangeComponent;
    }
    /**
     * Range which will be rendered as selected.
     * */
    set range(range) {
        this.value = range;
    }
    /**
     * Emits range when start selected and emits again when end selected.
     * */
    get rangeChange() {
        return this.valueChange;
    }
    get value() {
        return this.picker ? this.picker.range : undefined;
    }
    set value(range) {
        if (!this.picker) {
            this.queue = range;
            return;
        }
        if (range) {
            const visibleDate = range && range.start;
            this.visibleDate = visibleDate;
            this.picker.visibleDate = visibleDate;
            this.picker.range = range;
        }
    }
    get pickerValueChange() {
        return this.picker.rangeChange;
    }
    shouldHide() {
        return (super.shouldHide() && !!(this.value && this.value.start && this.value.end));
    }
    writeQueue() {
        if (this.queue) {
            const range = this.queue;
            this.queue = null;
            this.value = range;
        }
    }
}
RRangepickerComponent.decorators = [
    { type: Component, args: [{
                selector: 'r-rangepicker',
                template: ''
            },] }
];
RRangepickerComponent.propDecorators = {
    range: [{ type: Input }],
    rangeChange: [{ type: Output }]
};
//# sourceMappingURL=datepicker.component.js.map