"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Dir = void 0;
const util_1 = require("./util");
const Dirent_1 = require("./Dirent");
const errors = require("../internal/errors");
/**
 * A directory stream, like `fs.Dir`.
 */
class Dir {
    constructor(link, options) {
        this.link = link;
        this.options = options;
        this.iteratorInfo = [];
        this.closed = false;
        this.operationQueue = null;
        this.path = link.getPath();
        this.iteratorInfo.push(link.children[Symbol.iterator]());
    }
    closeBase() {
        // In a real filesystem implementation, this would close file descriptors
        // For memfs, we just need to mark as closed
    }
    readBase(iteratorInfo) {
        let done;
        let value;
        let name;
        let link;
        do {
            do {
                ({ done, value } = iteratorInfo[iteratorInfo.length - 1].next());
                if (!done) {
                    [name, link] = value;
                }
                else {
                    break;
                }
            } while (name === '.' || name === '..');
            if (done) {
                iteratorInfo.pop();
                if (iteratorInfo.length === 0) {
                    break;
                }
                else {
                    done = false;
                }
            }
            else {
                if (this.options.recursive && link.children.size) {
                    iteratorInfo.push(link.children[Symbol.iterator]());
                }
                return Dirent_1.default.build(link, this.options.encoding);
            }
        } while (!done);
        return null;
    }
    close(callback) {
        // Promise-based close
        if (callback === undefined) {
            if (this.closed) {
                return Promise.reject(new errors.Error('ERR_DIR_CLOSED'));
            }
            return new Promise((resolve, reject) => {
                this.close(err => {
                    if (err)
                        reject(err);
                    else
                        resolve();
                });
            });
        }
        // Callback-based close
        (0, util_1.validateCallback)(callback);
        if (this.closed) {
            process.nextTick(callback, new errors.Error('ERR_DIR_CLOSED'));
            return;
        }
        if (this.operationQueue !== null) {
            this.operationQueue.push(() => {
                this.close(callback);
            });
            return;
        }
        this.closed = true;
        try {
            this.closeBase();
            process.nextTick(callback);
        }
        catch (err) {
            process.nextTick(callback, err);
        }
    }
    closeSync() {
        if (this.closed) {
            throw new errors.Error('ERR_DIR_CLOSED');
        }
        if (this.operationQueue !== null) {
            throw new errors.Error('ERR_DIR_CONCURRENT_OPERATION');
        }
        this.closed = true;
        this.closeBase();
    }
    read(callback) {
        // Promise-based read
        if (callback === undefined) {
            return new Promise((resolve, reject) => {
                this.read((err, result) => {
                    if (err)
                        reject(err);
                    else
                        resolve(result !== null && result !== void 0 ? result : null);
                });
            });
        }
        // Callback-based read
        (0, util_1.validateCallback)(callback);
        if (this.closed) {
            process.nextTick(callback, new errors.Error('ERR_DIR_CLOSED'));
            return;
        }
        if (this.operationQueue !== null) {
            this.operationQueue.push(() => {
                this.read(callback);
            });
            return;
        }
        this.operationQueue = [];
        try {
            const result = this.readBase(this.iteratorInfo);
            process.nextTick(() => {
                const queue = this.operationQueue;
                this.operationQueue = null;
                for (const op of queue)
                    op();
                callback(null, result);
            });
        }
        catch (err) {
            process.nextTick(() => {
                const queue = this.operationQueue;
                this.operationQueue = null;
                for (const op of queue)
                    op();
                callback(err);
            });
        }
    }
    readSync() {
        if (this.closed) {
            throw new errors.Error('ERR_DIR_CLOSED');
        }
        if (this.operationQueue !== null) {
            throw new errors.Error('ERR_DIR_CONCURRENT_OPERATION');
        }
        return this.readBase(this.iteratorInfo);
    }
    [Symbol.asyncIterator]() {
        return {
            next: async () => {
                try {
                    const dirEnt = await this.read();
                    if (dirEnt !== null) {
                        return { done: false, value: dirEnt };
                    }
                    else {
                        return { done: true, value: undefined };
                    }
                }
                catch (err) {
                    throw err;
                }
            },
            [Symbol.asyncIterator]() {
                return this;
            },
        };
    }
}
exports.Dir = Dir;
//# sourceMappingURL=Dir.js.map