import { ReadableStream, TransformStream, } from 'node:stream/web';
export function stringStream(data, size) {
    return new ReadableStream(new StringStreamController(data, size));
}
export function fileStream(fd, size) {
    return new ReadableStream(new FileStreamController(fd, size));
}
export function concatStream(streams) {
    return new ReadableStream(new ConcatStreamController(streams));
}
export function transformStream(readable, transformer) {
    return readable.pipeThrough(new TransformStream(transformer));
}
class StringStreamController {
    chunkSize;
    type = 'bytes';
    index = 0;
    data;
    constructor(data, chunkSize = 1024 * 64) {
        this.chunkSize = chunkSize;
        this.data = new TextEncoder().encode(data);
    }
    async pull(controller) {
        const bytesLeft = this.data.length - this.index;
        if (bytesLeft <= 0) {
            controller.close();
            return;
        }
        const size = Math.min(this.chunkSize, bytesLeft);
        const slice = this.data.slice(this.index, this.index + size);
        controller.enqueue(slice);
        this.index += size;
    }
}
class FileStreamController {
    fd;
    chunkSize;
    type = 'bytes';
    index = 0;
    constructor(fd, chunkSize = 1024 * 64) {
        this.fd = fd;
        this.chunkSize = chunkSize;
    }
    async pull(controller) {
        const stat = await this.fd.stat();
        const bytesLeft = stat.size - this.index;
        if (bytesLeft <= 0) {
            this.fd.close();
            controller.close();
            return;
        }
        const size = Math.min(this.chunkSize, bytesLeft);
        const slice = new Uint8Array(size);
        await this.fd.read(slice, 0, size, this.index);
        controller.enqueue(slice);
        this.index += size;
    }
}
class ConcatStreamController {
    streams;
    type = 'bytes';
    index = 0;
    reader = null;
    constructor(streams) {
        this.streams = streams;
        this.nextReader();
    }
    nextReader() {
        const next = this.streams[this.index++];
        this.reader = next && next.getReader();
    }
    async pull(controller) {
        if (!this.reader) {
            return controller.close();
        }
        const data = await this.reader.read();
        if (data.done) {
            this.nextReader();
            return this.pull(controller);
        }
        controller.enqueue(data.value);
    }
}
export class StreamSlicer {
    chunkSize;
    progressCallback;
    partialChunk;
    offset = 0;
    constructor(chunkSize, progressCallback) {
        this.chunkSize = chunkSize;
        this.progressCallback = progressCallback;
        this.partialChunk = new Uint8Array(this.chunkSize); //where partial chunks are saved
    }
    send(buf, controller) {
        controller.enqueue(buf);
        this.partialChunk = new Uint8Array(this.chunkSize);
        this.offset = 0;
    }
    //reslice input into record sized chunks
    transform(chunk, controller) {
        let i = 0;
        if (this.progressCallback) {
            this.progressCallback(chunk.byteLength);
        }
        if (this.offset > 0) {
            const len = Math.min(chunk.byteLength, this.chunkSize - this.offset);
            this.partialChunk.set(chunk.slice(0, len), this.offset);
            this.offset += len;
            i += len;
            if (this.offset === this.chunkSize) {
                this.send(this.partialChunk, controller);
            }
        }
        while (i < chunk.byteLength) {
            const remainingBytes = chunk.byteLength - i;
            if (remainingBytes >= this.chunkSize) {
                const record = chunk.slice(i, i + this.chunkSize);
                i += this.chunkSize;
                this.send(record, controller);
            }
            else {
                const end = chunk.slice(i, i + remainingBytes);
                i += end.byteLength;
                this.partialChunk.set(end);
                this.offset = end.byteLength;
            }
        }
    }
    flush(controller) {
        if (this.offset > 0) {
            controller.enqueue(this.partialChunk.slice(0, this.offset));
        }
    }
}
