Please send questions to st10@humboldt.edu .
/*--------------------------------------------------------
 Implementation file for class Course with NO explicitly-
    declared destructor, copy constructor, or overloaded
    assignment operator.
 
 created by: Sharon Tuttle
 last modified: 12-12-03
 ---------------------------------------------------------*/

#include <iostream>
#include <string>
#include "Course.h"
#include "Prof.h"
#include "Student.h"
using namespace std;

//-----
// implementation of constructors
//
// assumption: when a course is first created,
//    no students are enrolled in it.
//-----

Course::Course() : courseNum(""), courseName(""), numEnrolled(0),
		   enrolled(NULL)
{
    // there must be a better way...!
    Prof emptyProf;
    instructor = emptyProf;
}

Course::Course(string cNum, string cName) : courseNum(cNum),
   courseName(cName), numEnrolled(0), 
   enrolled(NULL)
{
    // there must be a better way...!
    Prof emptyProf;
    instructor = emptyProf;
}   

Course::Course(string cNum, string cName, Prof instr) : courseNum(cNum),
   courseName(cName), numEnrolled(0), 
   enrolled(NULL), instructor(instr)
{
}

//-----
// implementation of copy constructor
//-----

        

//-----
// implementation of destructor
//-----



//-----
// implementation of accessors
//-----

string   Course::get_courseName() const
{
    return courseName;
}

string   Course::get_courseNum() const
{
    return courseNum;
}

int      Course::get_numEnrolled() const
{
    return numEnrolled;
}

Student* Course::get_enrolled() const
{
    return enrolled;
}

Prof     Course::get_instructor() const
{
    return instructor;
}

//-----
// implementation of mutators
//
// assumption: users can only change numEnrolled
//    and enrolled by enrolling, removing 
//    students from the course.
//-----

void Course::set_courseNum(string new_cNum)
{
    courseNum = new_cNum;
}

void Course::set_courseName(string new_cName)
{
    courseName = new_cName;
}

void Course::set_instructor(Prof new_instr)
{
    instructor = new_instr;
}

//-----
// implementation of overloaded operators
//-----

bool Course::operator ==(const Course& rightHandCourse) const
{
    // see if "easy to compare" fields are equal...
    if ((courseNum == rightHandCourse.courseNum) &&
        (courseName == rightHandCourse.courseName) &&
        (numEnrolled == rightHandCourse.numEnrolled) &&
        (instructor == rightHandCourse.instructor))
    {
        // if get here --- only students enrolled still need
        //    to be checked for equality
        for (int i=0; i < numEnrolled; i++)
        {
            if (! (enrolled[i] == rightHandCourse.enrolled[i]))
            {
                return false;
            }
        }

        // if make it out of loop without returning,
        //    then all students in enrolled are equivalent,
        //    too;
        return true;
    }
    else
    {
        // if get here --- one of "easy to compare" fields
        //    wasn't equivalent
        return false;
    }
}

//-----
// implementation of other methods
//-----
       
//-----
// Contract: enroll : Student -> bool
// Purpose: try to add Student st to enrolled array for
//          the calling Course. If numEnrolled ==
//          ENROLLMENT_MAX, st cannot be added,
//          and false is returned. Otherwise, st is
//          added to the enrolled array (as its last element), 
//          and numEnrolled increases by one.
//
// Examples: if Course c1 has c1.numEnrolled == ENROLLMENT_MAX,
//           and if st1 is a Student instance, then
//              c1.enroll(st1) == false
//           if Course c2 has c2.numEnrolled == 29
//           and if st1 is a Student instance, then
//              c2.enroll(st1) == true
//              c2.numEnrolled == 30
//              c2.enrolled[29] == st1
//-----
bool Course::enroll(Student st)
{
    // cannot add st if class is full
    if (numEnrolled == ENROLLMENT_MAX)
    {
        return false;
    }

    // if get here --- class is NOT full, can enroll this student!

    // if class is currently empty, allocate a new gradesArray
    //    (it will be of size ENROLLMENT_MAX, but class will
    //    carefully make sure that only its elements
    //    0 .. (numEnrolled-1) are considered enrolled Students)
    if (numEnrolled == 0)
    {
        enrolled = new Student[ENROLLMENT_MAX];
    }

    // add the new student to the end of the "filled"
    // portion of enrolled array
    //
    // do you see that, with numEnrolled "actual"
    //    students in enrolled, this new student
    //    will have index numEnrolled (which should then
    //    be incremented to reflect the new addition)?
    enrolled[numEnrolled] = st;
    numEnrolled++;
    return true;
}

