import { Inject, Injectable, Optional } from '@angular/core';
import { LOGGER } from '@anglr/common';
import { EditorHotkeys } from '@anglr/dynamic';
import { extend, generateId, isBlank } from '@jscrpt/common';
import { Subject, Subscription } from 'rxjs';
import * as i0 from "@angular/core";
import * as i1 from "@anglr/dynamic";
/**
 * Class used for handling layout metadata
 */
export class LayoutEditorMetadataManager {
    //######################### public properties #########################
    /**
     * Gets id of selected component
     */
    get selectedComponent() {
        return this._selectedComponent;
    }
    /**
     * Gets id of highlighted component
     */
    get highlightedComponent() {
        return this._highlightedComponent;
    }
    /**
     * Gets id of dragged over component
     */
    get draggedOverComponent() {
        return this._draggedOverComponent;
    }
    /**
     * Gets tree root for component tree
     */
    get root() {
        if (isBlank(this.rootComponentId)) {
            return null;
        }
        return this.components[this.rootComponentId];
    }
    /**
     * Occurs when layout changes
     */
    get layoutChange() {
        return this.layoutChangeSubject.asObservable();
    }
    /**
     * Occurs when selected component changes
     */
    get selectedChange() {
        return this._selectedChange.asObservable();
    }
    /**
     * Occurs when highlighted component changes
     */
    get highlightedChange() {
        return this._highlightedChange.asObservable();
    }
    /**
     * Occurs when display name of component changes
     */
    get displayNameChange() {
        return this._displayNameChanges.asObservable();
    }
    /**
     * Occurs when dragged over component changes
     */
    get draggedOverComponentChange() {
        return this._draggedOverComponentChange.asObservable();
    }
    /**
     * Gets flattened tree of components tree
     */
    get flatTree() {
        return (this._flatTree ?? (this._flatTree = this._buildFlatTree()));
    }
    //######################### constructor #########################
    constructor(_editorHotkeys, _logger) {
        this._editorHotkeys = _editorHotkeys;
        this._logger = _logger;
        //######################### protected fields #########################
        /**
         * Subscriptions created during initialization
         */
        this.initSubscriptions = new Subscription();
        /**
         * Array of all registered layout designer components
         */
        this.components = {};
        /**
         * Id of root component
         */
        this.rootComponentId = null;
        /**
         * Id of selected component
         */
        this._selectedComponent = null;
        /**
         * Id of highlighted component
         */
        this._highlightedComponent = null;
        /**
         * Used for emitting layout changes
         */
        this.layoutChangeSubject = new Subject();
        /**
         * Used for emitting selected component changes
         */
        this._selectedChange = new Subject();
        /**
         * Used for emitting highlighted component changes
         */
        this._highlightedChange = new Subject();
        /**
         * Used for emitting changes in components display name
         */
        this._displayNameChanges = new Subject();
        /**
         * Flattened tree of components tree
         */
        this._flatTree = null;
        /**
         * Id of dragged over component
         */
        this._draggedOverComponent = null;
        /**
         * Used for emitting dragged over component changes
         */
        this._draggedOverComponentChange = new Subject();
        this.initSubscriptions.add(this._editorHotkeys.delete.subscribe(() => {
            if (!this.selectedComponent) {
                return;
            }
            const component = this.components[this.selectedComponent];
            if (!component?.parent) {
                return;
            }
            component.parent.component.removeDescendant(this.selectedComponent);
            component.parent.component.invalidateVisuals();
        }));
        this.initSubscriptions.add(this._editorHotkeys.copy.subscribe(() => {
            if (!this.selectedComponent) {
                return;
            }
            const component = this.components[this.selectedComponent];
            this.metadataClipboard = component.component.options?.typeMetadata;
        }));
        this.initSubscriptions.add(this._editorHotkeys.cut.subscribe(() => {
            if (!this.selectedComponent) {
                return;
            }
            const component = this.components[this.selectedComponent];
            if (!component?.parent) {
                return;
            }
            this.metadataClipboard = component.component.options?.typeMetadata;
            component.parent.component.removeDescendant(this.selectedComponent);
            component.parent.component.invalidateVisuals();
        }));
        this.initSubscriptions.add(this._editorHotkeys.paste.subscribe(() => {
            if (!this.selectedComponent || !this.metadataClipboard) {
                return;
            }
            const component = this.components[this.selectedComponent];
            const newId = `${this.metadataClipboard.name}-${generateId(12)}`;
            if (component.component.canDrop) {
                component.component.addDescendant({
                    index: 0,
                    metadata: extend({}, this.metadataClipboard, {
                        id: newId,
                        displayName: newId,
                    }),
                    parentId: null,
                });
            }
            else if (component.parent?.component.canDrop) {
                component.parent.component.addDescendant({
                    index: component.component.index + 1,
                    metadata: extend({}, this.metadataClipboard, {
                        id: newId,
                        displayName: newId,
                    }),
                    parentId: null,
                });
            }
        }));
    }
    //######################### public methods - implementation of OnDestroy #########################
    /**
     * Called when component is destroyed
     */
    ngOnDestroy() {
        this.initSubscriptions.unsubscribe();
    }
    //######################### public methods #########################
    /**
     * Marks component as selected
     * @param id - Id of component that will be marked as selected
     */
    selectComponent(id) {
        this._selectedComponent = id ?? null;
        this._selectedChange.next();
    }
    /**
     * Removes selection of component
     */
    unselectComponent() {
        this._selectedComponent = null;
        this._selectedChange.next();
    }
    /**
     * Marks component as highlighted
     * @param id - Id of component that will be marked as selected
     */
    highlightComponent(id) {
        this._highlightedComponent = id ?? null;
        this._highlightedChange.next();
    }
    /**
     * Removes highlight of component
     */
    cancelHighlightedComponent() {
        this._highlightedComponent = null;
        this._highlightedChange.next();
    }
    /**
     * Mark component as being dragged over
     * @param id - Id of component that will be marked
     */
    dragOverComponent(id) {
        if (id === this._draggedOverComponent) {
            return;
        }
        this._draggedOverComponent = id ?? null;
        this._draggedOverComponentChange.next();
    }
    /**
     * Removes indication of component being dragged over
     * @param id - Id of component that will be marked
     */
    cancelDragOverComponent() {
        this._draggedOverComponent = null;
        this._draggedOverComponentChange.next();
    }
    /**
     * Registers layout designer component and returns true if component was registered successfuly, otherwise false
     * @param component - Component instance that is being registered
     * @param id - Id of registered component
     * @param parentId - Id of parent that holds this component
     */
    registerLayoutDesignerComponent(component, id, parentId) {
        if (isBlank(parentId)) {
            this.rootComponentId = id;
        }
        //already exists
        if (this.components[id]) {
            this._logger?.error(`LayoutEditorMetadataManager: Component with id ${id} is already registered!`);
            return false;
        }
        const parent = parentId ? this.components[parentId] : null;
        const componentItem = {
            component,
            parent,
            children: []
        };
        this.components[id] = componentItem;
        //insert into parent at the end
        if (parent) {
            parent.children.push(componentItem);
        }
        this._flatTree = null;
        this.layoutChangeSubject.next();
        this._logger?.debug('LayoutEditorMetadataManager: Registering component {{@id}}', { id: id });
        return true;
    }
    /**
     * Gets component from designer component tree
     * @param id - Id of component to be get
     */
    getComponent(id) {
        return this.components[id]?.component ?? null;
    }
    /**
     * Gets parent of component by id
     * @param id - Id of component which parent will be get
     */
    getParent(id) {
        return this.components[id]?.parent?.component ?? null;
    }
    /**
     * Gets index of componet in its parent, if not parent or id not found null is returned
     * @param id - Id of component which index from parent will be obtained
     */
    getIndex(id) {
        const item = this.components[id];
        if (!item || !item.parent) {
            return null;
        }
        return item.parent.children.indexOf(item);
    }
    /**
     * Gets component definition with information about component tree
     * @param id - Id of component definition
     */
    getComponentDef(id) {
        return this.components[id] ?? null;
    }
    /**
     * Unregisters layout designer component
     * @param id - Id of component that will be unregistered
     */
    unregisterLayoutDesignerComponent(id) {
        const componentItem = this.components[id];
        delete this.components[id];
        //unregister from parent
        if (componentItem?.parent) {
            const index = componentItem.parent.children.indexOf(componentItem);
            componentItem.parent.children.splice(index, 1);
        }
        if (id === this.rootComponentId) {
            this.rootComponentId = null;
        }
        this._flatTree = null;
        this.layoutChangeSubject.next();
        this._logger?.debug('LayoutEditorMetadataManager: Unregistering component {{@id}}', { id: id });
    }
    /**
     * Fires event indicating that display name of any component has changed
     */
    displayNameUpdated() {
        this._displayNameChanges.next();
    }
    /**
     * Gets layout component metadata which is using this service
     */
    getMetadata() {
        if (isBlank(this.rootComponentId) || !this.components[this.rootComponentId]) {
            return null;
        }
        return this.components[this.rootComponentId].component.options?.typeMetadata ?? null;
    }
    //######################### protected methods #########################
    /**
     * Builds flattened tree of components tree
     */
    _buildFlatTree() {
        if (isBlank(this.rootComponentId)) {
            return [];
        }
        const component = this.components[this.rootComponentId];
        if (!component) {
            return [];
        }
        return this._buildFlatTreeForComponent(component);
    }
    /**
     * Builds flattened tree of components tree
     * @param component - Component which tree should be flattened
     */
    _buildFlatTreeForComponent(component) {
        let result = [component];
        for (const child of component.children) {
            result = result.concat(this._buildFlatTreeForComponent(child));
        }
        return result;
    }
}
LayoutEditorMetadataManager.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.4", ngImport: i0, type: LayoutEditorMetadataManager, deps: [{ token: i1.EditorHotkeys }, { token: LOGGER, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
LayoutEditorMetadataManager.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.4", ngImport: i0, type: LayoutEditorMetadataManager });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.4", ngImport: i0, type: LayoutEditorMetadataManager, decorators: [{
            type: Injectable
        }], ctorParameters: () => [{ type: i1.EditorHotkeys }, { type: undefined, decorators: [{
                    type: Inject,
                    args: [LOGGER]
                }, {
                    type: Optional
                }] }] });
//# sourceMappingURL=layoutEditorMetadataManager.service.js.map