/*
 *  graphShell.cpp 
 * 
 *  (c) 2004 Daniel Jackson. 
 *  CIS 11 Data Structures & Algorithms 
 * 

Class Invariant: 

  notDone is true until user chooses quit. Then it is false, and the menu quits. 
  graphExists is true while myGraph points to a graph. is false while myGraph does not point 
  to a graph. Used to track creation and deletion. Also to ensure member functions are not called 
  while there is no graph.

myGraph is a pointer to the graph currently being tested. 

void DoMenu(); 
  Runs a menu for the graph class. Displays the menu, then performs the operation chosen. 

void PrintMenu(); 
  Prints out a menu. 

void DijkstraAlgorithm(std::size_t start); 
  Runs a menu based test of the Dijkstra class, which implements Dijkstra's Algorithm. 

bool noGraph(); 
  Postcondition: Returns true and displays an error message if there is no graph currently. 
  Returrns false if there is a graph. 
  
*/

#include <iostream>    // Provides cout and cin
#include <cctype>      // Provides tolower()
#include <iomanip>     // Provides setw()
#include <string>      // Provides string
#include <fstream>     // Provides ifstream
#include <vector>      // Provides vector
#include "Dijkstra.h"  // Provides Dijkstra 

template <class Item>
graphShell<Item>::graphShell() {
  notDone = true;
  graphExists = false;
  PrintMenu();
  while (notDone)
    DoMenu();
  if (graphExists)
    delete myGraph;
}

template <class Item>
void graphShell<Item>::PrintMenu() {
  std::cout << "Please make a choice:" << std::endl
    << " n - Make a new graph" << std::endl
    << " d - Delete the current graph" << std::endl
    << " p - Print the current graph" << std::endl
    << " l - Load a graph from a file" << std::endl
    << " f - Save the graph to a file" << std::endl
    << "    ** Filename for both is theFile.txt." << std::endl
    << " v - Add a Vertex" << std::endl
    << " e - Add an Edge" << std::endl
    << " x - Remove an Edge" << std::endl
    << " r - Resize the graph" << std::endl
    << " s - Show the size of the graph" << std::endl
    << " b - Display the neighbors" << std::endl
    << " g - Get a vertex" << std::endl
    << " c - Change a vertex" << std::endl
    << " a - Dijkstra's Algorithm" << std::endl
    << " q - Quit" << std::endl
    << " ? - Print this menu again" << std::endl << std::endl;
}

