import LineBuilder from "../codegen/LineBuilder.js";
import { EdgeDirectionType } from "./Edge.js";
import { graphviz_escape_string } from "./format/graphviz/graphviz_escape_string.js";

/**
 * @template T
 * @param {T} node
 * @return {string}
 */
function defaultNodeToDot(node) {
    return `[label="${graphviz_escape_string(node.toString())}"]`;
}

/**
 * Build a Graphviz DOT representation of the graph
 * @template Node
 * @param {Graph<Node>} graph
 * @param {function(Node):string} nodeToDot
 * @returns {string}
 */
export function convert_graph_to_dot_string({
                                                graph,
                                                nodeToDot = defaultNodeToDot
                                            }) {
    const lb = new LineBuilder();

    lb.add('strict digraph Graph {');

    lb.indent();

    const nodes = Array.from(graph.getNodes());
    const nodeCount = nodes.length;

    function nodeId(index) {
        return `n${index}`;
    }

    for (let i = 0; i < nodeCount; i++) {

        const node = nodes[i];

        lb.add(`${nodeId(i)} ${nodeToDot(node)};`);

    }

    //add edges
    for (let i = 0; i < nodeCount; i++) {

        const node = nodes[i];

        const attachedEdges = [];

        const nAttachedEdges = graph.getAttachedEdges(attachedEdges, node);

        for (let j = 0; j < nAttachedEdges; j++) {
            const edge = attachedEdges[j];

            const direction = edge.direction;
            if ((direction === EdgeDirectionType.Forward || direction === EdgeDirectionType.Undirected) && edge.first === node) {
                const id0 = nodeId(i);

                const index1 = nodes.indexOf(edge.second);

                const id1 = nodeId(index1);

                const attributes = [];

                if (direction === EdgeDirectionType.Undirected) {
                    attributes.push('dir="both"');
                }


                lb.add(`${id0} -> ${id1}${attributes.length === 0 ? '' : ` [${attributes.join(' ')}]`};`);
            }
        }
    }

    lb.dedent();

    lb.add('}');

    return lb.build();
}
