import { Queue } from 'queue-typescript';
// import {Assert} from '../utils/assert'
import { Edge } from './edge';
import { Node } from './node';
import { NodeCollection } from './nodeCollection';
export class Graph extends Node {
    constructor(id = '__graph__') {
        super(id);
        this.isCollapsed = false;
        this.nodeCollection = new NodeCollection();
    }
    noEmptySubgraphs() {
        for (const g of this.subgraphs()) {
            if (g.shallowNodeCount == 0)
                return false;
        }
        return true;
    }
    hasSubgraphs() {
        for (const n of this.shallowNodes)
            if (n.isGraph)
                return true;
        return false;
    }
    *subgraphs() {
        for (const n of this.deepNodes) {
            if (n instanceof Graph)
                yield n;
        }
    }
    isEmpty() {
        return this.shallowNodeCount == 0;
    }
    setEdge(sourceId, targetId) {
        const s = this.nodeCollection.find(sourceId);
        if (s == null)
            return;
        const t = this.nodeCollection.find(targetId);
        if (t == null)
            return;
        const e = new Edge(s, t);
        this.addEdge(e);
        return e;
    }
    get shallowNodes() {
        return this.nodeCollection.nodesShallow;
    }
    get deepNodes() {
        return this.nodeCollection.nodesDeep();
    }
    findNode(id) {
        return this.nodeCollection.find(id);
    }
    get edges() {
        return this.nodeCollection.edges;
    }
    *deepEdges() {
        for (const node of this.deepNodes) {
            for (const e of node.outEdges) {
                yield e;
            }
            for (const e of node.selfEdges) {
                yield e;
            }
        }
    }
    isConsistent() {
        return this.nodeCollection.isConsistent();
    }
    nodeIsConsistent(n) {
        return this.nodeCollection.nodeIsConsistent(n);
    }
    removeNode(n) {
        this.nodeCollection.removeNode(n);
    }
    addNode(n) {
        /*Assert.assert(n.parent == null || n.parent == this)*/
        n.parent = this;
        this.nodeCollection.addNode(n);
        return n;
    }
    addEdge(n) {
        this.nodeCollection.addEdge(n);
    }
    get shallowNodeCount() {
        return this.nodeCollection.nodeShallowCount;
    }
    get nodeCountDeep() {
        return this.nodeCollection.nodeDeepCount;
    }
    get edgeCount() {
        return this.nodeCollection.edgeCount;
    }
    // If n has the graph as the parent then return n,
    // otherwise set n = n.parent and repeat.
    // Return null if the node parent is above the graph.
    liftNode(n) {
        while (n != null && n.parent != this) {
            n = n.parent;
        }
        return n;
    }
}
export function* shallowConnectedComponents(graph) {
    const enqueueed = new Set();
    const queue = new Queue();
    for (const n of graph.shallowNodes) {
        if (enqueueed.has(n))
            continue;
        const nodes = new Array();
        enqueue(n, queue, enqueueed);
        while (queue.length > 0) {
            const s = queue.dequeue();
            nodes.push(s);
            for (const neighbor of neighbors(s)) {
                enqueue(neighbor, queue, enqueueed);
            }
        }
        yield nodes;
    }
    function* neighbors(n) {
        for (const e of n.outEdges)
            yield e.target;
        for (const e of n.inEdges)
            yield e.source;
    }
    function enqueue(n, queue, enqueueed) {
        if (!enqueueed.has(n)) {
            queue.enqueue(n);
            enqueueed.add(n);
        }
    }
}
//# sourceMappingURL=graph.js.map