/*
1. Към класа CAddress от упражнение 3 да се добави глобална приятелска функция (оператор) за извеждане на данните за адреса по указан като параметър изходен поток,например:
friend ostream& operator<<(ostream& toStream, const CAddress& addr)
2. Към класа CPerson от упражнение 3 да се добави частна функция за получаване на година, месец и ден от подадения като параметър единен граждански номер,например:
void getYMD(const string& strEGN,int &iYear,int &iMonth, int &iDay)const;
3. Към класа CPerson от упражнение 3 да се добави глобална приятелска функция (оператор) за извеждане на данните за личността по указан като параметър изходен поток,например:
friend ostream& operator<<(ostream& toStream, const CPerson& pers)
4. Към класа CStudent от упражнение 3 да се добави глобална приятелска функция за конзолен вход към единен граждански номер на подадения като параметър обект от класа CStudent,например:
friend void ConsoleInputEGN(CStudent& stud)
5. Към класа CStudent от упражнение 3 да се добави глобална приятелска функция (оператор) за извеждане на данните за студента по указан като параметър изходен поток,например:
friend ostream& operator<<(ostream& toStream, const CStudent& stud)
6. Към класа CStudent от упражнение 3 да се добавят следните глобални приятелски функции (оператори) за сравнение на двама студента (параметри):
? За равенство по възраст ==;
? За неравенство по възраст !=;
? За по-малко по възраст <;
? За по-голямо по възраст >;
7. Да се състави подходяща главна функция за проверка на създадените функции и оператори.*/


#include <iostream>
#include <string>
#include <ctime>
using namespace std;

class CAddress {
	string street;
	string post;
	string city;
public:

	friend ostream& operator<<(ostream& toStream, const CAddress& addr);

	CAddress() {
		street = "";
		post = "";
		city = "";
	}

	explicit CAddress(string s, string p, string c) {
		street = s;
		post = p;
		city = c;
	}

	CAddress(const CAddress& a) //copy constructor
	{
		street = a.street;
		post = a.post;
		city = a.city;
	}

	CAddress(const CAddress* a) //copy constructor
	{
		street = a->street;
		post = a->post;
		city = a->city;
	}

	string GetStreet() {
		return street;
	}

	void SetStreet(string s) {
		street = s;
	}

	string GetPost() {
		return post;
	}

	void SetPost(string p) {
		post = p;
	}

	string GetCity() {
		return city;
	}

	void SetCity(string c) {
		city = c;
	}

	ostream& Output(ostream& toStream) {
		toStream << street << ";" << post << ";" << city;
		return toStream;
	}

};

class CPerson {
public:
	enum Gender { male, female, undef };
private:
	string name;
	string egn;
	Gender gender;
public:
	friend ostream& operator<<(ostream& toStream, const CPerson& pers);
	CPerson() {
		name = "";
		gender = undef;
		egn = "";
	}

	explicit CPerson(string szName, Gender szGender) {
		name = szName;
		gender = szGender;
		egn = "";
	}

	explicit CPerson(string name, string egn, Gender gender) {
		this->name = name;
		if (checkEGN(egn))
			this->egn = egn;
		else
			this->egn = "1111111111";
		this->gender = gender;
	}

	void SetName(string szName) {
		name = szName;
	}

	string GetName() {
		return name;
	}

	void SetGender(Gender szGender) {
		gender = szGender;
	}

	Gender GetGender() {
		return gender;
	}

	bool SetEGN(string e) {
		if (checkEGN(e)) {
			egn = e;
			return true;
		}
		else if (e == "1111111111") {
			egn = e;
			return true;
		}
		else
			return false;
	}

