#include <iostream>
#include <string>
#include <fstream>
#define ID_Ksiazki 1
#define ID_Filmu 2

// Przykład wykorzystania dziedziczenia z klas abstrakcyjnych.
// Problem:
// Przechowujemy w bazie danych różne obiekty (ksiazki, filmy, programy, czasopisma, itp...)
// potrzebujemy możliwośći zapisu, odczytu, i np szukania
// Obiekty oczywiście różnią się od siebie ale każdy możemy zapisać i odczytać.
// Tak więc cechy wspólne obiektów (właściwie wspólne funkcjonalności) umieszczamy w klasie "Obiekty",
// po której to dziedziczą klasy opisujące poszczególne rodzaje obiektów (taki jak Ksiazka, Film, itp...).
// Ponieważ każdy obiekt odczytuje się i zapisuje nieco inaczej więc w każdej konkretnej klasie musimy
// reimplementować funkcje "odczytaj" i "zapisz" .
// Dzieki polimorfizmowi mając tablicę wskaźników typu "Obiekty" zbierającą wskaźniki faktycznie wskazujące
// na obiekty rożnych typów (konkretnych klas dziedziczących z "Obiekty") możemy w ten sam sposób uruchamiać
// funkcje faktycznie uruchamiane dla konkretnych obiektów.
// Istnieje też spsób odczytu, bazujący na funkcji "odczytaj" reimplementowanej dla każdej z klas osobno
// Algorytm odczytu jest następujący: odczytujemy z pliku tzw id (wartość typu int oznaczającą z jakim obiektem
// mamy do czynienia) w zależności od przeczytanego id tworzymy pusty obiekt odpowiedniego typu i uruchamiamy
// na nim funkcję odczytaj (dzięki polimorfizmowi uruchomi się funkcja zdefiniowana konkretnie dla tego obiektu).
// Zalety tak napisanego programu są nastepujące:
// Chcąc dodać kolejny obiekt, należy jedynie:
// Utworzyć odpowiednią klasę dziedziczącą z "Obiekty", zdefiniować w niej odziedziczone funkcje wirtualne (zapisz, odczytaj, przedstaw się)
// Dodać osługę nowego typu w funkcji "Odczytywanie" i prócz tego miejsca nie trzeba NIC MODYFIKOWAĆ w napisanym
// wcześniej kodzie

using namespace std;
class Obiekty  	// Klasa abstrakcyjna będąca zbiorem funkcji wspólnych dla każdego obiektu
				// klasa taka jest zwana często interfejsem
{
  public:
	int id;		// id konkretnego obiektu
	virtual void zapisz(ofstream &) =0;
	virtual void odczytaj(ifstream &) =0;
	virtual void przedstaw_sie() = 0;
	Obiekty(int id_num) :id(id_num) {}; // Konstruktor przydatny przy tworzeniu konstruktorow klas wyprowadzonych
};

class Ksiazka : public Obiekty  // Klasa opisująca konkretny obiekt
{
	public:
	string tytul;
	int liczba_stron;
	void zapisz(ofstream &);
	void odczytaj(ifstream &);
	void przedstaw_sie();
	Ksiazka(): Obiekty(1),tytul("pusto"),liczba_stron(0) {}; // Konstruktor bezparametryczny tworzacy "pusty" obiekt
	Ksiazka(string t,int l): Obiekty(1),tytul(t),liczba_stron(l){}; //Konstruktor tworzący konkretny obiekt

};
void Ksiazka::zapisz(ofstream &plik) // reimplementacja funkcji "zapisz"
{
	cout<<"zapisuje ksiazke "<<tytul<<endl; //dla testu
	plik<<ID_Ksiazki<<endl; //najpierw zapisz id
	plik<<tytul<<endl;		//potem wszystkie charakterystyczne dla obiektu pola
	plik<<liczba_stron<<endl;
};

void Ksiazka::odczytaj(ifstream &plik) //reimplementacja funkcji "zapisz", uruchamia się ją juz po odczytaniu konkretnego id
{

	plik.ignore(128,10); //zabazpieczenie "doczytania" do końca linii po odczytaniu id
	getline(plik,tytul);
	plik>>liczba_stron;

};

void Ksiazka::przedstaw_sie() //funkcja prezentująca konkretny obiekt, dla testów itp...
{
	cout<<"Jestem "<<tytul<<endl;
};

