// Class example - student record
// 
// This version includes:
//      a couple of constructors
//      a name that is copied (not referenced)
//      Some consts instead of hard-coded array limits  
//
// 
// 

#include <iostream.h>


// here we define the class. For this class we include all member
// function definitions inside the class definition itself, this
// doesn't have to be done this way!


class StudentRecord {

  // declare some constants
  const int n_hw = 3;		// the number of hw grades
  const int n_test = 2;		// the number of test grades


  // make the data members "private". That means we can't get at these
  // variables from outside the class (only member functions can 
  // access these variables

private:
  char *name;			// student name
  double hw[n_hw];		// homework grades
  double test[n_test];		// test grades


  // member functions are public (so we can call them from functions
  // that are not part of this class)

public:
  
  // here is a special function called a "constructor"
  // a constructor is called whenever a new variable of
  // type "StudentRecord" is created. A constructor is often
  // used to initialize an object.

  StudentRecord(void) {
    name=NULL;		// no name yet

    // clear all grades to 0

    for (int i=0;i<n_hw;i++)
      hw[i] = 0.0;
    for (int i=0;i<n_test;i++)
      test[i] = 0.0;


    // some debugging stuff!
    cout << "Student Record created" << endl;
  }


  // Another constructor - this one called with a char *
  // we initialize the name from the string passed in.
  //
  // instead of just using the pointer passed in
  // we make a copy of the entire string.

  StudentRecord( char *s) {
    // allocate memory for the copy of the name

    name= new char[strlen(s)+1];
    strcpy(name,s);

    // clear all grades to 0

    for (int i=0;i<n_hw;i++)
      hw[i] = 0.0;
    for (int i=0;i<n_test;i++)
      test[i] = 0.0;


    // some debugging stuff!
    cout << "Student Record created for " << name << endl;
  }


  // Yet Another! - this one called with everything
  //

  StudentRecord( char *s, 
		 double hw0, double hw1, double hw2, 
		 double t0, double t1) {

    // allocate memory for the copy of the name

    name= new char[strlen(s)+1];
    strcpy(name,s);

    // record all the scores

    SetHW(0,hw0);
    SetHW(1,hw1);
    SetHW(2,hw2);


    SetTest(0,t0);
    SetTest(1,t1);


    // some debugging stuff!
    cout << "Student Record created (with scores) for " << name << endl;
  }



  // ===================================================
  // Functions that set the value of data members
  // note that we "hide" the data members behind these 
  // functions...

  char *SetName( char *s) {
    // This is different than the first version!
    // we need to make a copy of the string
    // before we make a copy we need to make sure that
    // we don't already have a name - if so we should
    // free that memory

    if (name != NULL) {
      delete[] name;
    }

    // allocate memory for the copy of the name

    name= new char[strlen(s)+1];

    // make a copy (and return the address of the new copy)

    return( strcpy(name,s) );

  }


  // SetHW sets a single hw grade.
  // hwnum is the number of the hw and grade is the grade.

  double SetHW(int hwnum, double grade) {
    // do some error checking!
    if ((hwnum<0) || (hwnum>=n_hw)) {
      cerr << "Invalid HW number!" << endl;
      exit(1);
    }

    if ((grade<0.0) || (grade>100.0)) {
      cerr << "Invalid HW grade!" << endl;
      exit(1);
    }

    return( hw[hwnum] = grade);
  }


  // SetTest sets a single test grade.
  // testnum is the number of the test and grade is the grade.

  double SetTest(int testnum, double grade) {
    // do some error checking!
    if ((testnum<0) || (testnum>=n_test)) {
      cerr << "Invalid Test number!" << endl;
      exit(1);
    }

    if ((grade<0.0) || (grade>100.0)) {
      cerr << "Invalid Test grade!" << endl;
      exit(1);
    }

    return( test[testnum] = grade);
  }


  // ===================================================
  // functions that get the value of data members

  char *GetName(void) {
    return(name);
  }


  // GetHW returns a single homework grade

  double GetHW(int hwnum) {

    // do some error checking!
    if ((hwnum<0) || (hwnum>=n_hw)) {
      cerr << "Invalid Test number!" << endl;
      exit(1);
    }

    return(hw[hwnum]);
  }



  // GetTest returns a single test grade

  double GetTest(int testnum) {
    // do some error checking!
    if ((testnum<0) || (testnum>1)) {
      cerr << "Invalid Test number!" << endl;
      exit(1);
    }

    return(test[testnum]);
  }

  // getting the average means we need to
  // compute it (we don't need the "ave" data member).

  double GetAve(void) {
    double tot=0;

    for (int i=0;i<n_hw;i++)
      tot += hw[i];

    for (int i=0;i<n_test;i++)
      tot += test[i];

    return(tot/(n_hw + n_test));
  }


  // ===================================================

  // a function that prints out the record

  void print() {

    cout << "Name: " << name << endl;

    for (int i=0;i<n_hw;i++) 
      cout << "HW #" << i << ": " << hw[i] << endl;

    for (int i=0;i<n_test;i++) 
      cout << "TEST #" << i << ": " << test[i] << endl;

    cout << "Average: " << GetAve()<< endl;
  }
};



int main(void) {
  // declare a variable of type StudentRecord

  StudentRecord stu;

  // initialize with some stuff

  stu.SetName("Joe Student");

  stu.SetHW(0,90.5);
  stu.SetHW(1,100);
  stu.SetHW(2,55.25);

  stu.SetTest(0,82.5);
  stu.SetTest(1,88);


  //print the record
  stu.print();


  // create another one - initialze with a name
  StudentRecord bill("Billy Gates");
  
  // print Bill's dismal grades
  bill.print();

  // create another one using the third constructor
  StudentRecord guru("C++ Guru",100,100,100,100,100);

  // print out this awesome record
  guru.print();

  return(0);
}


  

