import { CommonModule } from '@angular/common';
import * as i0 from '@angular/core';
import { Component, Input, Injectable, NgModule } from '@angular/core';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { ReplaySubject, BehaviorSubject, map, shareReplay, combineLatestWith, mergeWith, switchMap, take, of, tap, withLatestFrom, filter, startWith } from 'rxjs';
import * as i1 from '@deja-js/component/core';
import { sortBy } from 'lodash-es';

/*
 *  @license
 *  Copyright Hôpitaux Universitaires de Genève. All Rights Reserved.
 *
 *  Use of this source code is governed by an Apache-2.0 license that can be
 *  found in the LICENSE file at https://github.com/DSI-HUG/dejajs-components/blob/master/LICENSE
 */
class ItemComponent {
    set selected(value) {
        this._selected = coerceBooleanProperty(value);
    }
    get selected() {
        return this._selected;
    }
}
ItemComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.1", ngImport: i0, type: ItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
ItemComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.1.1", type: ItemComponent, selector: "item", inputs: { value: "value", text: "text", selected: "selected" }, ngImport: i0, template: '', isInline: true });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.1", ngImport: i0, type: ItemComponent, decorators: [{
            type: Component,
            args: [{
                    selector: 'item',
                    template: ''
                }]
        }], propDecorators: { value: [{
                type: Input
            }], text: [{
                type: Input
            }], selected: [{
                type: Input
            }] } });

/*
 *  @license
 *  Copyright Hôpitaux Universitaires de Genève. All Rights Reserved.
 *
 *  Use of this source code is governed by an Apache-2.0 license that can be
 *  found in the LICENSE file at https://github.com/DSI-HUG/dejajs-components/blob/master/LICENSE
 */
class Item {
    constructor(id, label, model) {
        this.model = model;
        this.id = id;
        this.label = label;
    }
    /** Définit si l'élément peut être réduit.
     * @return True si l'élément peut être réduit.
     */
    get isCollapsible() {
        return this.items && (this.collapsible ?? true);
    }
    /** Définit si l'élément est visible.
     * @return True si l'élément est visible.
     */
    get isVisible() {
        return this.visible ?? true;
    }
    /** Définit si l'élément peut-être sélectionné.
     * @return True si l'élément peut-être sélectionné.
     */
    get isSelectable() {
        return this.selectable ?? true;
    }
}

/*
 *  @license
 *  Copyright Hôpitaux Universitaires de Genève. All Rights Reserved.
 *
 *  Use of this source code is governed by an Apache-2.0 license that can be
 *  found in the LICENSE file at https://github.com/DSI-HUG/dejajs-components/blob/master/LICENSE
 */
/** Service de gestion des listes (treelist et select).
 * Ce service permet la gestion du viewport et la gestion des caches des listes.
 * Il peut-être surchargé pour faire du lazy loading ou du paging.
 */
