import { EncodeTransformer } from './encoder.js';
import { DecodeTransformer } from './decoder.js';
import type { EncodeOptions, DecodeOptions } from '../options.js';
import { constants } from '../helper/constants.js';

export { UnexpectedEofError as UnexpectedEof } from '../helper/errors.js';
export type { EncodeOptions, DecodeOptions };

/** 编码为 UBJSON */
export function encode(value: unknown, options?: EncodeOptions): ReadableStream<Uint8Array<ArrayBuffer>> {
    if (value == null) {
        return new ReadableStream({
            type: 'bytes',
            start(controller) {
                const data = new Uint8Array([value === null ? constants.NULL : constants.NO_OP]);
                controller.enqueue(data);
                controller.close();
            },
        });
    }
    return new ReadableStream<unknown>({
        start(controller) {
            controller.enqueue(value);
            controller.close();
        },
    }).pipeThrough(encoder(options));
}

/** 编码为 UBJSON */
export function encodeMany(
    value: AsyncIterable<unknown>,
    options?: EncodeOptions,
): ReadableStream<Uint8Array<ArrayBuffer>> {
    return new ReadableStream<unknown>({
        async start(controller) {
            for await (const v of value) {
                if (v == null) controller.enqueue(undefined);
                else controller.enqueue(v);
            }
            controller.close();
        },
    }).pipeThrough(encoder(options));
}

/** 编码为 UBJSON */
export function encoder(options?: EncodeOptions): TransformStream<unknown, Uint8Array<ArrayBuffer>> {
    return new TransformStream(new EncodeTransformer(options));
}

/** 解码 UBJSON */
export async function decode(stream: ReadableStream<BufferSource>, options?: DecodeOptions): Promise<unknown> {
    const s = stream.pipeThrough(decoder(options));
    const r = s.getReader();
    const result = await r.read();
    void r.cancel();
    return result.done ? undefined : result.value;
}

/** 解码 UBJSON */
export async function* decodeMany(
    stream: ReadableStream<BufferSource>,
    options?: DecodeOptions,
): AsyncIterable<unknown> {
    const s = stream.pipeThrough(decoder(options));
    const r = s.getReader();
    for (;;) {
        const { done, value } = await r.read();
        if (done) break;
        yield value;
    }
}

/** 解码 UBJSON */
export function decoder(options?: DecodeOptions): TransformStream<BufferSource, unknown> {
    return new TransformStream(new DecodeTransformer(options));
}