//-----
// Contract: remove : Student -> bool
// Purpose: try to remove Student st from enrolled array for
//          the calling Course. If a Student equivalent
//          to st cannot be found in enrolled, then returns
//          false and nothing is changed. If such an 
//          equivalent st can be found, the first instance 
//          of such a Student will be removed from enrolled,
//          the numEnrolled will be decreased by 1, and
//          true will be returned.
//
//          (If numEnrolled goes to 0, will deallocate
//          enrolled array and make it NULL...)
//
// Examples: if Course c1 has enrolled NOT containing
//           a Student equivalent to st1, where
//           st1 is a Student instance, then
//              c1.remove(st1) == false
//           if Course c2 has c2.numEnrolled == 29
//           and st1 has an equivalent instance in c2's
//           enrolled array, then:
//              c2.remove(st1) == true
//              c2.numEnrolled == 28
//              c2.enrolled has one fewer element (one fewer
//         st1-equivalent Student)
//-----

bool Course::remove(Student st)
{
    bool notFound = true;
    int i = 0;

    // try to look for student st in enrolled
    while (notFound)
    {
        if (! (enrolled[i] == st))
        {
            i++;
        }
        else // found first instance of st!
        {
            notFound = false;

            // if this is ONLY st in array, simply deallocate
            //    enrolled
            if (numEnrolled == 1)
            {
	        delete [] enrolled;
                enrolled = false;
            }

            // otherwise, "remove" student by shifting all students
            //    to the right of this student by one position to 
            //    the left
            else
            {
                for (int j=i+1; j < numEnrolled; j++)
                {
                    enrolled[j-1] = enrolled[j];
                }
            }

            // interestingly enough, we can simply ignore
            //    the old "last element" student now;
            //    it cannot be accessed by Course's member
            //    functions...

            // ...as long as we remember to update numEnrolled!
            numEnrolled--;
            return true;
        }
    }
    
    // if get here: st isn't here to remove.
    return false;
}

//-----
// Contract: void -> void
// Purpose: print a roll report for the calling Course
//
// Examples: for Prof p1("Tuttle", "Sharon", "M.", "Dr.", ...)
//           and Student s1("Jones", "Anna", 3)
//               Student s2("Smith", "Joe", 3)
//               Student s3("Nguyen", "Dian", 3)
//           and Course c1("CS 131", "Intro to CS", p1);
//               c1.enroll(s1);
//               c1.enroll(s2);
//               c1.enroll(s3);   
//           Then c1.printRoll() should produce:
//
// Class Roll for CS 131 - Intro to CS:
//
// Instructor: Dr. Sharon M. Tuttle
// Enrollment: 3
// 1. Anna Jones
// 2. Joe Smith
// 3. Dian Nguyen 
//------------------------------------------------------------
void Course::printRoll() const
{
    // print header of class roll
    cout << endl;
    cout << "Class Roll for " << courseNum << " - " << courseName
         << ":" << endl;
    cout << endl;
    cout << "Instructor: " << instructor.mailingName() << endl;
    cout << "Enrollment: " << numEnrolled << endl;
    
    // print list of students in course
    for (int i = 0; i < numEnrolled; i++)
    {
        cout << (i+1) << ". " << enrolled[i].get_firstName()
             << " " << enrolled[i].get_lastName() << endl;
    }
    
    cout << endl;
}

//-----
// Contract : courseAvg : void -> double
// Purpose : compute and return the course average
//           by averaging the enrolled Students' 
//           current averages. If a student's
//           current average is computed as -1,
//           do not include it in the course
//           average (and only divide by the 
//           number of students with non-neg-1
//           averages). If no students have non-neg-1
//           averages, return a course average of -1.
// 
// Examples: for Student s1("Jones", "Anna", 3)
//                  with grades 100, 80, 75,
//               Student s2("Smith", "Joe", 3)
//                  with no grades
//               Student s3("Nguyen", "Dian", 3)
//                  with grades 60, 80, 90   
//           and Course c1("CS 131", "Intro to CS", p1);
//               c1.enroll(s1);
//               c1.enroll(s2);
//               c1.enroll(s3);   
//           Then c1.courseAvg() == (approx) 80.83333
//
//           For Course c2("CS 0", "Empty");
//               c2.courseAvg() == -1
//
//           For Course c3("CS 1", "Almost empty");
//               Student s4("Johnson", "Ann", 0);
//               Student s5("Jimson", "Benny", 0);
//               Student s6("Judd", "Ashley", 3);
//               c3.enroll(s4);
//               c3.enroll(s5);
//               c3.enroll(s6);
//           then c3.courseAvg() == -1 
//-----------------------------------------------------------

double Course::courseAvg() const
{
    int numWiAvg = 0;
    double sumAvgs = 0;
    double currAvg;

    for (int i=0; i < numEnrolled; i++)
    {
        // if this is "really" an average, count
        //    it and save its value;
        currAvg = enrolled[i].avgGrade();
        if (currAvg != -1)
        {
            numWiAvg++;
            sumAvgs += currAvg;
        }
        
        // otherwise, ignore it;
    }

    // return -1 if NO student had grades!
    if (numWiAvg == 0)
    {
        return -1;
    }
    else
    {
        return sumAvgs / numWiAvg;
    } 
}