// readSchoolsCSV.cc  P. Conrad    4/20/04
// read a comma separated values file of schools into an array of structs


#include <iostream>
using std::endl;
using std::cout;
using std::cin;
using std::cerr;

#include <iomanip>
using std::setw;

#include <fstream>
using std::ifstream;

const int shortStringLen = 21; // can hold only 20 chars, + 1 for \0
const int longStringLen = 41; // can hold only 40 chars, + 1 for \0
const int inputLineLen = 81; // can hold 80 chars, +1 for \0

struct School 
{
  char domainName[shortStringLen];
  char fullName[longStringLen];
  char mascot[longStringLen];
  char city[shortStringLen];
  char state[3]; // 2 for abbreviation, + 1 for \0
  int foundingYear;
  School *next;
};




void printSchool(School * p); // print out all fields in a School struct
void printSchoolHeaders(); // prints headers to go with output from printSchool
void parseInputLine(char *inputLine, School *p); // assigns fields in School

int main(int argc, char **argv)
{

  School *head = NULL; // head pointer
  School *tail = NULL; // tail pointer
  School *p = NULL; // temporary pointer

  char inputLine[inputLineLen];

  if (argc != 2)
    {
      cerr << "Error: Need a filename as second command line argument" << endl;
      exit(-1);
    }

  ifstream schoolsFile(argv[1]);

  if (!schoolsFile)
    {
      cerr << "Error: Could not open " << argv[1] << endl;
      exit(-1);
    }

  
  // read entire line into inputLine
  schoolsFile.getline(inputLine, inputLineLen); 
  
  while (!schoolsFile.eof())
  {

    p = new School;
    p -> next = NULL;

    parseInputLine(inputLine, p); // assigns fields in School
                                  // based on characters in InputLine
    
    if (head == NULL) // if this is the first school, assign value of head
      head = p;
    
    if (tail != NULL) // add this school to the end of the list
      tail -> next = p;    
    tail = p;
    
    // read entire next line into inputLine
    schoolsFile.getline(inputLine, inputLineLen); 

  } // while (!schoolsFile.eof())  

  // Now traverse the list, and print out all the values in the list

  cout << "Here is a list of all the schools that you read in:" << endl;

  printSchoolHeaders();

  for (p = head; p != NULL; p = p-> next)
  {
    printSchool(p);
  }
  
  return 0;

} // end main


void printSchoolHeaders()
{

  cout << setw(12) << "Domain"
       << setw(25) << "Full Name"
       << setw(15) << "Mascot"
       << setw(15) << "City"
       << setw(3)  << "St"
       << setw(5 ) << "Year"  << endl;

  cout << setw(12) << "======"
       << setw(25) << "========="
       << setw(15) << "======"
       << setw(15) << "===="
       << setw(3)  << "=="
       << setw(5 ) << "===="  << endl;

}

void printSchool(School * p)
{
  cout << setw(12) << p->domainName
       << setw(25) << p->fullName
       << setw(15) << p->mascot
       << setw(15) << p->city
       << setw(3)  << p->state
       << setw(5 ) << p->foundingYear << endl;
}


void parseInputLine(char *inputLine, School *p) // assigns fields in School
{
  // use strtok to separate up comma separated values

  // For into on strtok, do "man strtok" on a Unix system
  // Or see p. 368, Chapter 5 in Deitel/Deitel C++:How to Program 4th Edition


  char *tokenPtr;

  // read up to the , out of inputLine
  // then copy up to n-1 characters into p->domainName
  // n is "shortStringLen".  See p. 363-364 in DD chapter 5.

  tokenPtr = strtok(inputLine,",");
  strncpy(p->domainName,tokenPtr,shortStringLen);
  p->domainName[shortStringLen-1]='\0'; 

  // for safety, we put \0 into last element of p->domainName.
  // for explanation, see DD p. 364, common programming error 5.20

  // passing NULL into strtok() means "keep using previous string"
  // that is, we continue reading items out of inputLine
  // Do same thing for fullName

  tokenPtr = strtok(NULL,",");
  strncpy(p->fullName,tokenPtr,longStringLen);
  p->fullName[longStringLen-1] = '\0';

  // continue in this vein for mascot, city, and state

  tokenPtr = strtok(NULL,",");
  strncpy(p->mascot,tokenPtr,longStringLen);
  p->mascot[longStringLen-1] = '\0';

  tokenPtr = strtok(NULL,",");
  strncpy(p->city,tokenPtr,longStringLen);
  p->city[longStringLen-1] = '\0';

  tokenPtr = strtok(NULL,",");
  strncpy(p->state,tokenPtr,longStringLen);
  p->state[longStringLen-1] = '\0';

  // now, use atoi on the string returned by strtok
  // since foundingYear is an integer, not a string (char *)
  // null termination is not an issue, since foundingYear is not a string

  tokenPtr = strtok(NULL,",");
  p->foundingYear = atoi(tokenPtr);
  
  return; // all done!

}

