import { Stream } from '../../stream/Stream.js';
import { cloneMethod } from '../symbols.js';
import { Arbitrary } from './definition/Arbitrary.js';
import { Shrinkable } from './definition/Shrinkable.js';
export class GenericTupleArbitrary extends Arbitrary {
    constructor(arbs) {
        super();
        this.arbs = arbs;
        for (let idx = 0; idx !== arbs.length; ++idx) {
            const arb = arbs[idx];
            if (arb == null || arb.generate == null)
                throw new Error(`Invalid parameter encountered at index ${idx}: expecting an Arbitrary`);
        }
    }
    static makeItCloneable(vs, shrinkables) {
        vs[cloneMethod] = () => {
            const cloned = [];
            for (let idx = 0; idx !== shrinkables.length; ++idx) {
                cloned.push(shrinkables[idx].value);
            }
            GenericTupleArbitrary.makeItCloneable(cloned, shrinkables);
            return cloned;
        };
        return vs;
    }
    static wrapper(shrinkables) {
        let cloneable = false;
        const vs = [];
        for (let idx = 0; idx !== shrinkables.length; ++idx) {
            const s = shrinkables[idx];
            cloneable = cloneable || s.hasToBeCloned;
            vs.push(s.value);
        }
        if (cloneable) {
            GenericTupleArbitrary.makeItCloneable(vs, shrinkables);
        }
        return new Shrinkable(vs, () => GenericTupleArbitrary.shrinkImpl(shrinkables).map(GenericTupleArbitrary.wrapper));
    }
    generate(mrng) {
        return GenericTupleArbitrary.wrapper(this.arbs.map((a) => a.generate(mrng)));
    }
    static shrinkImpl(value) {
        let s = Stream.nil();
        for (let idx = 0; idx !== value.length; ++idx) {
            s = s.join(value[idx].shrink().map((v) => value
                .slice(0, idx)
                .concat([v])
                .concat(value.slice(idx + 1))));
        }
        return s;
    }
    withBias(freq) {
        return new GenericTupleArbitrary(this.arbs.map((a) => a.withBias(freq)));
    }
}
function genericTuple(arbs) {
    return new GenericTupleArbitrary(arbs);
}
function tuple(...arbs) {
    return new GenericTupleArbitrary(arbs);
}
export { genericTuple, tuple };