	string GetEGN() {
		return egn;
	}
private:
	bool isCorrectDateOfEGN(std::string yymmdd) {
		if (yymmdd.length() != 6)
			return false;

		int dayspermon[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

		int month, day, year;

		year = atoi(yymmdd.substr(0, 2).c_str());
		month = atoi(yymmdd.substr(2, 2).c_str());
		day = atoi(yymmdd.substr(4, 2).c_str());

		if (!(year != 0 && month != 0 && day != 0))
			return false;
		if (year < 0 || month < 1 || day < 1) {
			return false;
		}

		if (month >= 13 && month <= 20) {
			return false;
		}
		else if (month >= 21 && month <= 32) {
			month = month - 20;
		}
		else if (month >= 33 && month <= 40) {
			return false;
		}
		else if (month >= 41 && month <= 52) {
			month = month - 40;
		}
		else if (month > 52) {
			return false;
		}

		if (month == 2)
			if ((year % 4) == 0)
				dayspermon[2] = 29;

		if (day > dayspermon[month - 1]) {
			return false;
		}

		return true;
	}

	bool checkEGN(std::string egn) {
		int ctrSum = 0;
		int ctrField = 0;
		int teglo[] = { 2, 4, 8, 5, 10, 9, 7, 3, 6 };

		if (egn.length() != 10)
			return false;

		long long tmpEgn = atoi(egn.c_str());
		if (tmpEgn == 0)
			return false;

		if (!isCorrectDateOfEGN(egn.substr(0, 6))) {
			return false;
		}

		for (int i = 0; i < egn.length() - 1; ++i) {
			string t = egn.substr(i, 1);
			ctrSum += teglo[i] * atoi(t.c_str());
		}

		ctrField = (ctrSum % 11) % 10;

		if (ctrField != atoi(egn.substr(9, 1).c_str()))
			return false;
		else return true;
	}

	void getYMD(const string& strEGN, int& iYear, int& iMonth, int& iDay) const {
		iYear = atoi(strEGN.substr(0, 2).c_str());
		iMonth = atoi(strEGN.substr(2, 2).c_str());
		iDay = atoi(strEGN.substr(4, 2).c_str());

		if (iMonth >= 21 && iMonth <= 32) {
			iMonth = iMonth - 20;
			iYear = iYear + 1800;
		}
		else if (iMonth >= 41 && iMonth <= 52) {
			iMonth = iMonth - 40;
			iYear = iYear + 2000;
		}
		else iYear = iYear + 1900;
	}

public:

	void getYMD(int& iYear, int& iMonth, int& iDay) const {
		iYear = atoi(egn.substr(0, 2).c_str());
		iMonth = atoi(egn.substr(2, 2).c_str());
		iDay = atoi(egn.substr(4, 2).c_str());

		if (iMonth >= 21 && iMonth <= 32) {
			iMonth = iMonth - 20;
			iYear = iYear + 1800;
		}
		else if (iMonth >= 41 && iMonth <= 52) {
			iMonth = iMonth - 40;
			iYear = iYear + 2000;
		}
		else iYear = iYear + 1900;
	}

	Gender stringToGender(const string& strGender) const {
		if (strGender == "male")
			return Gender::male;
		else if (strGender == "female")
			return Gender::female;
		else return Gender::undef;
	}

	string genderToString() const {
		switch (gender) {
		case male:
			return "male";
		case female:
			return "female";
		case undef:
			return "undef";
		}
	}
};

class CStudent : public CPerson {
	string fnumber;
	CAddress address;
public:
	friend ostream& operator<<(ostream& toStream, CStudent& stud);
	friend void ConsoleInputEGN(CStudent& stud);
	friend bool operator==(CStudent& s1, CStudent& s2);
	friend bool operator!=(CStudent& s1, CStudent& s2);
	friend bool operator>(CStudent& s1, CStudent& s2);
	friend bool operator<(CStudent& s1, CStudent& s2);
	CStudent() {
		fnumber = "";
		address = new CAddress();
	}

	explicit CStudent(string n, string egn, Gender g, string s, string c, string p) : CPerson(n, egn, g) {
		address = new CAddress();
		address.SetStreet(s);
		address.SetCity(c);
		address.SetPost(p);
	}

	explicit CStudent(string n, string egn, Gender g, string s, string c, string p, string fn) : CPerson(n, egn, g) {
		address = new CAddress();
		address.SetStreet(s);
		address.SetCity(c);
		address.SetPost(p);
		fnumber = fn;
	}

