// stuVector.cpp

// Introduction to operator overloading
// Student and StudentVector classes
// Demonstrates the sort function from the standard library

#include <iostream>
#include <exception>
#include <string>
#include <fstream>
#include <vector>
#include <cstdlib>
#include <algorithm>			// sort()
using namespace std;
#include "range.h"

class Student {
public:
  Student() {  }
  
  Student( const string & id, const string & lname ) 
  {
    m_ID = id;
    m_LastName = lname;
  }

  bool operator <(const Student & S2) const
  // overloads the less than operator
  { return (m_ID < S2.m_ID); }
	
  bool operator ==(const Student & S2) const
  // overloads the equality operator
  { return m_ID == S2.m_ID; }

	friend ostream & operator <<(ostream & out, const Student & S )
	// overload the stream output operator
	{
		out << S.m_ID << " " << S.m_LastName << "\n";
		return out;
	}

	friend istream & operator >>(istream & inp, Student & S)
	// overload the stream input operator
	{
		inp >> S.m_ID >> S.m_LastName;
		return inp;
	}

private:
  string m_ID;
  string m_LastName;
};


// define a StudentVector class that acts as a "wrapper" around
// a vector<Student> object. 

class StudentVector {
public:
  // constructor that sets the capacity

  StudentVector(int capacity = 0)
  { 
		vStudents.reserve( capacity ); 
	}

  // adds a new student to the collection

  void add(const Student & S) 
  { 
		vStudents.push_back(S); 
	}

	const Student & at( int j ) const
	{
		return vStudents.at( j );
	}

	Student & at( int j )
	{
		return vStudents.at( j );
	}

	vector<Student>::iterator getBegin( )
	{
		return vStudents.begin( );
	}

	vector<Student>::iterator getEnd( )
	{
		return vStudents.end( );
	}

  // Returns a modifiable reference to a student. Permits
	// the collection to be modified. Throws RangeError.

  Student & operator [](unsigned j) 
  { 
		if( j < 0 || j >= vStudents.size( ) )
			throw RangeError( __FILE__, __LINE__, j );
		
		return vStudents[ j ]; 
	}

  // Returns a constant reference to a student. This is 
  // useful when a collection is declared const.
	// Throws RangeError.
  
	const Student & operator [](unsigned j) const
  { 
		if( j < 0 || j >= vStudents.size( ) )
			throw RangeError( __FILE__,__LINE__,j );

		return vStudents[ j ]; 
	}

  int size() const
  { 
		return vStudents.size(); 
	}

  // overloads the stream output operator

	friend ostream & operator <<(ostream & out, const StudentVector & vec)
	{
		vector<Student>::const_iterator I = vec.vStudents.begin( );
	
		while( I != vec.vStudents.end( ) )
		{
			out << *I;
			I++;
		}

		return out;
	}

private:
  vector<Student> vStudents;
};


//*************** TESTING THE CLASSES **********************

void Example1()
{
  StudentVector sVec;

	// Demonstrate two ways to throw an exception:
	// 1. vector class default exception
	// 2. our own RangeError exception
	try 
	{
		//cout << sVec.at( 0 );
		//cout << sVec[0];
	}
	catch( const exception & e )
	{
		cout << e.what( ) << endl;
	}

	sVec.add( Student ( "555-33-2222","Davis" ) );
	sVec.add( Student ( "222-33-4444","Adams" ) );
	sVec.add( Student ( "444-33-0000","Baker" ) );

	cout << "The collection:" << endl;
	cout << sVec << endl;

	try 
	{
		// subscript operator returns a modifiable reference 
		// to a student in the collection. Use this to modify
		// its contents. 
		sVec[0] = Student( "222-33-4444","Impostor" );

		// subscript also returns a const reference:
		const Student s3 = sVec[1];

		cout << "The collection:" << endl;
		cout << sVec << endl;

		// at() is overloaded. It can return a reference:
		sVec.at(1) = Student( "000-00-0000","Nobody" );

		// or it can return a const reference:
		cout << "At index 1: " << sVec.at( 1 ) << endl;

		// Throws RangeError exception:
		cout << sVec[5] << endl;
	}
	catch( const RangeError & R ) {
	    cout << R;
	}
	catch( ... ) {
		cout << "Unknown exception thrown.\n";
	}

}


void Example2( )
// iterators, sorting.
{
  StudentVector sVec( 10 );
  sVec.add( Student ( "444-33-0000","Baker" ) );
	sVec.add( Student ( "555-33-2222","Davis" ) );
  sVec.add( Student ( "222-33-4444","Adams" ) );
  sVec.add( Student ( "333-44-1111","Smith" ) );
  sVec.add( Student ( "300-33-0000","Jones" ) );

	// Must define operator < for the vector objects
	//   before calling sort:
	sort( sVec.getBegin( ), sVec.getEnd( ) );

	cout << "Sort by Student ID:" << endl;
	cout << sVec << endl;
}

int main()
{
	Example1( );

	//Example2( );


  return 0;
}

