import FileDescriptor from "../descriptor/FileDescriptor.js";

export default class FSUtil {
    /**
     * @param {FileSystemDirectoryHandle} rootDir 
     * @param {string} path 
     * @returns {Promise<string | null>}
     */
    static async getFileContent(rootDir, path) {
        const fileHandle = await FSUtil.getFileHandle(rootDir, FSUtil.fixPath(path));
        return await FSUtil.getFileHandleFileContent(fileHandle);
    }

    /**
     * @param {FileSystemFileHandle} handle 
     * @returns {Promise<string>}
     */
    static async getFileHandleFileContent(handle) {
        if (handle == null) {
            return null;
        }

        const file = await handle.getFile();
        return await file.text();
    }

    /**
     * @param {FileSystemDirectoryHandle} rootDir 
     * @param {string} path 
     * @returns {Promise<FileSystemFileHandle | null>}
     */
    static async getFileHandle(rootDir, path) {
        let resultDir = rootDir;
        let outputFile = null;
        const pathes = FSUtil.fixPath(path).split("/");
        let nextDirName = null;

        while (nextDirName = pathes.shift()) {
            let nextDir = null;

            for await (const value of resultDir.values()) {
                if (value.kind == 'directory' && value.name == nextDirName) {
                    nextDir = value;
                } else if (value.kind == 'file' && value.name == nextDirName) {
                    outputFile = value;
                }
            }

            if (nextDir != null) {
                resultDir = nextDir;
            }
        }

        return outputFile;
    }

    /**
     * @param {FileSystemDirectoryHandle} rootDir 
     * @param {string} path 
     * @returns {Promise<FileSystemDirectoryHandle | null>}
     */
    static async getDirectoryHandle(curDir, path) {
        let resultDir = curDir;
        const pathes = FSUtil.fixPath(path).split("/");
        let nextDirName = null;

        while (nextDirName = pathes.shift()) {
            let nextDir = null;

            for await (const value of resultDir.values()) {
                if (value.kind == 'directory' && value.name == nextDirName) {
                    nextDir = value;
                }
            }

            if (nextDir != null) {
                resultDir = nextDir;
            }
        }

        return resultDir;
    }

    /**
     * @param {string} path 
     * @returns {string}
     */
    static fixPath(path) {
        let fixedPath = path.replaceAll("/./", "/"); //remove same folder subpathes
        fixedPath = fixedPath.replaceAll("//", "/"); //remove double slashes

        if (fixedPath.startsWith("/")) {
            fixedPath = fixedPath.substring(1);
        }

        const pathParts = fixedPath.split("/");
        for (let i = pathParts.length - 1; i > 0; i--) {
            if (pathParts[i] == "..") {
                pathParts.splice(i - 1, 2);
                i--;
            }
        }

        return pathParts.join("/");
    }

    /**
     * @param {FileSystemDirectoryHandle} dir 
     * @param {string} fileName 
     * @param {boolean} includeSubDirs
     * @returns {Promise<FileDescriptor>}
     */
    static async findFile(dir, fileName, includeSubDirs = true) {
        const files = await FSUtil.#findFilesInTree(dir, fileName, includeSubDirs, true);
        return files.length > 0 ? files[0] : null;
    }

    /**
     * @param {FileSystemDirectoryHandle} dir 
     * @param {string} fileName 
     * @param {boolean} includeSubDirs
     * @returns {Promise<FileDescriptor[]>}
     */
    static async findFiles(dir, fileName, includeSubDirs = true) {
        return FSUtil.#findFilesInTree(dir, fileName, includeSubDirs, false);;
    }

    /**
     * @param {FileSystemDirectoryHandle} dir 
     * @param {string} fileName 
     * @param {boolean} includeSubDirs
     * @param {boolean} stopOnFirst
     * @returns {Promise<FileDescriptor[]>}
     */
    static async #findFilesInTree(dir, fileName, includeSubDirs = true, stopOnFirst = false, path = "") {
        const files = [];
        const subdirs = [];

        for await (const child of dir.values()) {
            if (child.kind == 'directory' && includeSubDirs) {
                subdirs.push(child);
            } else if (child.kind == 'file' && child.name == fileName) {
                files.push(new FileDescriptor(path, child));
                if(stopOnFirst){
                    return files;
                }
            }
        }

        for (let i = 0; i < subdirs.length; i++) {
            files.push(...(await FSUtil.#findFilesInTree(subdirs[i], fileName, includeSubDirs, stopOnFirst, path + "/" + subdirs[i].name)));
            if(stopOnFirst && files.length > 0){
                break;
            }
        }

        return files;
    }
}