class ItemService {
    constructor(diacriticService) {
        this.diacriticService = diacriticService;
        this.items$ = new ReplaySubject(1);
        this.models$ = new ReplaySubject(1);
        this.options$ = new ReplaySubject(1);
        this.childrenField$ = new BehaviorSubject('items');
        this.textField$ = new BehaviorSubject('label');
        this.valueField$ = new BehaviorSubject('value');
        this.searchField$ = new BehaviorSubject('searchText');
        this.query$ = new BehaviorSubject('');
        this.minSearchLength$ = new BehaviorSubject(0);
        this.refreshFlatItemList$ = new BehaviorSubject(undefined);
        this.refreshFilterItemList$ = new BehaviorSubject(undefined);
        this.refreshVisibleItemList$ = new BehaviorSubject(undefined);
        this.refreshSelection$ = new BehaviorSubject({});
        this.compareItems = (item1, item2) => {
            const isDefined = (value) => value !== undefined && value !== null;
            if (!isDefined(item1) || !isDefined(item2)) {
                return false;
            }
            else {
                const model1 = item1.model;
                const model2 = item1.model;
                if (model1?.equals) {
                    return model1.equals(item2.model);
                }
                else if (model2?.equals) {
                    return model2.equals(item1.model);
                }
                else if (item1.id && item2.id) {
                    return item1.id === item2.id;
                }
                else {
                    return item1.model === item2.model;
                }
            }
        };
        this.compareModels = (model1, model2, valueField) => {
            const isDefined = (value) => value !== undefined && value !== null;
            if (!isDefined(model1) || !isDefined(model2)) {
                return false;
            }
            else if (model1 === model2) {
                return true;
            }
            else if (Object(model1) === model1 && Object(model2) === model2) {
                const cmp1 = model1;
                const cmp2 = model2;
                if (cmp1?.equals) {
                    return cmp1.equals(model2);
                }
                else if (cmp2?.equals) {
                    return cmp2.equals(model1);
                }
                else if (valueField) {
                    return this.extractValueField(model1, valueField) === this.extractValueField(model2, valueField);
                }
                else {
                    return false;
                }
            }
            else {
                return false;
            }
        };
        const itemsFromOptions$ = this.options$.pipe(map(options => {
            const items = options.map(option => {
                const item = new Item(option.value, option.text);
                item.selected = option.selected === true || option.selected === '';
                return item;
            });
            if (items.length > 100) {
                // eslint-disable-next-line no-debugger
                debugger;
                console.error('Select options with more than 100 items can have performance options. Please bind directly the items in code behind with items or models input.');
            }
            return items;
        }), shareReplay({ bufferSize: 1, refCount: false }));
        const itemsFromModels$ = this.models$.pipe(combineLatestWith(this.valueField$, this.textField$, this.childrenField$), map(([models, valueField, textField, childrenField]) => (models && models instanceof Array && this.mapToItem(models, valueField, textField, childrenField)) || new Array()), shareReplay({ bufferSize: 1, refCount: false }));
        this.itemList$ = this.items$.pipe(mergeWith(itemsFromModels$, itemsFromOptions$), switchMap(items => {
            if (this.selectedItems$ && items?.length > 0) {
                return this.selectedItems$.pipe(take(1), map(selectedItems => {
                    if (selectedItems?.length) {
                        const selectedIdSet = selectedItems.reduce((set, item) => {
                            if (item.id) {
                                set.add(item.id);
                            }
                            return set;
                        }, new Set());
                        items.forEach(item => {
                            if (item.id) {
                                item.selected = selectedIdSet.has(item.id);
                            }
                        });
                    }
                    return items;
                }));
            }
            return of(items);
        }), shareReplay({ bufferSize: 1, refCount: false }));
        this.flatItemList$ = this.itemList$.pipe(combineLatestWith(this.refreshFlatItemList$), map(([items]) => {
            const addItems = (itms, depth) => itms.reduce((a, item) => {
                item.depth = depth;
                a.push(item);
                if (item.items?.length) {
                    return [...a, ...addItems(item.items, depth + 1)];
                }
                return a;
            }, new Array());
            return (items && addItems(items, 0)) || new Array();
        }), tap(() => {
            this.previousQuery = undefined;
        }), shareReplay({ bufferSize: 1, refCount: false }));
        const refreshFilterItemList$ = this.refreshFilterItemList$.pipe(tap(() => {
            this.previousQuery = undefined;
        }));
        this.filteredItemList$ = this.flatItemList$.pipe(combineLatestWith(this.query$, this.minSearchLength$, this.searchField$, refreshFilterItemList$), switchMap(([flatItemList, query, minSearchLength, searchField]) => {
            if (minSearchLength > 0 && (!query || typeof query === 'string' && query.length < minSearchLength)) {
                this.previousQuery = null;
                return of(new Array());
            }
            if (!query) {
                return of(flatItemList);
            }
            const listToFilter$ = typeof query === 'string' && this.previousQuery && query.includes(this.previousQuery) && this.filteredItemList$ ? this.filteredItemList$ : of(flatItemList);
            this.previousQuery = typeof query === 'string' ? query : null;
            const escapeChars = (text) => {
                const specialChars = ['\\', '/', '|', '&', ';', '$', '%', '@', '"', '<', '>', '(', ')', '+'];
                specialChars.forEach(c => text = text.replace(c, `\\${c}`));
                return text;
            };
            // Check regexp validity
            // regExp.test(this.getTextValue(item));
            let regExp;
            if (query) {
                if (typeof query === 'string') {
                    try {
                        query = this.diacriticService.remove(query);
                        const escapedQuery = escapeChars(query);
                        regExp = new RegExp(escapedQuery, 'i');
                    }
                    catch (exc) {
                        console.log('Invalid search parameters');
                    }
                }
                else {
                    regExp = query;
                    if (regExp.test === undefined) {
                        regExp = undefined;
                    }
                }
            }
            return listToFilter$.pipe(take(1), map(itemList => {
                // Filter the list
                let previousItem;
                return [...itemList].reverse().filter(item => {
                    let isVisible;
                    if (item.items === undefined) {
                        // child
                        isVisible = this.itemMatch(item, searchField, regExp);
                    }
                    else {
                        // parent
                        isVisible = this.parentItemMatch(item, previousItem, searchField, regExp);
                    }
                    if (isVisible) {
                        previousItem = item;
                    }
                    return isVisible;
                }).reverse();
            }));
        }), shareReplay({ bufferSize: 1, refCount: false }));
        this.visibleItemList$ = this.filteredItemList$.pipe(combineLatestWith(this.refreshVisibleItemList$), map(([items]) => {
            let isOdd = false;
            let hideDepth = undefined;
            return items.filter(item => {
                if (hideDepth !== undefined && hideDepth <= item.depth) {
                    // hidden by parent
                    return false;
                }
                if (item.isVisible ?? true) {
                    if (item.collapsed) {
                        // hide all children
                        hideDepth = item.depth + 1;
                    }
                    else {
                        // Clear children invisibility
                        hideDepth = undefined;
                    }
                    if (item.items === undefined) {
                        // child
                        isOdd = !isOdd;
                    }
                    else {
                        // parent
                        isOdd = false;
                    }
                    item.odd = isOdd;
                    return true;
                }
                else {
                    // hide all children
                    hideDepth = item.depth + 1;
                    return false;
                }
            });
        }), shareReplay({ bufferSize: 1, refCount: false }));
        this.selectedItems$ = this.visibleItemList$.pipe(combineLatestWith(this.valueField$, this.refreshSelection$), withLatestFrom(this.flatItemList$), filter(([_, flatItemList]) => flatItemList?.length > 0), switchMap(([[visibleItemList, valueField, refreshSelection], flatItemList]) => {
            const select = refreshSelection.selectItems;
            const unselect = refreshSelection.unselectItems;
            const toggle = refreshSelection.toggle;
            const checkSelectable = refreshSelection.checkSelectable;
            const selectParents = refreshSelection.selectParents;
            const selectModels = refreshSelection.selectModels;
            const selectValues = refreshSelection.selectValues;
            delete refreshSelection.selectItems;
            delete refreshSelection.unselectItems;
            delete refreshSelection.toggle;
            delete refreshSelection.checkSelectable;
            delete refreshSelection.selectParents;
            delete refreshSelection.selectModels;
            delete refreshSelection.selectValues;
            let itemsToChange;
            if (unselect) {
                const list = unselect === 'all' ? flatItemList : unselect;
                itemsToChange = list.filter(item => {
                    item.selecting = item.selected ? false : undefined;
                    return !(item.selecting ?? true);
                });
            }
            else {
                itemsToChange = new Array();
            }
            if (select && visibleItemList?.length > 0) {
                if (select === 'all') {
                    itemsToChange = visibleItemList.filter(item => {
                        item.selecting = ((selectParents || !item.items) && (!checkSelectable || item.isSelectable)) || item.selecting;
                        return item.selecting !== undefined;
                    });
                }
                else if (select?.length) {
                    itemsToChange = [...itemsToChange, ...select.filter(item => {
                            item.selecting = (!checkSelectable || item.isSelectable) || item.selecting;
                            return item.selecting !== undefined;
                        })];
                }
            }
            if (selectModels && visibleItemList?.length > 0) {
                itemsToChange = [...itemsToChange, ...visibleItemList.filter(item => {
                        item.selecting = ((!checkSelectable || item.isSelectable) && selectModels.some(model => this.compareModels(model, item.model, valueField))) || item.selecting;
                        return item.selecting !== undefined;
                    })];
            }
            if (selectValues && visibleItemList?.length > 0) {
                itemsToChange = [...itemsToChange, ...visibleItemList.filter(item => {
                        item.selecting = ((!checkSelectable || item.isSelectable) && selectValues.some(value => value === item.id)) || item.selecting;
                        return item.selecting !== undefined;
                    })];
            }
            if (toggle) {
                toggle.forEach(item => {
                    item.selecting = !item.selected;
                    itemsToChange.push(item);
                });
            }
            const itemsToSelect = itemsToChange.filter(item => item.selecting);
            const itemsToUnselect = itemsToChange.filter(item => !item.selecting);
            const selecting$ = itemsToSelect.length && this.selectingItems ? this.selectingItems(itemsToSelect) : of(itemsToSelect);
            return selecting$.pipe(map(selectable => selectable?.forEach(item => item.selected = true)), switchMap(() => itemsToUnselect.length && this.unSelectingItems ? this.unSelectingItems(itemsToUnselect) : of(itemsToUnselect)), map(unselectable => {
                unselectable?.forEach(item => item.selected = false);
                itemsToChange.forEach(item => delete item.selecting);
                return visibleItemList.filter(item => item.selected);
            }));
        }), startWith(new Array()), shareReplay({ bufferSize: 1, refCount: false }));
    }
    /** Map une structure de modèles en items
     * @param mods  Modèles à évaluer.
     * @param valueField (optional) Champs à traiter comme valeur.
     * @param textField (optional) Champs à traiter comme text.
     * @param childrenField (optional) Champs à traiter comme enfants.
     * @return Structure mapée
     */
    mapToItem(mods, valueField, textField, childrenField) {
        return mods.map(model => {
            const item = new Item();
            item.model = model;
            if (typeof model === 'string') {
                item.id = model;
                item.label = model;
            }
            else {
                const label = this.extractValueField(model, textField);
                if (label !== undefined) {
                    item.label = label;
                }
                const id = this.extractValueField(model, valueField);
                if (id !== undefined) {
                    item.id = id;
                }
                if (childrenField) {
                    const children = this.extractValueField(model, childrenField);
                    if (children && children instanceof Array) {
                        item.items = this.mapToItem(children, valueField, textField, childrenField);
                    }
                }
            }
            return item;
        });
    }
    /** Désélectionne tous les éléments sélectionnés */
    unselectAll() {
        this.refreshSelection$.next({ unselectItems: 'all' });
    }
    /** Sélectionne tous les éléments */
    selectAll(checkSelectable) {
        this.refreshSelection$.next({ selectItems: 'all', checkSelectable });
    }
    /** Déselectionne l'élément spécifié
     * @param item Elément à déselectionner.
     */
    unSelectItem(item) {
        this.refreshSelection$.next({ unselectItems: [item] });
    }
    /** Sélectionne l'élément spécifié
     * @param item Elément à sélectionner.
     */
    selectItem(item) {
        this.refreshSelection$.next({ selectItems: [item] });
    }
    /** Déselectionne les éléments spécifiés
     * @param items Liste des éléments à désélectionner.
     */
    unSelectItems(items) {
        this.refreshSelection$.next({ unselectItems: items });
    }
    /** Sélectionne les éléments spécifiés
     * @param items Liste des éléments à sélectionner.
     */
    selectItems(items) {
        this.refreshSelection$.next({ selectItems: items });
    }
    /** Set la selection sur les éléments spécifiés
     * @param items Liste des éléments à sélectionner.
     */
    setSelectedItems(items) {
        this.refreshSelection$.next({ unselectItems: 'all', selectItems: items });
    }
    /** Set la selection sur les éléments spécifiés
     * @param items Liste des modèles des éléments à sélectionner.
     */
    setSelectedModels(models) {
        this.refreshSelection$.next({ unselectItems: 'all', selectModels: models });
    }
    /** Set la selection sur les ids des éléments spécifiés
     * @param values Liste des ids des éléments à sélectionner.
     */
    setSelectedValues(values) {
        this.refreshSelection$.next({ unselectItems: 'all', selectValues: values });
    }
    /** Change l'état de sélection de l'élément spécifié.
     * @param items Liste des éléments à modifier.
     */
    toggleSelect(items) {
        this.refreshSelection$.next({ toggle: items });
    }
    /** Renvoie l'index de l'élément sur la liste plate corespondant à l'élément HTML spécifié
     * @return Index sur la liste plate corespondant à l'élément HTML
     */
    getItemIndexFromHtmlElement(element) {
        // eslint-disable-next-line no-loops/no-loops
        while (element?.parentElement && element.hasAttribute && !element.hasAttribute('flat') && element.parentElement.tagName !== 'BODY') {
            element = element.parentElement;
        }
        if (!element || !element.hasAttribute('flat')) {
            return undefined;
        }
        return +element.getAttribute('flat');
    }
    /** Retourne une valeur indiquant si l'élément spécifié correspond aux critères de recherche spécifiés
     * @param item Elément à analyser.
     * @param searchField Nom du champ à utiliser pour la recherche. Le champ représenté peut-être une valeur ou une function.
     * @param regExp Expression de test sur le champs spécifié.
     * @return True si l'élément correspond aux critères de recherche.
     */
    itemMatch(item, searchField, regExp) {
        const indexedItem = item;
        const value = (searchField && indexedItem[searchField]) ?? item.label;
        return value && regExp.test(this.diacriticService.remove(value));
    }
    parentItemMatch(item, previousItem, _searchField, _regExp) {
        // parent, visible only if a child is visible
        return previousItem && previousItem.depth === item.depth + 1;
    }
    extractValueField(model, field) {
        const indexedModel = model;
        const fields = field.split('.');
        return fields.reduce((mdl, fld) => mdl[fld], indexedModel);
    }
}
ItemService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.1", ngImport: i0, type: ItemService, deps: [{ token: i1.DiacriticService }], target: i0.ɵɵFactoryTarget.Injectable });
ItemService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.1.1", ngImport: i0, type: ItemService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.1", ngImport: i0, type: ItemService, decorators: [{
            type: Injectable
        }], ctorParameters: function () { return [{ type: i1.DiacriticService }]; } });

