/*
 * Implementation of the graph class and related classes.
 *
 * Author: John Vilk (jvilk@cs.umass.edu)
 */
#include "Graph.h"
#include "Subgraph.h"
#include "Cluster.h"
#include "Node.h"
#include "Edge.h"

namespace DotWriter {

// 'Tab' is two spaces
const char Graph::_tabCharacter = ' ';
const unsigned Graph::_tabIncrement = 2;

Graph::~Graph() {
  std::vector<Node *>::iterator nodeIt;
  for (nodeIt = _nodes.begin(); nodeIt != _nodes.end(); nodeIt++) {
    delete *nodeIt;
  }

  std::vector<Edge *>::iterator edgeIt;
  for (edgeIt = _edges.begin(); edgeIt != _edges.end(); edgeIt++) {
    delete *edgeIt;
  }

  std::vector<Subgraph *>::iterator sgIt;
  for (sgIt = _subgraphs.begin(); sgIt != _subgraphs.end(); sgIt++) {
    delete *sgIt;
  }

  std::vector<Cluster *>::iterator clusterIt;
  for (clusterIt = _clusters.begin(); clusterIt != _clusters.end(); clusterIt++) {
    delete *clusterIt;
  }
}

Subgraph* Graph::AddSubgraph(const std::string& label) {
  Subgraph* sg = new Subgraph(_idManager->GetSubgraphId(), _idManager,
    IsDigraph(), label);
  _subgraphs.push_back(sg);
  return sg;
}

Subgraph* Graph::AddSubgraph(const std::string& label, const std::string& id) {
  std::string sanitizedId = _idManager->ValidateCustomId(id);
  Subgraph* sg = new Subgraph(sanitizedId, _idManager, IsDigraph(), label);
  _subgraphs.push_back(sg);
  return sg;
}

Subgraph* Graph::AddSubgraph(const HtmlString& label) {
  Subgraph* sg = new Subgraph(_idManager->GetSubgraphId(), _idManager,
    IsDigraph(), label);
  _subgraphs.push_back(sg);
  return sg;
}

Subgraph* Graph::AddSubgraph(const HtmlString& label, const std::string& id) {
  std::string sanitizedId = _idManager->ValidateCustomId(id);
  Subgraph* sg = new Subgraph(sanitizedId, _idManager, IsDigraph(), label);
  _subgraphs.push_back(sg);
  return sg;
}

void Graph::RemoveSubgraph(Subgraph* subgraph) {
  std::vector<Subgraph*>::iterator it = std::find(_subgraphs.begin(),
    _subgraphs.end(), subgraph);

  if (it != _subgraphs.end())
    _subgraphs.erase(it);

  delete subgraph;
}

Cluster* Graph::AddCluster(const std::string& label) {
  Cluster* cluster = new Cluster(_idManager->GetClusterId(), _idManager,
    IsDigraph(), label);
  _clusters.push_back(cluster);
  return cluster;
}

Cluster* Graph::AddCluster(const std::string& label, const std::string& id) {
  std::string sanitizedId = _idManager->ValidateCustomClusterId(id);
  Cluster* cluster = new Cluster(sanitizedId, _idManager, IsDigraph(), label);
  _clusters.push_back(cluster);
  return cluster;
}

Cluster* Graph::AddCluster(const HtmlString& label) {
  Cluster* cluster = new Cluster(_idManager->GetClusterId(), _idManager,
    IsDigraph(), label);
  _clusters.push_back(cluster);
  return cluster;
}

Cluster* Graph::AddCluster(const HtmlString& label, const std::string& id) {
  std::string sanitizedId = _idManager->ValidateCustomClusterId(id);
  Cluster* cluster = new Cluster(sanitizedId, _idManager, IsDigraph(), label);
  _clusters.push_back(cluster);
  return cluster;
}

void Graph::RemoveCluster(Cluster* cluster) {
  std::vector<Cluster*>::iterator it = std::find(_clusters.begin(),
    _clusters.end(), cluster);

  if (it != _clusters.end())
    _clusters.erase(it);

  delete cluster;
}

Node* Graph::AddNode() {
  Node* node = new Node(_idManager->GetNodeId());
  _nodes.push_back(node);
  return node;
}

Node* Graph::AddNode(const std::string& label) {
  Node* node = new Node(_idManager->GetNodeId(), label);
  _nodes.push_back(node);
  return node;
}

Node* Graph::AddNode(const std::string& label, const std::string& id) {
  Node* node = new Node(_idManager->ValidateCustomId(id), label);
  _nodes.push_back(node);
  return node;
}

Node* Graph::AddNode(const HtmlString& label) {
  Node* node = new Node(_idManager->GetNodeId(), label);
  _nodes.push_back(node);
  return node;
}

Node* Graph::AddNode(const HtmlString& label, const std::string& id) {
  Node* node = new Node(_idManager->ValidateCustomId(id), label);
  _nodes.push_back(node);
  return node;
}

void Graph::RemoveNode(Node* node) {
  std::vector<Node*>::iterator it = std::find(_nodes.begin(), _nodes.end(),
    node);

  if (it != _nodes.end()) {
    _nodes.erase(it);
  }

  delete node;
}

Edge* Graph::AddEdge(Node* src, Node* dst) {
  Edge* edge = new Edge(src, dst);
  _edges.push_back(edge);
  return edge;
}

Edge* Graph::AddEdge(Node* src, Node* dst, const std::string& label) {
  Edge* edge = new Edge(src, dst, label);
  _edges.push_back(edge);
  return edge;
}

Edge* Graph::AddEdge(Node* src, Node* dst, const HtmlString& label) {
  Edge* edge = new Edge(src, dst, label);
  _edges.push_back(edge);
  return edge;
}

void Graph::RemoveEdge(Edge* edge) {
  std::vector<Edge*>::iterator it = std::find(_edges.begin(), _edges.end(),
    edge);

  if (it != _edges.end())
    _edges.erase(it);

  delete edge;
}

void Graph::PrintNECS(std::ostream& out, unsigned tabDepth) {
  std::string linePrefix = std::string(tabDepth*_tabIncrement, _tabCharacter);

  // Default styles.
  if (!_defaultNodeAttributes.Empty()) {
    out << linePrefix;
    out << "node [";
    _defaultNodeAttributes.Print(out);
    out << "];\n";
  }

  if (!_defaultEdgeAttributes.Empty()) {
    out << linePrefix;
    out << "edge [";
    _defaultEdgeAttributes.Print(out);
    out << "];\n";
  }

  // Output nodes
  std::vector<Node*>::iterator nodeIt;
  for (nodeIt = _nodes.begin(); nodeIt != _nodes.end(); nodeIt++) {
    out << linePrefix;
    Node* node = *nodeIt;
    node->Print(out);
  }

  // Output subgraphs.
  std::vector<Subgraph*>::iterator sgIt;
  for (sgIt = _subgraphs.begin(); sgIt != _subgraphs.end(); sgIt++) {
    Subgraph* sg = *sgIt;
    sg->Print(out, tabDepth+1);
  }

  // Output cluster subgraphs.
  std::vector<Cluster*>::iterator cIt;
  for (cIt = _clusters.begin(); cIt != _clusters.end(); cIt++) {
    Cluster* cluster = *cIt;
    cluster->Print(out, tabDepth+1);
  }

  // Output edges. We do this *after* subgraphs and clusters, since edges
  // can connect subgraphs.
  std::vector<Edge*>::iterator edgeIt;
  for (edgeIt = _edges.begin(); edgeIt != _edges.end(); edgeIt++) {
    out << linePrefix;
    Edge* edge = *edgeIt;
    edge->Print(IsDigraph(), out);
  }
}

}  // namespace DotWriter