	CStudent(CStudent& a) {
		address = a.address;
		fnumber = a.fnumber;
		this->SetName(a.GetName());
		this->SetGender(a.GetGender());
		this->SetEGN(a.GetEGN());
	}

	void SetAddress(CAddress& a) {
		address = a;
	}

	CAddress& GetAddress() {
		return address;
	}

	void SetFNum(string fn) {
		this->fnumber = fn;
	}

	string GetFNum() {
		return fnumber;
	}

	ostream& Output(ostream& out) {
		out << GetName() << ";" << GetGender() << ";" << GetEGN() << ";";
		address.Output(out);
		out << ";" << fnumber << endl;
		return out;
	}
};

ostream& operator<<(ostream& toStream, const CAddress& addr) {
	toStream << addr.street << ";" << addr.post << ";" << addr.city;
	return toStream;
}

ostream& operator<<(ostream& toStream, const CPerson& pers) {
	toStream << pers.name << ";" << pers.gender << ";" << pers.egn;
	return toStream;
}

ostream& operator<<(ostream& toStream, CStudent& stud) {
	toStream << stud.GetName() << ";" << stud.GetGender() << ";" << stud.GetEGN() << ";";
	toStream << stud.GetAddress();
	toStream << ";" << stud.fnumber;
	return toStream;
}

void ConsoleInputEGN(CStudent& stud) {
	string input;
	while (true) {
		cout << "Enter valid EGN:";
		cin >> input;
		if (stud.SetEGN(input))
			break;
		else cout << "Invalid EGN." << endl;
	}
}

bool operator==(CStudent& s1, CStudent& s2) {
	int s1year, s1month, s1day;
	int s2year, s2month, s2day;
	s1.getYMD(s1year, s1month, s1day);
	s2.getYMD(s2year, s2month, s2day);

	if ((s1year == s2year) && (s1month == s2month) && (s1day == s2day))
		return true;
	else return false;
}

bool operator!=(CStudent& s1, CStudent& s2) {
	int s1year, s1month, s1day;
	int s2year, s2month, s2day;
	s1.getYMD(s1year, s1month, s1day);
	s2.getYMD(s2year, s2month, s2day);
	if ((s1year != s2year) || (s1month != s2month) || (s1day != s2day))
		return true;
	else return false;
}

bool operator>(CStudent& s1, CStudent& s2) {
	int s1year, s1month, s1day;
	int s2year, s2month, s2day;
	s1.getYMD(s1year, s1month, s1day);
	s2.getYMD(s2year, s2month, s2day);

	if (s1year > s2year)
		return false;
	else if (s1year == s2year) {
		if (s1month > s2month)
			return false;
		else if (s1month == s2month) {
			if (s1day > s2day)
				return false;
			else return true;
		}
		else return true;
	}
	else return true;
}

bool operator<(CStudent& s1, CStudent& s2) {
	int s1year, s1month, s1day;
	int s2year, s2month, s2day;
	s1.getYMD(s1year, s1month, s1day);
	s2.getYMD(s2year, s2month, s2day);

	if (s1year < s2year)
		return false;
	else if (s1year == s2year) {
		if (s1month < s2month)
			return false;
		else if (s1month == s2month) {
			if (s1day < s2day)
				return false;
			else return true;
		}
		else return true;
	}
	else return true;
}


int main() {
	// https://georgi.unixsol.org/programs/egn.php

	CStudent s1("Gosho", "5412065927", CPerson::male, "Roza", "Varna", "9000", "24621449");
	CStudent s2("Mariya", "3108208010", CPerson::female, "Kon", "Dobrich", "8000", "24621507");

	if (s1 > s2) {
		"S1 e po-vuzrasten ot S2";
	}
	else if (s1 < s2) {
		"S2 e po-vuzrasten ot S1";
	}
	else {
		cout << "Dvata studenta sa astralni bliznaci";
	}
	return 0;
}