/*
 *  @license
 *  Copyright Hôpitaux Universitaires de Genève. All Rights Reserved.
 *
 *  Use of this source code is governed by an Apache-2.0 license that can be
 *  found in the LICENSE file at https://github.com/DSI-HUG/dejajs-components/blob/master/LICENSE
 */
class ItemEvent {
}

/*
 *  @license
 *  Copyright Hôpitaux Universitaires de Genève. All Rights Reserved.
 *
 *  Use of this source code is governed by an Apache-2.0 license that can be
 *  found in the LICENSE file at https://github.com/DSI-HUG/dejajs-components/blob/master/LICENSE
 */
/** Classe de tri d'une liste plate ou hierarchique */
class SortingService {
    /** Trie les éléments de la liste plate spécifiée en fonction du modèle de tri spécifié
     * @param list Liste à trier.
     * @param sortInfos Modèle de tri à appliquer.
     * @return Liste triée.
     */
    sort(list, sortInfo) {
        if (list?.length) {
            const sortInfos = sortInfo instanceof Array ? sortInfo : [sortInfo];
            let i = sortInfos.length;
            // eslint-disable-next-line no-loops/no-loops
            while (--i >= 0) {
                const si = sortInfos[i];
                list = sortBy(list, si.name);
                if (si.order === 'descending') {
                    list = list.reverse();
                }
            }
        }
        return list;
    }
    /** Trie les éléments de la liste hierarchique spécifiée en fonction du modèle de tri spécifié.  Peut être surchargé pour implémenter un tri asynchrone
     * @param tree Liste à trier.
     * @param sortInfos Modèle de tri à appliquer.
     * @param childrenField Champ à utiliser pour la recherche dans les enfants d'un parent.
     * @return Observable résolue par la fonction.
     */
    sortTree(tree, sortInfo, childrenField = 'items') {
        return this.sort(tree, sortInfo).map(child => {
            const indexed = child;
            if (indexed?.[childrenField] && indexed?.[childrenField] instanceof Array) {
                this.sortTree(indexed[childrenField], sortInfo, childrenField);
            }
            return child;
        });
    }
}
SortingService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.1", ngImport: i0, type: SortingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
SortingService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.1.1", ngImport: i0, type: SortingService, providedIn: 'root' });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.1", ngImport: i0, type: SortingService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root'
                }]
        }] });

/*
 *  @license
 *  Copyright Hôpitaux Universitaires de Genève. All Rights Reserved.
 *
 *  Use of this source code is governed by an Apache-2.0 license that can be
 *  found in the LICENSE file at https://github.com/DSI-HUG/dejajs-components/blob/master/LICENSE
 */
class ItemModule {
}
ItemModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.1", ngImport: i0, type: ItemModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
ItemModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.1.1", ngImport: i0, type: ItemModule, declarations: [ItemComponent], imports: [CommonModule], exports: [ItemComponent] });
ItemModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.1.1", ngImport: i0, type: ItemModule, imports: [CommonModule] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.1", ngImport: i0, type: ItemModule, decorators: [{
            type: NgModule,
            args: [{
                    declarations: [
                        ItemComponent
                    ],
                    exports: [
                        ItemComponent
                    ],
                    imports: [
                        CommonModule
                    ]
                }]
        }] });

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

export { Item, ItemComponent, ItemEvent, ItemModule, ItemService, SortingService };
//# sourceMappingURL=deja-js-component-v2-item-list.mjs.map
