#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include "graph.hh"
extern "C" {
#include "bliss_C.h"
}

struct bliss_graph_struct {
  bliss::Graph *g;
};

extern "C"
BlissGraph *bliss_new(const unsigned int n)
{
  BlissGraph *graph = new bliss_graph_struct;
  assert(graph);
  graph->g = new bliss::Graph(n);
  assert(graph->g);
  return graph;
}

extern "C"
BlissGraph *bliss_read_dimacs(FILE *fp)
{
  bliss::Graph *g = bliss::Graph::read_dimacs(fp);
  if(!g)
    return 0;
  BlissGraph *graph = new bliss_graph_struct;
  assert(graph);
  graph->g = g;
  return graph;
}

extern "C"
void bliss_write_dimacs(BlissGraph *graph, FILE *fp)
{
  assert(graph);
  assert(graph->g);
  graph->g->write_dimacs(fp);
}

extern "C"
void bliss_release(BlissGraph *graph)
{
  assert(graph);
  assert(graph->g);
  delete graph->g; graph->g = 0;
  delete graph;
}

extern "C"
void bliss_write_dot(BlissGraph *graph, FILE *fp)
{
  assert(graph);
  assert(graph->g);
  graph->g->write_dot(fp);
}

extern "C"
unsigned int bliss_get_nof_vertices(BlissGraph *graph)
{
  assert(graph);
  assert(graph->g);
  return graph->g->get_nof_vertices();
}

extern "C"
unsigned int bliss_add_vertex(BlissGraph *graph, unsigned int l)
{
  assert(graph);
  assert(graph->g);
  return graph->g->add_vertex(l);
}

extern "C"
void bliss_add_edge(BlissGraph *graph, unsigned int v1, unsigned int v2)
{
  assert(graph);
  assert(graph->g);
  graph->g->add_edge(v1, v2);
}

extern "C"
int bliss_cmp(BlissGraph *graph1, BlissGraph *graph2)
{
  assert(graph1);
  assert(graph1->g);
  assert(graph2);
  assert(graph2->g);
  return graph1->g->cmp(graph2->g);
}

extern "C"
unsigned int bliss_hash(BlissGraph *graph)
{
  assert(graph);
  assert(graph->g);
  return graph->g->get_hash();
}

extern "C"
BlissGraph *bliss_permute(BlissGraph *graph, const unsigned int *perm)
{
  assert(graph);
  assert(graph->g);
  assert(graph->g->get_nof_vertices() == 0 || perm);
  BlissGraph *permuted_graph = new bliss_graph_struct;
  assert(permuted_graph);
  permuted_graph->g = graph->g->permute(perm);
  return permuted_graph;
}

extern "C"
void
bliss_find_automorphisms(BlissGraph *graph,
			 void (*hook)(void *user_param,
				      unsigned int n,
				      const unsigned int *aut),
			 void *hook_user_param,
			 BlissStats *stats)
{
  bliss::Stats s;
  assert(graph);
  assert(graph->g);
  graph->g->find_automorphisms(s, hook, hook_user_param);

  if(stats)
    {
      stats->group_size_approx = s.group_size_approx;
      stats->nof_nodes = s.nof_nodes;
      stats->nof_leaf_nodes = s.nof_leaf_nodes;
      stats->nof_bad_nodes = s.nof_bad_nodes;
      stats->nof_canupdates = s.nof_canupdates;
      stats->nof_generators = s.nof_generators;
      stats->max_level = s.max_level;
    }
}


extern "C"
const unsigned int *
bliss_find_canonical_labeling(BlissGraph *graph,
			      void (*hook)(void *user_param,
					   unsigned int n,
					   const unsigned int *aut),
			      void *hook_user_param,
			      BlissStats *stats)
{
  bliss::Stats s;
  const unsigned int *canonical_labeling = 0;
  assert(graph);
  assert(graph->g);
  
  canonical_labeling = graph->g->canonical_form(s, hook, hook_user_param);

  if(stats)
    {
      stats->group_size_approx = s.group_size_approx;
      stats->nof_nodes = s.nof_nodes;
      stats->nof_leaf_nodes = s.nof_leaf_nodes;
      stats->nof_bad_nodes = s.nof_bad_nodes;
      stats->nof_canupdates = s.nof_canupdates;
      stats->nof_generators = s.nof_generators;
      stats->max_level = s.max_level;
    }

  return canonical_labeling;
}
