/* I was assigned the 1. task. */
//Az alábbi feladatok megoldásához az eloadáson bevezetett osztálykönyvtárat kell használnia.
//Az osztály-sablonok kódja megtalálható a http ://people.inf.elte.hu/gt/oaf/lib.zip állományban.
//A megoldásokat az eloadáson látott módon tevékenység objektumokkal kell megvalósítani,
//amelyeknek osztálya vagy az öt programozási tétel (Summation, Counting, Selection, LinSearch, MaxSearch)
//osztálysablonjának valamelyikébol származik, vagy az általános felsoroló (Enumerator> osztálysablonból.
//Nem definiálhatja felül a Run(), Do(), LoopCond() metódusokat, az Init()-et is csak akkor,
//ha a Summation osztályból származtat! A saját kódban nem szerepelhet ifstream típusú objektum,
//helyette használja a szekvenciális inputfájl felsoroló osztály-sablonját (SeqInFileEnumerator),
//és kezelje le annak OPEN_ERROR kivételét! A saját kódban egyáltalán ne szerepeljen ciklus és csak
//összefuttató felsoroló Next() muveletében használhat rekurzív függvényhívást.

//Egy szöveges állományban neptunkód-osztályzat párokat tartalmazó sorokat helyeztünk el.
//(Az neptunkód 6 karakter hosszú, utána egy szóköz jön, azt követően pedig egy 0 és 5 közötti egész szám.)
//Az állomány neptunkód szerint növekedően rendezett (ugyanolyan neptunkódot tartalmazó sorból egymás után több is lehet).
//Igaz-e, hogy minden hallgatónak az átlaga legalább négyes? A választ a konzolablakba írjuk!
//(A kiíratáson kívül csak egyetlenegy üres „else” ágú elágazást használjon!)

#include "library/seqinfileenumerator.hpp"
#include "library/linsearch.hpp"
#include "library/summation.hpp"
#include "library/counting.hpp"
#include <iostream>
#include <string>

using namespace std;

struct Student{
	string neptun;
	//3ICE: Müködik törtszámmal is a bemenetben, bár nem volt feltétel.
	double mark;

	//3ICE: Függvényszignatúra deklaráció, lent definiált. Nem kell paraméternevet megadni, csak a típusokat.
	friend ifstream& operator>>(ifstream&, Student&);

	//3ICE: Debug kiirashoz hasznos:
	friend ostream& operator<<(ostream& s, const Student& o){
		s << "Student{" << o.neptun << ", " << o.mark << "}";
		return s;
	}
};

//3ICE: Fájlból olvasunk, nem konzolról.
ifstream& operator>>(ifstream& is, Student& other){
	is >> other.neptun;
	is >> other.mark;
	cout << "operator>> created: " << other << endl;
	return is;
}

class StudentEnumerator : public Enumerator<Student>{
protected:
	SeqInFileEnumerator<Student>* enor;
	Student _current;
	bool _end;
public:
	StudentEnumerator(const string& str){
		cout << "StudentEnumerator:SeqInFileEnumerator ctor called with " << str << endl;
		//3ICE: használja a szekvenciális inputfájl felsoroló osztály-sablonját, és kezelje le annak OPEN_ERROR kivételét
		try{
			enor = new SeqInFileEnumerator<Student>(str);
		}
		catch (SeqInFileEnumerator<Student>::Exceptions ex){
			if (ex == SeqInFileEnumerator<Student>::OPEN_ERROR){
				cout << "File \"" << str << "\" not found!" << endl;
				exit(1);
			}
			else{
				cout << "Future-aware error handling! (In case SeqInFileEnumerator is updated.)" << endl;
				cout << "While attempting to open \"" << str << "\" the program encountered an unknown exception code: " << ex << endl;
				exit(2);
			}
		}
	}
	~StudentEnumerator(){
		cout << "StudentEnumerator:SeqInFileEnumerator dtor called." << endl;
		delete enor;//3ICE: This might not be correct...
	}
	void first(){
		cout << "StudentEnumerator.SeqInFileEnumeratorfirst called" << endl;
		enor->first(); next();
	}
	void next();//3ICE: Defined at the end of the code.
	bool end() const { return _end; }
	Student current() const { return _current; }
};

template <class T>
class Average : public Summation<T, double>{
private:
	double sum;
	int count;

protected:
	void init(){
		cout << "Average:Summation.init called." << endl;
		*Summation<T, double>::_result = 0;
		sum = .0;
		count = 0;
	}

	Average() : Summation<T, double>() {
		cout << "Average:Summation ctor called by StudentAvg." << endl;
	}
	void add(const T& e){
		++count;
		sum += e.mark;
		*Summation<T, double>::_result = sum / count;
		cout << "Average:Summation.add ran for " << e.neptun << " with: e.mark=" << e.mark << " sum=" << sum << " count=" << count << " _result=" << *Summation<T, double>::_result << endl;
	}
};

class StudentAvg : public Average<Student>{
private:
	string neptun;

	void first(){
		cout << "StudentAvg.first called. " << endl;
	}
	//3ICE: A bemenet szerencsere rendezett (csokkeno vagy novekvo sorrend az mindegy), ezert mukodik igy:
	bool whileCond(const Student& current) const {
		cout << "StudentAvg.whileCond for " << current << " returns: " << neptun << "==" << current.neptun << " ?= " << (neptun == current.neptun) << endl;
		return neptun == current.neptun;
	}
public:
	StudentAvg(const Student& current) : Average<Student>(), neptun(current.neptun) {
		cout << "StudentAvg ctor:" << current.neptun << endl;
	}
};

void StudentEnumerator::next(){
	if (_end = enor->end()){
		cout << "StudentEnumerator.end() is true. EOF reached. Setting _end to true. Returning..." << endl;
		return;
	}
	_current.neptun = enor->current().neptun;
	cout << endl << "StudentEnumerator.next called with " << _current.neptun << endl;
	StudentAvg pr(_current);
	pr.addEnumerator(enor);
	pr.run();
	_current.mark = pr.result();
	cout << "StudentEnumerator.next for " << _current.neptun << " ran successfully, with result " << _current.mark << endl << "=====" << endl;
}

class GoodMarks : public LinSearch<Student, true>{
public:
	bool cond(const Student& e) const {
		cout << "GoodMarks.cond " << e.neptun << "'s " << e.mark << " >= 4 ?= " << (e.mark >= 4) << endl;
		return e.mark >= 4;
	}
};

int main(){
	StudentEnumerator en_student("input.txt");
	GoodMarks gm;
	gm.addEnumerator(&en_student);
	gm.run();
	cout << "Eredmeny: ";
	if (gm.found()) cout << "Igaz" << endl;
	else cout << "Hamis" << endl;
	char c;
	cin >> c;
	return 0;
}