import { MXValueComponent, AXValuableComponent, AXComponent } from '@acorex/cdk/common';
import * as i0 from '@angular/core';
import { input, computed, signal, viewChild, inject, Renderer2, effect, forwardRef, HostBinding, ChangeDetectionStrategy, ViewEncapsulation, Component, NgModule } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { CommonModule } from '@angular/common';

/**
 * Calculates the horizontal position of a pointer event (mouse or touch) as a percentage
 * of the target element's width.
 *
 * @param event - The mouse or touch event to get the pointer position from.
 * @returns The percentage of the event's position relative to the target element's width,
 *          or -1 if the event is invalid.
 *
 * @remarks
 * - For `MouseEvent`, it uses the `clientX` property.
 * - For `TouchEvent`, it uses the `clientX` of the first touch point.
 * - Returns `-1` if the event is neither a valid `MouseEvent` nor a `TouchEvent`.
 *
 * @example
 * ```typescript
 * document.addEventListener('click', function(event) {
 *   const percentage = getPointerPercentage(event);
 *   console.log(`Click position: ${percentage.toFixed(2)}% of the element's width`);
 * });
 *
 * document.addEventListener('touchstart', function(event) {
 *   const percentage = getPointerPercentage(event);
 *   console.log(`Touch position: ${percentage.toFixed(2)}% of the element's width`);
 * });
 * ```
 */
function getPointerPercentage(event) {
    let clientX;
    if (event instanceof MouseEvent) {
        clientX = event.clientX; // Mouse event uses clientX
    }
    else if (event instanceof TouchEvent && event.touches.length > 0) {
        clientX = event.touches[0].clientX; // Touch event uses the first touch's clientX
    }
    else {
        return -1; // Return -1 for invalid events
    }
    // Use currentTarget to get the bounding box of the element the event listener is attached to
    const rect = event.currentTarget.getBoundingClientRect();
    const clickX = clientX - rect.left; // Calculate X position relative to the element
    const divWidth = rect.width; // Get the width from the bounding box
    const percentage = (clickX / divWidth) * 100; // Calculate percentage
    return percentage;
}

/**
 * @description
 * The `AXRatePickerComponent` provides a customizable rating picker.
 * The component allows users to select a rating between 0 and the defined maximum.
 * It supports transitions, dynamic value changes, and hover states.
 *
 * @example
 * <ax-rate-picker [max]="5" [readonly]="false" [disabled]="false"></ax-rate-picker>
 */
