#include <iostream>
#include <stdexcept>
using namespace std;

class SmartArray{
	private:
		int* data;
		int size;
	public:
		SmartArray(int s){
			size=s;
			data=new int[size];
			for(int i=0;i<size;i++){
				data[i]=0;
			}
		}
		~SmartArray(){delete[] data;}

		//1. Τελεστης operator[] (Γρηγορος, χωρις ελεγχο οριων)
		//Εκδοχη Α: Κανονικη (επιστρεφει αναφορα & για να μπορουμε να ΓΡΑΨΟΥΜΕ)
		int& operator[](int index){
			return data[index];
		}

		//Εκδοχη Β: Const (επιστρεφει const αναφορα για να μπορουμε μονο να ΔΙΑΒΑΣΟΥΜΕ)
		const int& operator[](int index) const{
			return data[index];
		}
		//2. Συναρτηση at() (Ασφαλης, με ελεγχο οριων)
		int& at(int index){
			//Ελεγχει αν το index ειναι εκτος οριων
			if(index<0||index>=size){
				throw out_of_range("Σφαλμα: Προσπελαση εκτος οριων του πινακα");
			}
			return data[index];
		}
		const int& at(int index) const{
			if(index<0||index>size){
				throw out_of_range("Σφαλμα: Προσπελαση εκτος οριων του πινακα");
			}
			return data[index];
		}
		int getSize() const{
			return size;
		}
};
//Πως δουλεβουν στην πραξη
void printFirstElement(const SmartArray& arr){
	//ΕΔΩ καλειται η const εκδοχη του operator[] αυτοματα
	cout<<"Το πρωτο στοιχειο ειναι: "<<arr[0]<<endl;
	//Αν εγραφα arr[0]=5; ο compiler θα πετουσε error λογω του const
}

class Player{
	private:
		string name;
		int score;
	public:
		Player(string n="Unknown",int s=0){
			name=n;
			score=s;
		}
		//Με τους δυο παρακατω τελεστες εκπαιδευω το cin και το cout ωστε να συμπεριφερονται στα δικα μου αντικειμενα
		//σαν να ηταν απλοι αριθμοι
		//1. Τελεστης εκτυπωσης
		friend ostream& operator<<(ostream& os,const Player& obj){
			//Γραφω ακριβως πως θελω να τυπωνεται το αντικειμενο
			os<<"[Player: "<<obj.name<<" | Score: "<<obj.score<<"]";
			return os;
		}

		//2. Τελεστης εισαγωγης
		friend istream& operator>>(istream& is,Player& obj){
			cout<<"  -> Give player name: ";
			is>>obj.name;
			cout<<"  -> Give player score: ";
			is>>obj.score;
			return is;
		}
};

int main(){
	SmartArray myArray(5);
	//Χρηση του operator[]
	//Εδω επιστρεφει αναφορα (&) αρα μπορουμε να βαλουμε '=' και να δωσουμε τιμη
	myArray[0]=10;
	myArray[1]=20;
	cout<<"Θεση 1: "<<myArray[1]<<endl;
	//Χρηση της at()
	myArray.at(2)=30;//Δουλευει ακριβως οπως το []
	cout<<"Θεση 2: "<<myArray.at(2)<<endl;
	//Τεστ Ασφαλειας
	//Το παρακατω θα δωσει σκουπιδια χωρις προειδοποιηση
	//cout<<"Λαθος με []: "<<myArray[100]<<endl;
	//Η at() ομως οχι με τη βοηθεια του try-catch
	try{
		cout<<"Θεση 100 με at(): "<<endl;
		myArray.at(100)=50;
	}
	catch(const out_of_range& error){
		cout<<"Το προγραμμα σωθηκε απο κρασαρισμα! Μυνημα: "<<error.what()<<endl;
	}
	//-------------------------------------------------------------------------------
	//Χρηση των istream, ostream
	Player p1;
	Player p2("Mario",500);
	//isream: Αντι για p1.setName(x) και p1.setScore(y) γραφουμε:
	cin>>p1;
	//ostream: Αντι για p1.getName() και p1.getScore() γραφουμε:
	cout<<p1<<" versus "<<p2<<endl;
	//Το return os; πυ βαλαμε στον ορισμο του ostream μας επιτρεπει να βαλουμε πολλα "<<" το ενα διπλα στο αλλο
	return 0;
}

/*ΧΡΗΣΗΜΟΤΗΤΑ ΑΥΤΩΝ ΤΩΝ OPERATORS
-Κανονικα, ετσι βαζουμε και διαβαζουμε το 3ο στοιχειο ενος πινακα-αντικειμενο:
Playlist myMusic(10);
myMusic.setSong(3,"Bohemian Rhapsody");
cout<<myMusic.getSong(3);
-Ομως με το operator[] συμπεριφερεται σαν απλος πινακας:
Playlist myMusic(10);
myMusic[3]="Bohemian Rhapsody";
cout<<myMusic[3];
-Και τελος, με την at() εχουμε και την ασφαλεια
*/

