"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.nodeContainsErrors = exports.getIdentifierText = exports.edgeStatementHasAttributes = exports.isNodeId = exports.isSubGraphStatement = exports.isEdgeStatement = exports.isAttrStatement = exports.findAllStatements = exports.findOptionalSemicolons = exports.findAllEdges = exports.getAllowedEdgeOperation = exports.findNodeAtOffset = exports.checkSourceFile = void 0;
const types_js_1 = require("./types.js");
const util_js_1 = require("./service/util.js");
const visitor_js_1 = require("./visitor.js");
function checkSourceFile(file) {
    const g = file.graph;
    if (g) {
        const messages = checkGraphSemantics(file, g);
        if (messages) {
            file.diagnostics.push.apply(file.diagnostics, messages);
        }
    }
}
exports.checkSourceFile = checkSourceFile;
function getNarrowerNode(offset, prev, toCheck) {
    const prevRange = prev.end - prev.pos;
    if (toCheck.pos <= offset && offset <= toCheck.end) {
        let nrange = toCheck.end - toCheck.pos;
        if (nrange < prevRange) {
            return toCheck;
        }
    }
    return prev;
}
function rangeContainsOffset(range, offset, inclusiveEnd) {
    return inclusiveEnd
        ? range.pos <= offset && offset <= range.end
        : range.pos <= offset && offset < range.end;
}
function findNodeAtOffset(root, offset, inclusiveEnd = false) {
    if (root.pos === offset && root.pos === root.end)
        return root;
    if (rangeContainsOffset(root, offset, inclusiveEnd)) {
        const narrowerChild = (0, visitor_js_1.forEachChild)(root, child => findNodeAtOffset(child, offset, inclusiveEnd));
        return narrowerChild ? narrowerChild : root;
    }
    return undefined;
}
exports.findNodeAtOffset = findNodeAtOffset;
function getAllowedEdgeOperation(graph) {
    return graph.kind === types_js_1.SyntaxKind.DirectedGraph
        ? types_js_1.SyntaxKind.DirectedEdgeOp
        : types_js_1.SyntaxKind.UndirectedEdgeOp;
}
exports.getAllowedEdgeOperation = getAllowedEdgeOperation;
function checkGraphSemantics(file, root) {
    const expectedEdgeOp = getAllowedEdgeOperation(root);
    const invalidEdgeRhses = findEdgeErrors(expectedEdgeOp, root);
    return invalidEdgeRhses == undefined || invalidEdgeRhses.length === 0
        ? undefined
        : createEdgeViolationDiagnostics(file, expectedEdgeOp, invalidEdgeRhses);
}
function findAllEdges(node) {
    const allEdges = [];
    (0, visitor_js_1.forEachChild)(node, child => {
        if (isEdgeStatement(child)) {
            if (child.rhs && child.rhs.length > 0) {
                for (const edgeRhs of child.rhs)
                    allEdges.push(edgeRhs);
            }
        }
        const childEdges = findAllEdges(child);
        if (childEdges && childEdges.length > 0)
            allEdges.push.apply(allEdges, childEdges);
    });
    return allEdges;
}
exports.findAllEdges = findAllEdges;
function findOptionalSemicolons(node) {
    const statements = findAllStatements(node);
    const terminators = statements.map(p => p.terminator);
    return terminators.filter(t => !!t);
}
exports.findOptionalSemicolons = findOptionalSemicolons;
function isStatement(node) {
    return node.kind === types_js_1.SyntaxKind.SubGraphStatement
        || node.kind === types_js_1.SyntaxKind.EdgeStatement
        || node.kind === types_js_1.SyntaxKind.NodeStatement
        || node.kind === types_js_1.SyntaxKind.IdEqualsIdStatement
        || node.kind === types_js_1.SyntaxKind.AttributeStatement;
}
function findAllStatements(node, kind) {
    const allStatements = [];
    (0, visitor_js_1.forEachChild)(node, child => {
        if ((kind === undefined && isStatement(child)) || (child.kind === kind)) {
            allStatements.push(child);
        }
        const childStatements = findAllStatements(child, kind);
        if (childStatements && childStatements.length > 0)
            allStatements.push.apply(allStatements, childStatements);
    });
    return allStatements;
}
exports.findAllStatements = findAllStatements;
function findEdgeErrors(expectedEdgeOp, node) {
    const edges = findAllEdges(node);
    const wrongEdges = edges && edges.length > 0
        ? edges.filter(e => e.operation.kind !== expectedEdgeOp)
        : undefined;
    if (wrongEdges && wrongEdges.length > 0) {
        wrongEdges.forEach(e => e.operation.flags |= 2);
        return wrongEdges;
    }
    return undefined;
}
function createEdgeViolationDiagnostics(file, expectedEdgeOp, violators) {
    const op = expectedEdgeOp === types_js_1.SyntaxKind.UndirectedEdgeOp ? "--" : "->";
    const graphType = expectedEdgeOp === types_js_1.SyntaxKind.UndirectedEdgeOp ? "undirected" : "directed";
    const message = `Invalid edge operation, use "${op}" in ${graphType} graph`;
    const code = createCheckerError(0);
    const category = types_js_1.DiagnosticCategory.Error;
    violators.forEach(edge => edge.operation.flags |= 2);
    return violators.map(edge => {
        const start = (0, util_js_1.getStart)(file, edge.operation);
        const end = edge.operation.end;
        return {
            message,
            code,
            category,
            start,
            end,
        };
    });
}
function getInvalidEdgeRhs(allowedOp, edges) {
    const res = [];
    for (const e of edges) {
        if (e.operation.kind !== allowedOp)
            res.push(e);
    }
    return res;
}
function isAttrStatement(node) {
    return node.kind === types_js_1.SyntaxKind.AttributeStatement;
}
exports.isAttrStatement = isAttrStatement;
function isEdgeStatement(node) {
    return node.kind === types_js_1.SyntaxKind.EdgeStatement;
}
exports.isEdgeStatement = isEdgeStatement;
function isSubGraphStatement(node) {
    return node.kind === types_js_1.SyntaxKind.SubGraphStatement;
}
exports.isSubGraphStatement = isSubGraphStatement;
function isGraph(node) {
    return node.kind === types_js_1.SyntaxKind.DirectedGraph || node.kind === types_js_1.SyntaxKind.UndirectedGraph;
}
function isNodeId(node) {
    return node.kind === types_js_1.SyntaxKind.NodeId;
}
exports.isNodeId = isNodeId;
function edgeStatementHasAttributes(es) {
    return es.attributes
        && es.attributes.length > 0
        && es.attributes.some(a => a.assignments && a.assignments.length > 0);
}
exports.edgeStatementHasAttributes = edgeStatementHasAttributes;
function getIdentifierText(n) {
    switch (n.kind) {
        case types_js_1.SyntaxKind.HtmlIdentifier:
            return n.htmlContent;
        case types_js_1.SyntaxKind.TextIdentifier:
            return n.text;
        case types_js_1.SyntaxKind.NumericIdentifier:
            return n.text;
        case types_js_1.SyntaxKind.QuotedTextIdentifier:
            return n.concatenation;
        default:
            return (0, util_js_1.assertNever)(n);
    }
}
exports.getIdentifierText = getIdentifierText;
function createCheckerError(sub) {
    return {
        source: 4,
        sub,
    };
}
function nodeContainsErrors(node) {
    return (node.flags & 2) === 2;
}
exports.nodeContainsErrors = nodeContainsErrors;
//# sourceMappingURL=checker.js.map