class Film : public Obiekty
{
	public:
	string tytul;
	string format;
	void zapisz(ofstream &);
	void odczytaj(ifstream &);
	void przedstaw_sie();
	Film(): Obiekty(2),tytul("pusto"),format("brak") {};
	Film(string t,string f) : Obiekty(2),tytul(t),format(f)
	{};
};

void Film::zapisz(ofstream &plik)
{
	cout<<"zapisuje film "<<tytul<<endl;
	plik<<ID_Filmu<<endl;
	plik<<tytul<<endl;
	plik<<format<<endl;
};

void Film::odczytaj(ifstream &plik)
{

	plik.ignore(128,10);
	getline(plik,tytul);
	plik>>format;

};

void Film::przedstaw_sie()
{
	cout<<"Jestem "<<tytul<<" format: "<<format<<endl;
};


void Odczytywanie(Obiekty *baza_danych[],string nazwa_pliku) //funkcja odpowiedzialna za odczytywanie roznych obiektow z pliku
		//i umieszczanie ich adresów we wspolnej tablicy "baza_danych[]"
{
  int element_i=0;
  ifstream plik;
  cout<<"test!";
  plik.open(nazwa_pliku.c_str());
  while(plik.good())
  {
    int kolejne_id=0;
	    plik>>kolejne_id;		// najpierw wczytaj id

									// zapewne oznacza to, że już koniec pliku
	    cout<<kolejne_id<<"<-id  nr_elementu->"<<element_i<<endl; //dla testu na ekran
        if(!plik.good()) break; //jeśli nie udało się odczytać id to wyjdz z petli

	  Obiekty *wsk_czytanego_ob=NULL; //Deklarujemy pusty wskaźnik typu Obiekty*
		switch(kolejne_id) //Odczytaliśmy id obiektu i na tej podstawie wiemy jaki obiekt utworzyć
		{
		case ID_Ksiazki:
			{
			wsk_czytanego_ob = new Ksiazka;
			break;
			}
		case ID_Filmu:
			{
			wsk_czytanego_ob = new Film;
			break;
			}
	// TO DO:
	//	case ID_nowego_typu :
	//	    {
	//		wsk_czytanego_ob = new Nowy_obiekt;
	//		break;
	//		}

		default :
			cout<<"problem!!! nie wiadomo co to jest! Id nieznane!" <<endl;
		}

		wsk_czytanego_ob->odczytaj(plik); //tutaj wywołujemy funkcję dla obiektu wskazywanego przez wskażnik
											// dzieki polimorfizmowi wywołą się odpowiednia funkcja
		baza_danych[element_i]=wsk_czytanego_ob;
		element_i++;
  }
  plik.close();
}


int main(void)
{
	Ksiazka k1("Pan Tadeusz",112),k2("Tylko dla orlow",148);  //deklaracja obiektow dla testów
	Film f1("2001: Odyseja kosmiczna","dvd");
	Film f2("Gwiezdne wojny 1","mp4");

	Obiekty* baza_obiektow[10];						// statyczna deklaracja tablic wskaźników
	Obiekty* baza_obiektow_przeczytanych[10];		//

    baza_obiektow[0]=&k1;    //dla testu wpisujemy wskaźniki odpowiednich obiektów do bazy
    baza_obiektow[1]=&f1;
    baza_obiektow[2]=&k2;
    baza_obiektow[3]=&f2;


    int Ilosc_obiektow=4;

// zapisywanie do pliku
	ofstream plik("Out.txt");
if(plik.good())
	{
	for(int i=0;i<Ilosc_obiektow;i++)
		baza_obiektow[i]->zapisz(plik); //niezależnie od typu każdy obiekt zapisze się poprawnie (znowu dzieki polimorfizmowi)

		plik.close();
	}
// odczytywanie z pliku
Odczytywanie(baza_obiektow_przeczytanych,"Out.txt");

// sprawdzanie czy w bazie obiektow przeczytanych faktycznie znajdują się nasze obiekty
for(int i=0;i<Ilosc_obiektow;i++)
	baza_obiektow_przeczytanych[i]->przedstaw_sie();

return 0;
}


// Zadania:
// 1) Dodać i przetestować koleny typ obiektu, np. Czasopismo, Program, Utwór muzyczny itp...
//    Program powinien go odpowiednio zapisywać i odczytywać podobnie jak to ma miejsce dla Ksiazki i Filmu
// 2) Wymyślić i zapisać procedurę szukania (można to bardzo różnie potraktować, np podawać rodzaj i tutuł)
// 3) Zmodyfikować program tak aby odczytywał on istniejącą bazę, pozwalał na jej modyfikacje i zapisywał wynikową bazę
//


