/**
 * Copyright (c) 2020-present, Goldman Sachs
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { isNonNullable, returnUndefOnError, addUniqueEntry, } from '@finos/legend-shared';
import { DIRECTORY_PATH_DELIMITER, } from '@finos/legend-graph';
export const GENERATION_FILE_ROOT_NAME = 'GENERATION_FILE_ROOT';
class FileSystemElement {
    name;
    directory;
    parentId;
    constructor(name, fileGenerationParent) {
        this.name = name;
        this.parentId = fileGenerationParent;
    }
    get path() {
        if (!this.directory) {
            return this.name;
        }
        const parentDirectoryName = this.directory.getDirectoryPath();
        return !parentDirectoryName
            ? this.name
            : `${parentDirectoryName}${DIRECTORY_PATH_DELIMITER}${this.name}`;
    }
}
export class FileSystem_Directory extends FileSystemElement {
    children = [];
    setDirectory(val) {
        this.directory = val;
    }
    addChild(val) {
        addUniqueEntry(this.children, val);
    }
    addElement(val) {
        this.addChild(val);
        val.directory = this;
    }
    static createDirectoryFromParent(name, parent, genParent) {
        const newDirectory = new FileSystem_Directory(name, genParent);
        newDirectory.setDirectory(parent);
        return newDirectory;
    }
    static getOrCreateDirectory(parent, directoryName, insert) {
        const index = directoryName.indexOf(DIRECTORY_PATH_DELIMITER);
        const str = index === -1 ? directoryName : directoryName.substring(0, index);
        let node;
        node = parent.children.find((child) => child instanceof FileSystem_Directory && child.name === str);
        if (!node) {
            if (!insert) {
                throw new Error(`Can't find file node '${str}' in directory '${directoryName}'`);
            }
            // create the node if it is not in parent directory
            node = FileSystem_Directory.createDirectoryFromParent(str, parent);
            parent.addChild(node);
        }
        if (index !== -1) {
            return FileSystem_Directory.getOrCreateDirectory(node, directoryName.substring(index + DIRECTORY_PATH_DELIMITER.length), insert);
        }
        return node;
    }
    getDirectoryPath() {
        if (!this.directory) {
            return '';
        }
        const parentDirectoryName = this.directory.getDirectoryPath();
        return !parentDirectoryName
            ? this.name
            : `${parentDirectoryName}${DIRECTORY_PATH_DELIMITER}${this.name}`;
    }
}
export class FileSystem_File extends FileSystemElement {
    content;
    format;
    constructor(name, content, format, parentId) {
        super(name, parentId);
        this.content = content;
        this.format = format;
    }
}
export const getFileSystemTreeNodeData = (fileNode) => ({
    id: fileNode.path,
    label: fileNode.name,
    childrenIds: fileNode instanceof FileSystem_Directory
        ? fileNode.children.map((child) => child.path)
        : undefined,
    fileNode: fileNode,
});
export const populateDirectoryTreeNodeChildren = (node, treeData) => {
    if (node.childrenIds && node.fileNode instanceof FileSystem_Directory) {
        node.childrenIds = node.fileNode.children.map((child) => child.path);
        node.fileNode.children
            .map((child) => getFileSystemTreeNodeData(child))
            .forEach((childNode) => {
            const currentNode = treeData.nodes.get(childNode.id);
            if (currentNode) {
                currentNode.childrenIds = childNode.childrenIds;
                currentNode.label = childNode.label;
            }
            else {
                treeData.nodes.set(childNode.id, childNode);
            }
        });
    }
};
export const getFileSystemTreeData = (dir, rootWrapperName) => {
    const rootIds = [];
    const nodes = new Map();
    if (rootWrapperName) {
        const rootNode = getFileSystemTreeNodeData(dir);
        rootNode.label = rootWrapperName;
        addUniqueEntry(rootIds, rootNode.id);
        nodes.set(rootNode.id, rootNode);
    }
    else {
        dir.children
            .toSorted((a, b) => a.name.localeCompare(b.name))
            .sort((a, b) => (b instanceof FileSystem_Directory ? 1 : 0) -
            (a instanceof FileSystem_Directory ? 1 : 0))
            .forEach((childDirectory) => {
            const childTreeNodeData = getFileSystemTreeNodeData(childDirectory);
            addUniqueEntry(rootIds, childTreeNodeData.id);
            nodes.set(childTreeNodeData.id, childTreeNodeData);
        });
    }
    return { rootIds, nodes };
};
export const addNode = (element, treeData, showRoot) => {
    const newNode = getFileSystemTreeNodeData(element);
    treeData.nodes.set(newNode.id, newNode);
    if (!element.directory ||
        (element.directory.path === GENERATION_FILE_ROOT_NAME && !showRoot)) {
        treeData.rootIds = Array.from(new Set(treeData.rootIds).add(newNode.id));
    }
    else {
        const parentNode = treeData.nodes.get(element.directory.path);
        if (parentNode) {
            parentNode.childrenIds = parentNode.childrenIds
                ? Array.from(new Set(parentNode.childrenIds).add(newNode.id))
                : [newNode.id];
        }
    }
    return newNode;
};
export const openNode = (element, treeData, showRoot) => {
    let currentElement = element;
    let openingNode;
    while (currentElement.directory) {
        const node = treeData.nodes.get(currentElement.path) ??
            addNode(currentElement, treeData, showRoot);
        node.isOpen = currentElement instanceof FileSystem_Directory;
        openingNode = !openingNode ? node : openingNode;
        currentElement = currentElement.directory;
    }
    return openingNode;
};
export const getFileSystemChildNodes = (node, treeData) => {
    if (node.childrenIds && node.fileNode instanceof FileSystem_Directory) {
        populateDirectoryTreeNodeChildren(node, treeData);
        return node.childrenIds
            .map((id) => treeData.nodes.get(id))
            .filter(isNonNullable)
            .sort((a, b) => a.label.localeCompare(b.label))
            .sort((a, b) => (b.fileNode instanceof FileSystem_Directory ? 1 : 0) -
            (a.fileNode instanceof FileSystem_Directory ? 1 : 0));
    }
    return [];
};
export const buildFileSystemDirectory = (rootDirectory, filesResultIndex, filesIndex) => {
    Array.from(filesResultIndex.values()).forEach((fileResult) => {
        const resultValue = fileResult.value;
        const filePath = resultValue.fileName;
        const index = filePath.lastIndexOf(DIRECTORY_PATH_DELIMITER);
        const fileName = index === -1
            ? filePath
            : filePath.substring(index + DIRECTORY_PATH_DELIMITER.length, filePath.length);
        const directoryName = index === -1 ? undefined : filePath.substring(0, index);
        let directory = rootDirectory;
        if (directoryName) {
            directory = FileSystem_Directory.getOrCreateDirectory(rootDirectory, directoryName, true);
        }
        const file = new FileSystem_File(fileName, resultValue.content, fileResult.value.format, fileResult.parentId);
        directory.addElement(file);
        filesIndex.set(filePath, file);
    });
};
const openNodeById = (id, treeData) => {
    if (treeData) {
        const node = treeData.nodes.get(id);
        if (node) {
            node.isOpen = true;
        }
    }
};
export const reprocessOpenNodes = (treeData, filesIndex, rootDirectory, openedNodeIds, showRoot) => {
    const openNodeElement = (elementPath) => {
        const element = filesIndex.get(elementPath) ??
            returnUndefOnError(() => FileSystem_Directory.getOrCreateDirectory(rootDirectory, elementPath, false));
        if (element) {
            return openNode(element, treeData, showRoot);
        }
        return undefined;
    };
    openedNodeIds.forEach(openNodeElement);
    if (openedNodeIds.includes(rootDirectory.path)) {
        openNodeById(rootDirectory.path, treeData);
    }
};
//# sourceMappingURL=FileSystemTreeUtils.js.map