class AXRatePickerComponent extends MXValueComponent {
    /**
     * @description
     * Initializes the component and sets up value change subscription.
     */
    constructor() {
        super();
        /**
         * @description
         * The icon to be used for each rating point.
         * @default 'fa-star'
         */
        this.iconName = input('fa-star', ...(ngDevMode ? [{ debugName: "iconName" }] : []));
        /**
         * @description
         * Maximum value for the rating.
         * Defines how many rating points are available.
         * @default 5
         */
        this.max = input(5, ...(ngDevMode ? [{ debugName: "max" }] : []));
        /**
         * @description
         * Defines the step increment between rating values.
         * The rating value will be rounded to the nearest multiple of this step.
         * @default 0.01
         */
        this.step = input(0.01, ...(ngDevMode ? [{ debugName: "step" }] : []));
        /**
         * @description
         * Whether the rating should have a transition effect when changing values.
         * @default true
         */
        this.hasTransition = input(true, ...(ngDevMode ? [{ debugName: "hasTransition" }] : []));
        /**
         * @description
         * The percentage of the current rating value relative to the maximum rating.
         */
        this.ratePercentage = computed(() => Math.round((this.currentValue() / this.max()) * 10000) / 100, ...(ngDevMode ? [{ debugName: "ratePercentage" }] : []));
        /**
         * @description
         * The current rating value as a signal.
         */
        this.currentValue = signal(this.max(), ...(ngDevMode ? [{ debugName: "currentValue" }] : []));
        /**
         * @description
         * State for tracking hover status and previous value.
         */
        this.prevState = {
            ishover: false,
            previousValue: undefined,
        };
        /**
         * @description
         * Reference to the container element.
         */
        this.containerEl = viewChild.required('c');
        /**
         * @description
         * Reference to the rating element.
         */
        this.ratingEl = viewChild.required('r');
        /**
         * @description
         * Renderer for manipulating styles.
         */
        this.renderer = inject(Renderer2);
        /**
         * @description
         * Array of rating values from 1 to `max`.
         */
        this.rates = computed(() => Array(this.max())
            .fill(0)
            .map((_, index) => index + 1), ...(ngDevMode ? [{ debugName: "rates" }] : []));
        effect(() => {
            if (!this.hasTransition()) {
                this.renderer.removeStyle(this.ratingEl().nativeElement, 'transition');
            }
            return this.hasTransition();
        });
        this.onValueChanged.subscribe((val) => {
            if (val.value < 0) {
                this.currentValue.set(0);
                console.warn('ax-rate-picker: value cant be negative!');
            }
            else if (val.value > this.max()) {
                this.currentValue.set(this.max());
                console.warn('ax-rate-picker: value cant be more than max!');
            }
            else {
                this.currentValue.set(val.value);
            }
        });
    }
    /**
     * @description
     * Calculates and updates the rating based on the mouse or touch event.
     *
     * @param event - The mouse or touch event triggering the rating calculation.
     */
    calculateRate(event) {
        if (this.hasTransition()) {
            this.renderer.removeStyle(this.ratingEl().nativeElement, 'transition');
        }
        event.preventDefault();
        if (!this.readonly && !this.disabled) {
            const pointerPercentage = getPointerPercentage(event);
            if (pointerPercentage === -1) {
                return console.warn('Only support touch and click events.');
            }
            const value = this.roundToStep(pointerPercentage);
            const decimals = this.countDecimals(this.step());
            if (this.prevState.ishover === true) {
                this.currentValue.set(value);
            }
            else {
                this.commitValue(Number(value.toFixed(decimals)));
                this.mouseEnter();
            }
        }
    }
    /**
     * @description
     * Rounds the rating value to the nearest step.
     *
     * @param percentage - The calculated percentage from pointer event.
     * @returns The rounded rating value.
     */
    roundToStep(percentage) {
        const value = (percentage / 100) * this.max();
        const value2 = Math.round(value / (this.step() || 0.01)) * this.step();
        return value2;
    }
    /**
     * @description
     * Handles mouse enter events to start tracking mouse movements for rating.
     */
    mouseEnter() {
        this.prevState = {
            ishover: true,
            previousValue: this.value,
        };
        if (this.hasTransition()) {
            this.renderer.setStyle(this.ratingEl().nativeElement, 'opacity', '80%');
        }
        const moveListener = (moveEvent) => this.calculateRate(moveEvent);
        const endListener = () => this.onEnd(moveListener, endListener);
        const container = this.containerEl().nativeElement;
        container.addEventListener('mousemove', moveListener);
        container.addEventListener('mouseup', endListener);
        container.addEventListener('mouseleave', endListener);
    }
    /**
     * @description
     * Handles mouse leave events to reset styles.
     */
    mouseLeave() {
        if (this.hasTransition()) {
            this.renderer.removeStyle(this.ratingEl().nativeElement, 'opacity');
        }
    }
    /**
     * @description
     * Cleans up event listeners and restores the previous rating value.
     *
     * @param moveListener - The function to remove for mouse move events.
     * @param endListener - The function to remove for mouse end events.
     */
    onEnd(moveListener, endListener) {
        if (this.hasTransition()) {
            this.renderer.setStyle(this.ratingEl().nativeElement, 'transition', `width ${this.max() * 50 + 250}ms cubic-bezier(0.29, 0.72, 0.68, 0.85)`);
            this.renderer.removeStyle(this.ratingEl().nativeElement, 'opacity');
        }
        this.currentValue.set(this.prevState.previousValue);
        this.prevState = {
            ishover: false,
            previousValue: undefined,
        };
        const container = this.containerEl().nativeElement;
        container.removeEventListener('mousemove', moveListener);
        container.removeEventListener('mouseup', endListener);
        container.removeEventListener('mouseleave', endListener);
    }
    countDecimals(number) {
        if (Math.floor(number) === number)
            return 0;
        return number.toString().split('.')[1].length || 0;
    }
    /**
     * @description
     * Determines if the component is active (i.e., not readonly or disabled).
     */
    get isActive() {
        return !this.readonly && !this.disabled;
    }
    /**
     * @description
     * Determines if the component is in readonly mode.
     */
    get isReadonly() {
        return this.readonly;
    }
    /**
     * @description
     * Determines if the component is disabled.
     */
    get isDisabled() {
        return this.disabled;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXRatePickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AXRatePickerComponent, isStandalone: true, selector: "ax-rate-picker", inputs: { readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: false, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: false, isRequired: false, transformFunction: null }, iconName: { classPropertyName: "iconName", publicName: "iconName", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, hasTransition: { classPropertyName: "hasTransition", publicName: "hasTransition", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.ax-active": "this.isActive", "class.ax-readonly": "this.isReadonly", "class.ax-disable": "this.isDisabled" } }, providers: [
            { provide: AXValuableComponent, useExisting: AXRatePickerComponent },
            {
                provide: NG_VALUE_ACCESSOR,
                useExisting: forwardRef(() => AXRatePickerComponent),
                multi: true,
            },
            { provide: AXComponent, useExisting: AXRatePickerComponent },
        ], viewQueries: [{ propertyName: "containerEl", first: true, predicate: ["c"], descendants: true, isSignal: true }, { propertyName: "ratingEl", first: true, predicate: ["r"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div\n  #c\n  class=\"ax-rate-picker-container\"\n  (click)=\"calculateRate($event)\"\n  (touchstart)=\"calculateRate($event)\"\n  (mouseenter)=\"mouseEnter()\"\n  (mouseleave)=\"mouseLeave()\"\n>\n  <div #r class=\"ax-rate-picker-rating\" [style.width.%]=\"ratePercentage()\">\n    <div class=\"ax-rate-picker-icons ax-rp-active\">\n      @for (rate of rates(); track rate) {\n        <i class=\"ax-rate-picker-icon fa-solid\" [class]=\"iconName()\" [class.pointer]=\"!this.readonly\"></i>\n      }\n    </div>\n  </div>\n  <div class=\"ax-rate-picker-icons ax-rp-inactive\">\n    @for (rate of rates(); track rate) {\n      <i class=\"ax-rate-picker-icon fa-solid\" [class]=\"iconName()\"></i>\n    }\n  </div>\n</div>\n", styles: ["ax-rate-picker{--ax-comp-rate-picker-bg-color: var(--ax-sys-color-warning-surface);--ax-comp-rate-picker-inactive-bg-color: var(--ax-sys-color-dark);--ax-comp-rate-picker-inactive-bg-color-opacity: .2}.ax-dark ax-rate-picker{--ax-comp-rate-picker-inactive-bg-color: var(--ax-sys-color-light)}.ax-xs ax-rate-picker,ax-rate-picker.ax-xs{--ax-comp-rate-picker-font-size: 1rem}.ax-sm ax-rate-picker,ax-rate-picker.ax-sm{--ax-comp-rate-picker-font-size: 1.5rem}.ax-md ax-rate-picker,ax-rate-picker{--ax-comp-rate-picker-font-size: 2rem}.ax-lg ax-rate-picker,ax-rate-picker.ax-lg{--ax-comp-rate-picker-font-size: 2.5rem}.ax-xl ax-rate-picker,ax-rate-picker.ax-xl{--ax-comp-rate-picker-font-size: 3rem}ax-rate-picker{font-size:var(--ax-comp-rate-picker-font-size, 1.5rem)}ax-rate-picker.ax-active{cursor:pointer}ax-rate-picker.ax-disable{cursor:not-allowed;opacity:.5}ax-rate-picker .ax-rate-picker-container{position:relative;width:min-content}ax-rate-picker .ax-rate-picker-container .ax-rate-picker-rating{position:relative;z-index:10;overflow:hidden}ax-rate-picker .ax-rate-picker-container .ax-rate-picker-rating .ax-rate-picker-icons{display:flex}ax-rate-picker .ax-rate-picker-container .ax-rate-picker-rating .ax-rate-picker-icons.ax-rp-active{color:rgba(var(--ax-comp-rate-picker-bg-color))}ax-rate-picker .ax-rate-picker-container .ax-rate-picker-icons{display:flex}ax-rate-picker .ax-rate-picker-container .ax-rate-picker-icons.ax-rp-inactive{color:rgba(var(--ax-comp-rate-picker-inactive-bg-color),var(--ax-comp-rate-picker-inactive-bg-color-opacity));position:absolute;left:50%;top:50%;translate:-50% -50%;z-index:5}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXRatePickerComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ax-rate-picker', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, inputs: ['readonly', 'disabled'], providers: [
                        { provide: AXValuableComponent, useExisting: AXRatePickerComponent },
                        {
                            provide: NG_VALUE_ACCESSOR,
                            useExisting: forwardRef(() => AXRatePickerComponent),
                            multi: true,
                        },
                        { provide: AXComponent, useExisting: AXRatePickerComponent },
                    ], template: "<div\n  #c\n  class=\"ax-rate-picker-container\"\n  (click)=\"calculateRate($event)\"\n  (touchstart)=\"calculateRate($event)\"\n  (mouseenter)=\"mouseEnter()\"\n  (mouseleave)=\"mouseLeave()\"\n>\n  <div #r class=\"ax-rate-picker-rating\" [style.width.%]=\"ratePercentage()\">\n    <div class=\"ax-rate-picker-icons ax-rp-active\">\n      @for (rate of rates(); track rate) {\n        <i class=\"ax-rate-picker-icon fa-solid\" [class]=\"iconName()\" [class.pointer]=\"!this.readonly\"></i>\n      }\n    </div>\n  </div>\n  <div class=\"ax-rate-picker-icons ax-rp-inactive\">\n    @for (rate of rates(); track rate) {\n      <i class=\"ax-rate-picker-icon fa-solid\" [class]=\"iconName()\"></i>\n    }\n  </div>\n</div>\n", styles: ["ax-rate-picker{--ax-comp-rate-picker-bg-color: var(--ax-sys-color-warning-surface);--ax-comp-rate-picker-inactive-bg-color: var(--ax-sys-color-dark);--ax-comp-rate-picker-inactive-bg-color-opacity: .2}.ax-dark ax-rate-picker{--ax-comp-rate-picker-inactive-bg-color: var(--ax-sys-color-light)}.ax-xs ax-rate-picker,ax-rate-picker.ax-xs{--ax-comp-rate-picker-font-size: 1rem}.ax-sm ax-rate-picker,ax-rate-picker.ax-sm{--ax-comp-rate-picker-font-size: 1.5rem}.ax-md ax-rate-picker,ax-rate-picker{--ax-comp-rate-picker-font-size: 2rem}.ax-lg ax-rate-picker,ax-rate-picker.ax-lg{--ax-comp-rate-picker-font-size: 2.5rem}.ax-xl ax-rate-picker,ax-rate-picker.ax-xl{--ax-comp-rate-picker-font-size: 3rem}ax-rate-picker{font-size:var(--ax-comp-rate-picker-font-size, 1.5rem)}ax-rate-picker.ax-active{cursor:pointer}ax-rate-picker.ax-disable{cursor:not-allowed;opacity:.5}ax-rate-picker .ax-rate-picker-container{position:relative;width:min-content}ax-rate-picker .ax-rate-picker-container .ax-rate-picker-rating{position:relative;z-index:10;overflow:hidden}ax-rate-picker .ax-rate-picker-container .ax-rate-picker-rating .ax-rate-picker-icons{display:flex}ax-rate-picker .ax-rate-picker-container .ax-rate-picker-rating .ax-rate-picker-icons.ax-rp-active{color:rgba(var(--ax-comp-rate-picker-bg-color))}ax-rate-picker .ax-rate-picker-container .ax-rate-picker-icons{display:flex}ax-rate-picker .ax-rate-picker-container .ax-rate-picker-icons.ax-rp-inactive{color:rgba(var(--ax-comp-rate-picker-inactive-bg-color),var(--ax-comp-rate-picker-inactive-bg-color-opacity));position:absolute;left:50%;top:50%;translate:-50% -50%;z-index:5}\n"] }]
        }], ctorParameters: () => [], propDecorators: { iconName: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconName", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], hasTransition: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasTransition", required: false }] }], containerEl: [{ type: i0.ViewChild, args: ['c', { isSignal: true }] }], ratingEl: [{ type: i0.ViewChild, args: ['r', { isSignal: true }] }], isActive: [{
                type: HostBinding,
                args: ['class.ax-active']
            }], isReadonly: [{
                type: HostBinding,
                args: ['class.ax-readonly']
            }], isDisabled: [{
                type: HostBinding,
                args: ['class.ax-disable']
            }] } });

class AXRatePickerModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXRatePickerModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.16", ngImport: i0, type: AXRatePickerModule, imports: [CommonModule, AXRatePickerComponent], exports: [AXRatePickerComponent] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXRatePickerModule, imports: [CommonModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXRatePickerModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [CommonModule, AXRatePickerComponent],
                    exports: [AXRatePickerComponent],
                }]
        }] });

/**
 * Generated bundle index. Do not edit.
 */

export { AXRatePickerComponent, AXRatePickerModule };
//# sourceMappingURL=acorex-components-rate-picker.mjs.map
