import { Element } from './Element';
import { Edge } from './Edge';
import { Node } from './Node';

export class Graph extends Element {
  public children: Array<Graph> = [];
  public nodes: Array<Node> = [];
  public is_root = false;
  public edges: Array<Edge> = [];

  // eslint-disable-next-line @typescript-eslint/naming-convention
  public toString (level = 0): string {
    const header = this.parent
      ? `subgraph cluster_${this.full_name}`
      : `digraph ${this.full_name}`;
    let children = `\n  ${this.children.map ((c) => c.toString (level + 1))
      .join ('\n  ')}\n`;
    let nodes = `\n  ${this.nodes.map ((c) => c.toString ())
      .join ('\n  ')}\n`;
    let edges = `\n  ${this.edges.map ((c) => c.toString ())
      .join ('\n  ')}\n`;

    if (children === '\n  \n')
      children = '';
    if (nodes === '\n  \n')
      nodes = '';
    if (edges === '\n  \n')
      edges = '';

    return `${header} {${children}${nodes}${edges}}`
      .replace (/\n/gu, `\n${'  '.repeat (level)}`)
      .replace (/^\s+$/gmu, '');
  }

  public add_node (constructor: () => Node): void {
    const node = constructor ();
    node.parent = this.full_name;
    this.nodes.push (node);
  }

  public add_graph (constructor: () => Graph): void {
    const graph = constructor ();
    graph.parent = this.full_name;
    graph.update_parent ();
    this.children.push (graph);
  }

  public update_parent (): void {
    for (const node of this.nodes)
      node.parent = this.full_name;

    for (const graph of this.children) {
      graph.parent = this.full_name;
      graph.update_parent ();
    }
  }

  public add_edge (origin: string, target: string): void {
    this.edges.push (
      new Edge (`${this.full_name}_${origin}`, `${this.full_name}_${target}`)
    );
  }
}