import { inject, Inject, Injectable, Injector, Optional } from '@angular/core';
import { LOGGER } from '@anglr/common';
import { generateId } from '@jscrpt/common';
import { RelationsDebugger } from '../relationsDebugger/relationsDebugger.service';
import * as i0 from "@angular/core";
import * as i1 from "../relationsDebugger/relationsDebugger.service";
/**
 * Manager used for managing all components used in relations
 */
export class RelationsComponentManager {
    //######################### constructor #########################
    constructor(logger, relationsDebugger) {
        this.logger = logger;
        this.relationsDebugger = relationsDebugger;
        //######################### protected properties #########################
        /**
         * Id of current scope
         */
        this.scopeId = null;
        /**
         * Registered components
         */
        this.components = {};
        /**
         * Object storing all scopes and their managers
         */
        this.scopes = {};
        /**
         * Instance of parent relations component manager
         */
        this.parent = null;
        /**
         * Cache containing children scoped components
         */
        this.childrenCache = {};
        /**
         * Injector for angular DI
         */
        this.injector = inject(Injector);
    }
    //######################### public methods - implementation of OnDestroy #########################
    /**
     * Called when component is destroyed
     */
    ngOnDestroy() {
        //removes nested scopes first
        if (this.scopes) {
            for (const scopeId in this.scopes) {
                const scope = this.scopes[scopeId];
                if (scope) {
                    for (const manager of scope) {
                        manager.ngOnDestroy();
                    }
                }
                delete this.scopes[scopeId];
            }
        }
        for (const componentId in this.components) {
            this.removeCacheFromHierarchy(componentId);
        }
        //removes self from parent scopes
        if (this.parent && this.scopeId) {
            const scope = this.parent.scopes[this.scopeId];
            if (scope) {
                const index = scope.indexOf(this);
                if (index >= 0) {
                    scope.splice(index, 1);
                }
            }
        }
    }
    //######################### public methods #########################
    /**
     * Registers newly created component
     * @param id - Id of component to be registered
     * @param component - Instance of registered component
     */
    registerComponent(id, component) {
        if (this.components[id]) {
            this.logger?.warn(`RelationsComponentManager: Component with id '${id}' is already registered, provide unique id.`);
            return;
        }
        component.ɵɵRelationsComponentId ?? (component.ɵɵRelationsComponentId = generateId(12));
        this.removeCacheFromHierarchy(id);
        this.components[id] = component;
        component.ɵɵinjector = this.injector;
        if (ngRelationsDebugger) {
            this.relationsDebugger?.registerComponent(id, component);
        }
    }
    /**
     * Unregisters component
     * @param id - Id of component to be unregistered
     */
    unregisterComponent(id) {
        if (!this.components[id]) {
            this.logger?.warn(`RelationsComponentManager: Component with id '${id}' has already been unregistered.`);
            return;
        }
        this.removeCacheFromHierarchy(id);
        const component = this.components[id];
        delete this.components[id];
        if (ngRelationsDebugger) {
            this.relationsDebugger?.unregisterComponent(id, component);
        }
    }
    /**
     * Gets instance of component or array of component instances or null
     * @param id - Unique identification of component, or components
     */
    get(id) {
        const component = this.components[id];
        if (component) {
            return component;
        }
        if (this.childrenCache[id]) {
            return this.childrenCache[id];
        }
        const components = this.getChildrenComponents(id);
        if (components) {
            return components;
        }
        return this.getParentComponents(id);
    }
    /**
     * Gets id of relations component, for this manager
     * @param component - Component which id should be found
     */
    getId(component) {
        for (const id in this.components) {
            const comp = this.components[id];
            if (comp === component) {
                return id;
            }
        }
        return null;
    }
    /**
     * Opens new scope for relations component manager
     * @param id - Id of newly created scope
     */
    openScope(id) {
        var _a;
        (_a = this.scopes)[id] ?? (_a[id] = []);
        const scope = new RelationsComponentManager(this.logger);
        scope.scopeId = id;
        scope.parent = this;
        this.scopes[id].push(scope);
        return scope;
    }
    //######################### protected methods #########################
    /**
     * Removes cached components from hierarchy
     * @param id - Id of component which will be removed from cache
     */
    removeCacheFromHierarchy(id) {
        //no parent or parent cache does not contains id
        if (!this.parent || !this.parent.childrenCache[id]) {
            return;
        }
        delete this.parent.childrenCache[id];
        this.parent.removeCacheFromHierarchy(id);
    }
    /**
     * Gets children components when scopes are used
     * @param id - Id of components to be get
     */
    getChildrenComponents(id) {
        const getFromScope = (scopeId) => {
            const scope = this.scopes[scopeId];
            const result = [];
            for (const mngr of scope) {
                const component = mngr.components[id];
                if (component) {
                    result.push(component);
                    continue;
                }
                const components = mngr.getChildrenComponents(id);
                if (Array.isArray(components)) {
                    for (const cmp of components) {
                        result.push(cmp);
                    }
                    continue;
                }
                return null;
            }
            if (result.length) {
                return result;
            }
            return null;
        };
        for (const scopeId in this.scopes) {
            const result = getFromScope(scopeId);
            if (result) {
                this.childrenCache[id] = result;
                return result;
            }
        }
        return null;
    }
    /**
     * Gets parent components when scopes are used
     * @param id - Id of component to be get
     */
    getParentComponents(id) {
        if (!this.parent) {
            return null;
        }
        const component = this.parent.components[id] ?? null;
        if (component) {
            return component;
        }
        return this.parent.getParentComponents(id);
    }
}
RelationsComponentManager.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.4", ngImport: i0, type: RelationsComponentManager, deps: [{ token: LOGGER, optional: true }, { token: i1.RelationsDebugger, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
RelationsComponentManager.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.4", ngImport: i0, type: RelationsComponentManager });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.4", ngImport: i0, type: RelationsComponentManager, decorators: [{
            type: Injectable
        }], ctorParameters: () => [{ type: undefined, decorators: [{
                    type: Inject,
                    args: [LOGGER]
                }, {
                    type: Optional
                }] }, { type: i1.RelationsDebugger, decorators: [{
                    type: Optional
                }] }] });
//# sourceMappingURL=relationsComponentManager.service.js.map