#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <ctime>


using namespace std;

#define check( s, a, b ) { \
if( (a) != (b) ) { \
cout << "## FAIL (" << (s) << "): " << (a) << " != " << (b) << endl; \
} \
cout << "## PASS (" << (s) << "): " <<  (a) << endl; \
}


/*********************/
/* Forward Reference */
/* class A & B refer to each other */
/*********************/

class B; // forward reference

class A {
//	B b;
	B& br;
	B* bp;
};

class B {
//	A a;
	A& ar;
	A* ap;
};

 class Student {
 private:
	 string lname;
	 string fname;
	 int year;
	 vector<string> cls;
	 int sid;
 public:
	 Student(string l, string f);
	 //Student(string l, string f) : lname(l), fname(f), year(1) {};
	 string get_name() const { return (lname + " " + fname); }
	 int get_year() { return year; }
	 void promote() { year = ( (!year || year >= 4) ? 0 : year+1 ); }
	 bool graduated() { return (year==0); }
	 void enroll(string classname);
	 string get_class(int i) { return cls[i]; }
	 int get_sid() { return sid; }
	 void drop( string classname );
	 int get_class_cnt() { return cls.size(); }

	 // ++ prefix
	 // return the reference of itself
	 Student &operator++() { promote(); return *this; }
	 // postfix ++
	 // return a copy of itself before increment
	 Student operator++(int) { Student t = *this; promote(); return t; }

	 // < operator
	 friend bool operator<(const Student s1, const Student s2);
	 friend ostream& operator<<( ostream &out, Student &s );

 };

 ostream& operator<<( ostream &out, Student &s )
 {
	 out << "( " << s.get_name() << ", " << s.get_year() << ", " << s.get_sid() << " )";
	 return out;
 }

 bool operator<(const Student s1, const Student s2)
 {
	 return (s1.year <  s2.year);
 }

 Student::Student( string l, string f) : lname(l), fname(f), year(1) // using copy constructor
 {
	 // use assignment operator
	 //lname = l;
	 //fname = f;
	 //year = 1;
	 cls.clear();
	 srand((unsigned int) time(0));
	 sid = rand() % 100000000;
 }

 void Student::enroll( string classname )
 {
	 vector<string>::iterator it;
	 it = find( cls.begin(), cls.end(), classname );
	 // not found
	 if( it == cls.end() )
		 cls.push_back( classname );
	 // found
	 else
		 cout << "class name " << classname << " already exists" << endl;
 }

 void Student::drop( string classname )
 {
	 vector<string>::iterator it;
	 it = find( cls.begin(), cls.end(), classname );
	 if( it == cls.end() )
		 cout << "class name " << classname << " is not found" << endl;
	 else
		 cls.erase(it);
 }

int main(int argc, const char * argv[]) {
    
    Student s1( "Kim", "Soochul" );
    check( "get_year()", s1.get_year(), 1 );
    check( "get_name()", s1.get_name(), "Kim Soochul" );

    ++s1;

    check( "get_year()", s1.get_year(), 2 );
    check( "graduated()", s1.graduated(), false );

	s1++;
	check( "get_year()", s1.get_year(), 3 );
    
	Student s2("Kim", "James");

	if( s1 < s2 ) 
		cout << "## FAIL: You are wrong" << endl;
	else
		cout << "## PASS: You are right!" << endl;

	// Print student: (name, year, sid)
	cout << "Student s1:" << s1 << endl;
	cout << "Student s1:" << s2 << endl;

	// overload cin
	// cin >> s1;

    s1.enroll( "C++" );
    s1.enroll( "Java" );
    check( "get_class()", s1.get_class(0), "C++" );
    check( "get_class()", s1.get_class(1), "Java" );
    s1.promote(); s1.promote(); s1.promote();
    check( "graduated()", s1.graduated(), true );
    
	//ex2
	check( "get_sid()", s1.get_sid(), s1.get_sid() );

	//ex3
	s1.drop( "C++" );
    check( "get_class(0)", s1.get_class(0), "Java" );
    check( "get_class_cnt()", s1.get_class_cnt(), 1 );

	s1.enroll( "Java" );
	check( "get_class_cnt()", s1.get_class_cnt(), 1 );


    system( "pause" );
    return 0;
}


