"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ObjectTreeService = void 0;
const tslib_1 = require("tslib");
const core_1 = require("@loopback/core");
const lodash_1 = require("lodash");
const object_tree_model_1 = require("../../models/object-tree.model");
const application_service_1 = require("../application.service");
const content_entity_service_1 = require("../content-entity/content-entity.service");
const object_node_service_1 = require("../object-node/object-node.service");
const object_type_service_1 = require("../object-type.service");
const application_error_1 = require("./../../helper/application-error");
const entity_name_1 = require("./../../models/entity-name");
const application_service_2 = require("./../application.service");
const object_tree_const_1 = require("./object-tree.const");
let ObjectTreeService = class ObjectTreeService {
    /*
    private init: ObjectTreeInit;*/
    constructor(objectNodeService, objectTypeService, contentEntityService, appCtx) {
        this.objectNodeService = objectNodeService;
        this.objectTypeService = objectTypeService;
        this.contentEntityService = contentEntityService;
        this.appCtx = appCtx;
        this.appCtx.entityActions['ObjectTreeController.prototype.findChildren'] = {
            entityName: entity_name_1.EntityName.objectTree,
            entityActionType: application_service_2.EntityActionType.read,
        };
        this.appCtx.entityActions['ObjectTreeController.prototype.findOwnerTree'] = {
            entityName: entity_name_1.EntityName.objectTree,
            entityActionType: application_service_2.EntityActionType.read,
        };
        this.appCtx.entityActions['ObjectTreeController.prototype.findTree'] = {
            entityName: entity_name_1.EntityName.objectTree,
            entityActionType: application_service_2.EntityActionType.read,
        };
        this.appCtx.entityActions['ObjectTreeController.prototype.findNamespaceTree'] = {
            entityName: entity_name_1.EntityName.objectTree,
            entityActionType: application_service_2.EntityActionType.read,
        };
        this.appCtx.entityActions['ObjectTreeController.prototype.findNode'] = {
            entityName: entity_name_1.EntityName.objectTree,
            entityActionType: application_service_2.EntityActionType.read,
        };
    }
    get ready() {
        return this.appCtx.getExtensionContext(object_tree_const_1.OBJECT_TREE_PROVIDER).ready;
    }
    async createNewApplicationSubTree(parentNode, treeNodeName, treeNodeTypeId, tree, ctx) {
        const treeNode = await this.objectNodeService.add(lodash_1.merge(tree.treeNode, {
            name: treeNodeName,
            objectTypeId: treeNodeTypeId,
            parentNodeId: parentNode.id,
        }), application_service_1.CurrentContext.get(ctx, {
            nodeContext: { parent: new application_service_1.ExpectedValue(parentNode) },
        }), false, false);
        for (const childTypeId in tree.children) {
            for (const childName in tree.children[childTypeId]) {
                for (const childTree of tree.children[childTypeId][childName]) {
                    await this.createNewApplicationSubTree(treeNode, childName, childTypeId, childTree, ctx);
                }
            }
        }
        return treeNode;
    }
    async updateApplicationSubTree(treeNode, tree, ctx) {
        if (0 < Object.keys(tree.treeNode).length) {
            treeNode = await this.objectNodeService.modifyById(treeNode.id, tree.treeNode, application_service_1.CurrentContext.get(ctx, {
                nodeContext: { node: new application_service_1.ExpectedValue(treeNode) },
            }));
        }
        for (const childTypeId in tree.children) {
            for (const childName in tree.children[childTypeId]) {
                const existingChildren = await this.objectNodeService.searchByParentNodeId(treeNode.id, { objectTypeId: childTypeId, name: childName });
                let index = 0;
                while (index < tree.children[childTypeId][childName].length &&
                    index < existingChildren.length) {
                    await this.updateApplicationSubTree(existingChildren[index], tree.children[childTypeId][childName][index], ctx);
                    index++;
                }
                while (index < tree.children[childTypeId][childName].length) {
                    await this.createNewApplicationSubTree(treeNode, childName, childTypeId, tree.children[childTypeId][childName][index], ctx);
                    index++;
                }
                while (index < existingChildren.length) {
                    await this.objectNodeService.removeById(existingChildren[index].id, application_service_1.CurrentContext.get(ctx, {
                        nodeContext: { node: new application_service_1.ExpectedValue(existingChildren[index]) },
                    }));
                    index++;
                }
            }
        }
        return treeNode;
    }
    async registerApplicationTree(parentNode, treeNodeName, treeNodeTypeId, tree, reset, ctx = new application_service_1.CurrentContext()) {
        if (!parentNode) {
            throw application_error_1.ApplicationError.notFound({
                treeNodeName,
                treeNodeTypeId,
                missing: 'parentNode',
            });
        }
        const treeNodes = await this.objectNodeService.searchByParentNodeId(parentNode.id, { objectTypeId: treeNodeTypeId, name: treeNodeName });
        if (!treeNodes || 0 === treeNodes.length) {
            return this.createNewApplicationSubTree(parentNode, treeNodeName, treeNodeTypeId, tree, ctx);
        }
        if (1 < treeNodes.length) {
            throw application_error_1.ApplicationError.corruptedData({
                treeNodeName,
                treeNodeTypeId,
                'treeNode.length': treeNodes.length,
            });
        }
        if (reset) {
            await this.objectNodeService.removeById(treeNodes[0].id, application_service_1.CurrentContext.get(ctx, {
                nodeContext: {
                    node: new application_service_1.ExpectedValue(treeNodes[0]),
                    parent: new application_service_1.ExpectedValue(parentNode),
                },
            }));
            return this.createNewApplicationSubTree(parentNode, treeNodeName, treeNodeTypeId, tree, ctx);
        }
        return this.updateApplicationSubTree(treeNodes[0], tree, ctx);
    }
    /*
     * Add service methods here
     */
    async getOwnerTreeNodes(ownerType, ownerName, ctx) {
        await this.ready;
        const tree = await ctx.treeContext.treeNode.getOrSetValue(async () => {
            return this.objectNodeService.searchOwner(ownerType, ownerName);
        });
        if (!tree) {
            throw application_error_1.ApplicationError.notFound({ owner: ownerType, ownerName: ownerName });
        }
        return lodash_1.concat(tree, await this.objectNodeService.searchByParentTreeId(tree.id));
    }
    async buildTreeFromNodes(treeNode, availableNodes, ctx) {
        return new object_tree_model_1.ObjectTree(treeNode).init(availableNodes);
    }
    async getOwnerTree(ownerType, ownerName, ctx) {
        const objectNodes = await this.getOwnerTreeNodes(ownerType, ownerName, ctx);
        return this.buildTreeFromNodes(objectNodes[0], objectNodes, ctx);
    }
    async getTree(ownerType, ownerName, namespaceType, namespaceName, treeType, treeName, ctx) {
        const objectNodes = await this.objectNodeService.getTreeNodes(ownerType, ownerName, namespaceType, namespaceName, treeType, treeName, ctx);
        return this.buildTreeFromNodes(objectNodes[0], objectNodes, ctx);
    }
    async getNode(ownerType, ownerName, namespaceType, namespaceName, treeType, treeName, nodeType, nodeName, ctx) {
        const objectNodes = await this.objectNodeService.getTreeNodes(ownerType, ownerName, namespaceType, namespaceName, treeType, treeName, ctx);
        const types = await this.objectTypeService.getImplementingTypes(nodeType);
        const treeNode = lodash_1.find(objectNodes, (node) => node.name === nodeName && -1 < lodash_1.indexOf(types, node.objectTypeId));
        if (!treeNode) {
            throw application_error_1.ApplicationError.notFound({
                node: nodeType,
                nodeName: nodeName,
                tree: treeType,
                treeName: treeName,
                namespace: namespaceType,
                namespaceName: namespaceName,
                owner: ownerType,
                ownerName: ownerName,
            });
        }
        return this.buildTreeFromNodes(treeNode, objectNodes, ctx);
    }
    async getNamespaceNodes(ownerType, ownerName, namespaceType, namespaceName, ctx) {
        await this.ready;
        const namespace = await ctx.treeContext.treeNode.getOrSetValue(async () => {
            return this.objectNodeService.searchNamespace(ownerType, ownerName, namespaceType, namespaceName);
        });
        if (!namespace) {
            throw application_error_1.ApplicationError.notFound({
                namespace: namespaceType,
                namespaceName: namespaceName,
                owner: ownerType,
                ownerName: ownerName,
            });
        }
        return lodash_1.concat(namespace, await this.objectNodeService.searchByParentTreeId(namespace.id));
    }
    async getNamespaceTree(ownerType, ownerName, namespaceType, namespaceName, ctx) {
        const objectNodes = await this.getNamespaceNodes(ownerType, ownerName, namespaceType, namespaceName, ctx);
        return this.buildTreeFromNodes(objectNodes[0], objectNodes, ctx);
    }
    async getChildrenByImplementedTypeId(tree) {
        const result = {};
        for (const child of tree.children) {
            const implementedTypes = await this.objectTypeService.getImplementedTypes(child.treeNode.objectTypeId);
            for (const implementedType of implementedTypes) {
                if (!(implementedType in result)) {
                    result[implementedType] = [];
                }
                result[implementedType].push(child);
            }
        }
        return result;
    }
    async loadChildrenNodes(treeId, ctx) {
        const root = await ctx.treeContext.treeNode.getOrSetValue(async () => {
            return this.objectNodeService.searchById(treeId);
        });
        if (!root) {
            throw application_error_1.ApplicationError.notFound({
                tree: treeId,
            });
        }
        return this.objectNodeService.loadChildrenNodes(root);
    }
    async loadTree(treeId, ctx) {
        const objectNodes = await this.loadChildrenNodes(treeId, ctx);
        return this.buildTreeFromNodes(objectNodes[0], objectNodes, ctx);
    }
    isInTree(tree, node) {
        if (node.id === tree.treeNode.id) {
            return true;
        }
        for (const child of tree.children) {
            if (this.isInTree(child, node)) {
                return true;
            }
        }
        return false;
    }
    getChildOfType(tree, objectTypeId) {
        for (const child of tree.children) {
            if (child.treeNode.objectTypeId === objectTypeId) {
                return child;
            }
        }
        return null;
    }
};
ObjectTreeService = tslib_1.__decorate([
    tslib_1.__param(0, core_1.service(object_node_service_1.ObjectNodeService)),
    tslib_1.__param(1, core_1.service(object_type_service_1.ObjectTypeService)),
    tslib_1.__param(2, core_1.service(content_entity_service_1.ContentEntityService)),
    tslib_1.__param(3, core_1.service(application_service_1.ApplicationService)),
    tslib_1.__metadata("design:paramtypes", [object_node_service_1.ObjectNodeService,
        object_type_service_1.ObjectTypeService,
        content_entity_service_1.ContentEntityService,
        application_service_1.ApplicationService])
], ObjectTreeService);
exports.ObjectTreeService = ObjectTreeService;
//# sourceMappingURL=object-tree.service.js.map