template <class Item>
void graphShell<Item>::DoMenu() {
  char choice;
  int integer;
  std::ifstream infile;
  std::ofstream outfile;
  std::size_t vertices, i, j;
  Item nothing, label;
  std::set<std::size_t> theSet;
  std::set<std::size_t>::iterator theIterator;
  std::cout << "Which choice?  ";
  std::cin >> choice;
  choice = std::tolower(choice);
  switch (choice) {
    case 'n':
      std::cout << "How big do you want the graph's initial capacity (0 or negative for default):  ";
      std::cin >> integer;
      if (graphExists)
        delete myGraph;
      if (integer > 0)
        myGraph = new graph<Item>(integer);
      else
        myGraph = new graph<Item>;
      graphExists = true;
      break;
    case 'd':
      if (noGraph())
        break;
      delete myGraph;
      graphExists = false;
      std::cout << "The graph has been deleted." << std::endl << std::endl;
      break;
    case 'p':
      if (noGraph())
        break;
      for (i = 0; i < myGraph->size(); ++i) {
        std::cout << std::setw(2) << i;
        for (j = 0; j < myGraph->size(); ++j) {
          std::cout << setw(4) << myGraph->EdgeWeight(i, j); }
        std::cout << endl; }
      break;
    case 'l':
      if (noGraph())
        break;
      infile.open("theFile.txt");
      if (infile) {
        infile >> vertices;
        for (i = 0; i < vertices; ++i) {
          myGraph->AddVertex(nothing); }
        for (i = 0; i < vertices; ++i) {
          for (j = 0; j < vertices; ++j) {
            infile >> integer;
            if (integer >= 0)
              myGraph->AddEdge(i, j, integer); } }
        infile.close(); } else
        std::cout << "That file was not able to be opened." << std::endl;
      break;
    case 'f':
      if (noGraph())
        break;
      outfile.open("theFile.txt");
      if (outfile) {
        outfile << myGraph->size() << std::endl;
        for (i = 0; i < myGraph->size(); ++i) {
          for (j = 0; j < myGraph->size(); ++j)
            outfile << myGraph->EdgeWeight(i, j) << " ";
          outfile << std::endl; } } else
        std::cout << "That file was not able to be opened." << std::endl;
      break;
    case 'v':
      if (noGraph())
        break;
      std::cout << "Please enter the label to give the vertex:  ";
      std::cin >> label;
      myGraph->AddVertex(label);
      break;
    case 'e':
      if (noGraph())
        break;
      std::cout << "What is the source vertex?  ";
      std::cin >> i;
      std::cout << "What is the target vertex?  ";
      std::cin >> j;
      std::cout << "What is the weight?  ";
      std::cin >> integer;
      myGraph->AddEdge(i, j, integer);
      break;
    case 'x':
      if (noGraph())
        break;
      std::cout << "What is the source vertex?  ";
      std::cin >> i;
      std::cout << "What is the target vertex?  ";
      std::cin >> j;
      myGraph->RemoveEdge(i, j);
      break;
    case 'r':
      if (noGraph())
        break;
      std::cout << "What is the new size?  ";
      std::cin >> i;
      myGraph->Resize(i);
      break;
    case 's':
      if (noGraph())
        break;
      std::cout << myGraph->size() << std::endl;
      break;
    case 'b':
      if (noGraph())
        break;
      std::cout << "What vertex?  ";
      std::cin >> vertices;
      theSet = myGraph->Neighbors(vertices);
      for (theIterator = theSet.begin(); theIterator != theSet.end(); ++theIterator)
        std::cout << std::setw(4) << *theIterator;
      std::cout << std::endl;
      break;
    case 'g':
      if (noGraph())
        break;
      std::cout << "What vertex?  ";
      std::cin >> vertices;
      label = (*myGraph)[vertices];
      std::cout << label << std::endl;
      break;
    case 'c':
      if (noGraph())
        break;
      std::cout << "What vertex?  ";
      std::cin >> vertices;
      std::cout << "What is the new value?  ";
      std::cin >> label;
      (*myGraph)[vertices] = label;
      break;
    case 'a':
      if (noGraph())
        break;
      std::cout << "What vertex is the start vertex?  ";
      std::cin >> vertices;
      DijkstraAlgorithm(vertices);
      break;
    case 'q':
      notDone = false;
      break;
    case '?':
      PrintMenu();
      break;
    default:
      std::cout << "Try again, that is not a valid choice." << std::endl; }
}

template <class Item>
bool graphShell<Item>::noGraph() {
  if (graphExists)
    return false;
  std::cout << "There is no graph. Create a new one." << std::endl;
  return true;
}

template <class Item>
void graphShell<Item>::DijkstraAlgorithm(std::size_t start) {
  bool notDone = true;
  char choice;
  int* distances = new int[myGraph->size()];
  int dist;
  std::size_t vertex;
  std::size_t* previous = new std::size_t[myGraph->size()];
  std::vector<std::size_t> path;
  std::vector<std::size_t>::iterator theIterator;
  std::size_t i;
  Dijkstra<Item> test(myGraph, start);
  while (notDone) {
    std::cout << std::endl
      << " d - Print the distance array" << std::endl
      << " p - Print the path array" << std::endl
      << " v - Print the distance and path to a vertex" << std::endl
      << " s - Change to a new start vertex" << std::endl
      << " q - quit" << std::endl << std::endl
      << "Enter your choice:  ";
    std::cin >> choice;
    switch (choice) {
      case 'd':
        test.GetDistances(distances);
        for (i = 0; i < myGraph->size(); ++i)
          std::cout << std::setw(6) << distances[i];
        break;
      case 'p':
        test.GetPaths(previous);
        for (i = 0; i < myGraph->size(); ++i)
          std::cout << std::setw(3) << previous[i];
        break;
      case 'v':
        std::cout << "What vertex?  ";
        std::cin >> vertex;
        dist = test.Distance(vertex);
        path = test.Path(vertex);
        if (!path.empty()) {
          std::cout << std::setw(2) << dist;
          for (theIterator = path.begin(); theIterator != path.end(); ++theIterator)
            std::cout << std::setw(3) << *theIterator; } else
          std::cout << "It is impossible to get from the start vertex to that vertex." << std::endl;
        break;
      case 's':
        std::cout << "What vertex?  ";
        std::cin >> vertex;
        test.ChangeStart(vertex);
        break;
      case 'q':
        notDone = false;
        break;
      default:
        std::cout << "I don't understand, try again." << std::endl; } }
  delete [] distances;
  delete [] previous;
}