"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.FilterPattern = void 0;
exports.serializeQuads = serializeQuads;
exports.parseQuads = parseQuads;
exports.uniqueQuads = uniqueQuads;
exports.termToInt = termToInt;
exports.solveBgp = solveBgp;
exports.getAppliedBindings = getAppliedBindings;
exports.matchBinding = matchBinding;
const n3_1 = require("n3");
const StreamUtil_1 = require("./StreamUtil");
const TermUtil_1 = require("./TermUtil");
/**
 * Helper function for serializing an array of quads, with as result a Readable object.
 *
 * @param quads - The array of quads.
 * @param contentType - The content-type to serialize to.
 *
 * @returns The Readable object.
 */
function serializeQuads(quads, contentType) {
    return (0, StreamUtil_1.pipeSafely)((0, StreamUtil_1.guardedStreamFrom)(quads), new n3_1.StreamWriter({ format: contentType }));
}
/**
 * Helper function to convert a Readable into an array of quads.
 *
 * @param readable - The readable object.
 * @param options - Options for the parser.
 *
 * @returns A promise containing the array of quads.
 */
async function parseQuads(readable, options = {}) {
    return (0, StreamUtil_1.arrayifyStream)((0, StreamUtil_1.pipeSafely)(readable, new n3_1.StreamParser(options)));
}
/**
 * Filter out duplicate quads from an array.
 *
 * @param quads - Quads to filter.
 *
 * @returns A new array containing the unique quads.
 */
function uniqueQuads(quads) {
    const uniques = [];
    for (const quad of quads) {
        if (!uniques.some((item) => quad.equals(item))) {
            uniques.push(quad);
        }
    }
    return uniques;
}
/**
 * Converts a term to a number. Returns undefined if the term was undefined.
 *
 * @param term - Term to parse.
 * @param radix - Radix to use when parsing. Default is 10.
 */
function termToInt(term, radix = 10) {
    if (term) {
        return Number.parseInt(term.value, radix);
    }
}
/**
 * Represents a triple pattern to be used as a filter.
 */
class FilterPattern {
    subject;
    predicate;
    object;
    /**
     * @param subject - Optionally filter based on a specific subject.
     * @param predicate - Optionally filter based on a predicate.
     * @param object - Optionally filter based on a specific object.
     */
    constructor(subject, predicate, object) {
        this.subject = typeof subject === 'string' ? (0, TermUtil_1.toNamedTerm)(subject) : null;
        this.predicate = typeof predicate === 'string' ? (0, TermUtil_1.toNamedTerm)(predicate) : null;
        this.object = typeof object === 'string' ? (0, TermUtil_1.toNamedTerm)(object) : null;
    }
}
exports.FilterPattern = FilterPattern;
/**
 * Finds the matching bindings in the given data set for the given BGP query.
 *
 * @param bgp - BGP to solve
 * @param data - Dataset to query.
 */
function solveBgp(bgp, data) {
    let result = [{}];
    for (const pattern of bgp) {
        const newResult = [];
        for (const binding of result) {
            newResult.push(...getAppliedBindings(pattern, binding, data));
        }
        result = newResult;
    }
    return result;
}
/**
 * Queries a data store with a pattern to find all resulting bindings.
 * Before matching the pattern with the store,
 * the given binding is applied to the pattern first.
 *
 * The resulting binding includes the given binding.
 *
 * @param pattern - Pattern to match with the data store.
 * @param binding - Pattern to first apply to the given pattern.
 * @param data - Data store to query.
 */
function getAppliedBindings(pattern, binding, data) {
    const result = [];
    const matches = data.getQuads(pattern.subject.termType === 'Variable' ? binding[pattern.subject.value] : pattern.subject, pattern.predicate.termType === 'Variable' ? binding[pattern.predicate.value] : pattern.predicate, pattern.object.termType === 'Variable' ? binding[pattern.object.value] : pattern.object, null);
    for (const match of matches) {
        result.push({
            ...binding,
            ...matchBinding(pattern, match),
        });
    }
    return result;
}
/**
 * Finds the binding necessary to match the given pattern to the given quad.
 * This function assumes it has been verified that the pattern can match the quad.
 *
 * @param pattern - Pattern that can match the quad.
 * @param match - A quad that can be matched by the given pattern.
 */
function matchBinding(pattern, match) {
    const result = {};
    for (const pos of ['subject', 'predicate', 'object']) {
        if (pattern[pos].termType === 'Variable') {
            result[pattern[pos].value] = match[pos];
        }
    }
    return result;
}
//# sourceMappingURL=QuadUtil.js.map