#include <iostream>
#include <fstream>
#include <vector>
#include <string>

using namespace std;

class Pitalica
{
public:
	virtual ~Pitalica()
		{}
	virtual void Ucitaj( istream& istr ) = 0;
	virtual void Postavi() = 0;
	virtual int Izvestaj( ostream& ) const = 0;
	virtual Pitalica* Kopija() const = 0;

	static Pitalica* UcitajPitanje( istream& istr );	
};

class TekstualnaPitalica : public Pitalica
{
public:
	void Ucitaj( istream& istr )
		{
		getline( istr, _TekstPitanja );
		if(!istr)
			throw "Greska pri citanju teksta pitanja!";
		}
		
	void Postavi()
		{
		cout << _TekstPitanja << endl
			 << "--------------------------------------------" << endl
			 << "Upisite odgovor u jednom redu ili znak '@' ako ne znate:"
			 << endl;
		cin >> ws;
		getline( cin, _Odgovor );
		}
		
	int Izvestaj( ostream& ostr ) const
		{
		// prevazilazenje problema sa neispravnom bibliotekom
		string s = "@@";
		s[1] = 0;
		
		if( _Odgovor == s ){
			ostr << "(-1) Nije odgovoreno.";
			return -1;
			}
		else{
			ostr << "( ? ) Odgovoreno je: " << _Odgovor;
			return 0;
			}
		}
		
	TekstualnaPitalica* Kopija() const
		{ return new TekstualnaPitalica(*this); }
		
private:
	string _TekstPitanja;
	string _Odgovor;
};

class AbcdPitalica : public Pitalica
{
public:
	void Ucitaj( istream& istr )
		{
		getline( istr, _TekstPitanja );
		if(!istr)
			throw "Greska pri citanju teksta pitanja!";
		int n;
		istr >> n >> ws >> _TacanOdgovor >> ws;
		for( int i=0; i<n; i++ ){
			string s;
			getline( istr, s );
			_PonudjeniOdgovori.push_back( s );
			}
		if(!istr)
			throw "Greska pri citanju odgovora na pitanje!";
		}
		
	void Postavi()
		{
		cout << _TekstPitanja << endl
			 << "--------------------------------------------" << endl;
		for( int i=0; i<_PonudjeniOdgovori.size(); i++ )
			cout << _PonudjeniOdgovori[i] << endl;
		cout << "--------------------------------------------" << endl
			 << Uputstvo()
			 << endl;
		cin >> _Odgovor;
		}
		
	int Izvestaj( ostream& ostr ) const
		{
		if( _Odgovor == _TacanOdgovor ){
			ostr << "( 5 ) Tacan odgovor.";
			return 5;
			}
		else if( _Odgovor == "@" ){
			ostr << "(-1) Nije odgovoreno.";
			return -1;
			}
		else{
			ostr << "( -3 ) Netacan odgovor.";
			return -3;
			}
		}
		
	AbcdPitalica* Kopija() const
		{ return new AbcdPitalica(*this); }

protected:
	virtual char* Uputstvo() const
		{ 
		return 
			"Upisite slovo koje stoji ispred tacnog odgovora\n"
			"ili znak '@' ako ne znate.";
		}
		
private:
	string _TekstPitanja;
	vector<string> _PonudjeniOdgovori;
	string _TacanOdgovor;
	string _Odgovor;
};

class AbcdRedosled : public AbcdPitalica
{
public:
	AbcdRedosled* Kopija() const
		{ return new AbcdRedosled(*this); }
		
protected:
	char* Uputstvo() const
		{ 
		return 
			"Navedite tacan redosled odgovora upisivanjem slova\n"
			"koja stoje ispred odgovora (npr: acbd)\n"
			"ili znak '@' ako ne znate.";
		}		
};

Pitalica* Pitalica::UcitajPitanje( istream& istr )
{
	string tip;
	istr >> tip >> ws;
	if(!istr)
		throw "Greska pri citanju tipa pitanja!";

	Pitalica* p = 0;
	if( !strcmp(tip.c_str(),"TekstualnoPitanje"))
		p = new TekstualnaPitalica();
	else if( !strcmp(tip.c_str(),"AbcdPitalica"))
		p = new AbcdPitalica();
	else if( !strcmp(tip.c_str(),"AbcdRedosled"))
		p = new AbcdRedosled();
	else
		throw "Nepoznat tip pitanja!";
	p->Ucitaj( istr );
	return p;
}

class Test
{
public:
	Test()
		{}

	Test( const Test& t )
		{ init(t); }

	~Test()
		{ deinit(); }
		
	Test& operator = ( const Test& t )
		{ 
		if( this != &t ){
			deinit();
			init(t); 
			}
		return *this;
		}

	void Ucitaj( istream& istr )
		{
		deinit();
		int n;
		istr >> n >> ws;
		if( !istr )
			throw "Greska pri citanju testa!";
		if( n<=0 )
			throw "Test je prazan!";
		for( int i=0; i<n; i++ ){
			Pitalica* p = Pitalica::UcitajPitanje( istr );
			_Pitanja.push_back( p );
			}
		}

	void Postavi()
		{
		for( unsigned i=0; i<_Pitanja.size(); i++ ){
			cout << endl
				 << "*** Pitanje br. " << (i+1) << " ***" 
				 << endl << endl;
			_Pitanja[i]->Postavi();
			cout << endl;
			}
		}
		
	void Izvestaj( ostream& ostr ) const
		{
		int suma = 0;
		ostr << "Rezultat testiranja:" << endl
			 << "-----------------------------------------" << endl;
		for( unsigned i=0; i<_Pitanja.size(); i++ ){
			ostr << (i+1) << ".  ";
			suma += _Pitanja[i]->Izvestaj( ostr );
			ostr << endl;
			}
		ostr << "-----------------------------------------" << endl
			 << "Rezultat: " << suma << " poena" << endl;
		}

private:
	void deinit()
		{
		for( unsigned i=0; i<_Pitanja.size(); i++ )
			delete _Pitanja[i];
		_Pitanja.clear();
		}

	void init( const Test& t )
		{
		for( unsigned i=0; i<t._Pitanja.size(); i++ )
			_Pitanja.push_back( t._Pitanja[i]->Kopija() );
		}

	vector<Pitalica*> _Pitanja;	
};

main()
{
	try	{
		Test t;
		ifstream f( "pitanja.txt" );
		if( !f )
			throw "Nije uspelo otvaranje datoteke sa testom!";
		t.Ucitaj( f );
		t.Postavi();
		cout << endl << endl << endl;
		t.Izvestaj(cout);
		}
	catch( const char* s ){
		cerr << "GRESKA: " << s << endl;
		}
	return 0;
}

