/*
 * Licensed Materials - Property of Perforce Software, Inc. 
 * © Copyright Perforce Software, Inc. 2014, 2021 
 * © Copyright IBM Corp. 2009, 2014
 * © Copyright ILOG 1996, 2009
 * All Rights Reserved.
 *
 * Note to U.S. Government Users Restricted Rights:
 * The Software and Documentation were developed at private expense and
 * are "Commercial Items" as that term is defined at 48 CFR 2.101,
 * consisting of "Commercial Computer Software" and
 * "Commercial Computer Software Documentation", as such terms are
 * used in 48 CFR 12.212 or 48 CFR 227.7202-1 through 227.7202-4,
 * as applicable.
 */

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

import ilog.views.sdm.IlvSDMModel;
import ilog.views.sdm.model.IlvDefaultSDMModel;
import ilog.views.sdm.util.IlvXMLConnector;
import ilog.views.util.IlvProductUtil;

/**
 * This class generates SDM data files of various sizes (the size is a power of
 * ten).
 */
public class GraphData {
  private String dataDir = "data/";
  private final double horizontalSpacing = 100;
  private final double verticalSpacing = 100;

  private boolean subgraphs;
  private boolean intergraphLinks;
  private int nodeCount;
  private int linkCount;

  /**
   * The main method generates graph files between 10 and 10000 nodes.
   */
  public static void main(String[] args) {
    // This sample uses JViews Diagrammer features. When deploying an
    // application that includes this code, you need to be in possession
    // of a Perforce JViews Diagrammer Deployment license.
    IlvProductUtil.DeploymentLicenseRequired(IlvProductUtil.JViews_Diagrammer_Deployment);

    GraphData data = new GraphData();
    try {
      for (int size = 1; size <= 5; size++) {
        data.generateGraphFile(size, false, false);
        data.generateGraphFile(size, true, false);
        data.generateGraphFile(size, true, true);
      }
    } catch (Exception ex) {
      ex.printStackTrace();
    }
  }

  /**
   * Generates a graph file.
   */
  public void generateGraphFile(int graphSize, boolean subgraphs, boolean intergraphLinks) throws IOException {
    String filename = dataDir + (int) Math.pow(10, graphSize) + (subgraphs ? "s" : "") + (intergraphLinks ? "i" : "")
        + ".xml";

    // System.out.println("Generating " + filename + " ...");

    try {
      new File(dataDir).mkdir();
    } catch (Exception t) {
    }

    IlvSDMModel model = new IlvDefaultSDMModel();

    createGraph(model, graphSize, subgraphs, intergraphLinks);

    IlvXMLConnector xmlConnector = new IlvXMLConnector();
    BufferedWriter writer = new BufferedWriter(new FileWriter(filename));
    xmlConnector.writeXML(model, writer, null, null);

    writer.close();
  }

  /**
   * Creates a graph of a given power of ten, e.g.: power = 2 -> graph of size
   * 100.
   */
  public IlvSDMModel createGraph(IlvSDMModel model, int graphSize, boolean subgraphs, boolean intergraphLinks) {
    this.subgraphs = subgraphs;
    this.intergraphLinks = intergraphLinks;

    double width = Math.pow(6, graphSize) * horizontalSpacing;
    double height = Math.pow(3, graphSize) * verticalSpacing * graphSize;

    nodeCount = 0;
    linkCount = 0;

    createGraph(model, null, graphSize, 0, 0, width, height, "type0");
    return model;
  }

  /**
   * Creates a graph of a given power of ten, e.g.: power = 2 -> graph of size
   * 100. This is the recursive version.
   */
  private Graph createGraph(IlvSDMModel model, Object parent, int power, double x, double y, double width, double height,
      String type) {
    Graph graph = new Graph();

    if (power == 0) {
      Object node = addNode(model, parent, x, y, type);
      graph.in = node;
      graph.out1 = node;
      graph.out2 = node;
      graph.out3 = node;
    } else {
      double w = width / 6;
      double h = height / 3;

      if (subgraphs) {
        parent = addNode(model, parent, x + width / 2, y + height / 2, "subgraph");
      }

      Graph graph0 = createGraph(model, parent, power - 1, x + width / 2, y, w, h, "type0");
      Graph graph1 = createGraph(model, parent, power - 1, x + width / 6, y + h, w, h, "type1");
      Graph graph2 = createGraph(model, parent, power - 1, x + width / 2, y + h, w, h, "type2");
      Graph graph3 = createGraph(model, parent, power - 1, x + width / 2 + 2 * width / 6, y + h, w, h, "type3");
      Graph graph4 = createGraph(model, parent, power - 1, x + width / 12, y + 2 * h, w, h, "type4");
      Graph graph5 = createGraph(model, parent, power - 1, x + 3 * width / 12, y + 2 * h, w, h, "type5");
      Graph graph6 = createGraph(model, parent, power - 1, x + 5 * width / 12, y + 2 * h, w, h, "type6");
      Graph graph7 = createGraph(model, parent, power - 1, x + 7 * width / 12, y + 2 * h, w, h, "type7");
      Graph graph8 = createGraph(model, parent, power - 1, x + 9 * width / 12, y + 2 * h, w, h, "type8");
      Graph graph9 = createGraph(model, parent, power - 1, x + 11 * width / 12, y + 2 * h, w, h, "type9");

      addLink(model, parent, graph0.out1, graph1.in, "type1");
      addLink(model, parent, graph0.out2, graph2.in, "type2");
      addLink(model, parent, graph0.out3, graph3.in, "type3");

      addLink(model, parent, graph1.out1, graph4.in, "type4");
      addLink(model, parent, graph1.out3, graph5.in, "type5");

      addLink(model, parent, graph2.out1, graph6.in, "type6");
      addLink(model, parent, graph2.out3, graph7.in, "type7");

      addLink(model, parent, graph3.out1, graph8.in, "type8");
      addLink(model, parent, graph3.out3, graph9.in, "type9");

      if (!subgraphs || intergraphLinks) {
        graph.in = graph0.in;
        graph.out1 = graph4.out1;
        graph.out2 = graph6.out3;
        graph.out3 = graph9.out3;
      } else {
        graph.in = parent;
        graph.out1 = parent;
        graph.out2 = parent;
        graph.out3 = parent;
      }
    }

    return graph;
  }

  /**
   * A utility class that represents the input and ouput nodes of a graph.
   */
  private class Graph {
    private Object in;
    private Object out1, out2, out3;
  }

  /**
   * Adds a node.
   */
  private Object addNode(IlvSDMModel model, Object parent, double x, double y, String type) {
    Object node = model.createNode("node");
    model.setID(node, "node" + nodeCount);
    model.setObjectProperty(node, "x", Double.valueOf(x));
    model.setObjectProperty(node, "y", Double.valueOf(y));
    model.setObjectProperty(node, "name", "Node " + nodeCount);
    model.setObjectProperty(node, "type", type);
    model.addObject(node, parent, null);
    nodeCount++;
    return node;
  }

  /**
   * Adds a link.
   */
  private Object addLink(IlvSDMModel model, Object parent, Object from, Object to, String type) {
    Object link = model.createLink("link");
    model.setID(link, "link" + linkCount);
    model.setFrom(link, from);
    model.setTo(link, to);
    model.setObjectProperty(link, "type", type);
    model.addObject(link, parent, null);
    linkCount++;
    return link;
  }

  /**
   * Return the number of nodes in the last created graph.
   */
  public int getNodeCount() {
    return nodeCount;
  }

  /**
   * Return the number of links in the last created graph.
   */
  public int getLinkCount() {
    return linkCount;
  }
}