/* This program uses code from "Algorithms in C, Third Edition,"
 *               by Robert Sedgewick, Addison-Wesley, 1998.
 */

#include <stdlib.h>

#include "list_interface.h"
#include "twoD_arrays.h"
#include "graphs.h"


// a graph where edges are recorded as a linked list for each vertex.
struct struct_graph
{
   int number_of_vertices;
   list * adjacencies;
};


// creates a graph with the specified number of vertices
graph newGraph(int number)
{
   graph result = malloc(sizeof(*result));
   result->number_of_vertices = number;
   result->adjacencies = malloc(number * sizeof(list));
   
   // initialize adjacencies to signify that at present the
   // graph contains no edges.
   int i;
   for (i = 0; i < number; i++)
   {
      result->adjacencies[i] = newList();
   }
   
   return result;
}

// deallocates the memory allocated to graph g.
void destroyGraph(graph g)
{
   if (g == NULL) return;
   
   int i;
   for (i = 0; i < g->number_of_vertices; i++)
   {
      destroyList(g->adjacencies[i]);
   }
   free(g->adjacencies);
   free(g);
}

// returns the number of vertices in g.
int numVertices(graph g)
{
   if (g == NULL) return 0;
   return g->number_of_vertices;
}

// returns the list of neighbors of vertex v in graph g.
list vertexNeighbors(graph g, int v)
{
   if (g == NULL) return NULL;
   if ((v < 0) || (v >= g->number_of_vertices))
   {
      return NULL;
   }
   list result = listDeepCopy(g->adjacencies[v]);
   return result;
}

// returns 1 if the specified edge exists, 0 otherwise.
int edgeExists(graph g, int v1, int v2)
{
   if (g == NULL) return 0;
   if ((v1 < 0) || (v1 >= g->number_of_vertices))
   {
      return 0;
   }
   if ((v2 < 0) || (v2 >= g->number_of_vertices))
   {
      return 0;
   }

   link n;
   for (n = listFirst(g->adjacencies[v1]); n != NULL; n = linkNext(n))
   {
      if (linkItem(n) == v2)
      {
         return 1;
      }
   }
   
   return 0;
}

// connects vertices v1 and v2 in the graph.
// returns 1 if successful, 0 if errors occurred.
int addEdge(graph g, int v1, int v2)
{
   if (g == NULL) return 0;
   if ((v1 < 0) || (v1 >= g->number_of_vertices))
   {
      return 0;
   }
   if ((v2 < 0) || (v2 >= g->number_of_vertices))
   {
      return 0;
   }
   
   if (edgeExists(g, v1, v2))
   {
      return 1;
   }
   
   insertAtBeginning(g->adjacencies[v1], newLink(v2));
   if (v1 != v2)
   {
      insertAtBeginning(g->adjacencies[v2], newLink(v1));
   }

   return 1;
}

// disconnects vertices v1 and v2 in the graph.
// returns 1 if successful, 0 if errors occurred.
int removeEdge(graph g, int v1, int v2)
{
   if (g == NULL) return 0;
   if ((v1 < 0) || (v1 >= g->number_of_vertices))
   {
      return 0;
   }
   if ((v2 < 0) || (v2 >= g->number_of_vertices))
   {
      return 0;
   }
   
   link n;
   link previous = NULL;
   link to_delete;

   for (n = listFirst(g->adjacencies[v1]); n != NULL; n = linkNext(n))
   {
      if (linkItem(n) == v2)
      {
         if (previous == NULL)
         {
            to_delete = deleteFirst(g->adjacencies[v1]);
         }
         else
         {
            to_delete = deleteNext(g->adjacencies[v1], previous);
         }
         free(to_delete);
         break;
      }
      previous = n;
   }
   
   previous = NULL;
   for (n = listFirst(g->adjacencies[v2]); n != NULL; n = linkNext(n))
   {
      if (linkItem(n) == v1)
      {
         if (previous == NULL)
         {
            to_delete = deleteFirst(g->adjacencies[v2]);
         }
         else
         {
            to_delete = deleteNext(g->adjacencies[v2], previous);
         }
         free(to_delete);
         break;
      }
      previous = n;
   }
   
   return 1;
}


// Prints the graph, namely the vertices, and the neighbors of each vertex.
void printGraph(graph g)
{
   if (g == NULL) 
   {
      printf("\nNULL graph\n\n");
      return;
   }
   printf("\n");
   int v, n;
   for (v = 0; v < g->number_of_vertices; v++)
   {
      printNeighbors(g, v);
   }
   printf("\n");
}

// Prints the neighbors of vertex v.
void printNeighbors(graph g, int v)
{
   if (g == NULL) 
   {
      printf("\nNULL graph\n\n");
      return;
   }
   if ((v < 0) || (v >= g->number_of_vertices))
   {
      printf("%d is not a vertex of the graph\n", v);
      return;
   }

   list neighbors = vertexNeighbors(g, v);
   printf("Vertex %d. Neighbors: ", v);
   link n;
   for (n = listFirst(neighbors); n != NULL; n = linkNext(n))
   {
      printf("%d ", linkItem(n));
   }
   printf("\n");
   destroyList(neighbors);
}
