#include <iostream>
#include <iomanip>
#include <string>

using namespace std;

//Κλήση με διεύθυνδη
void changeByPointer(float *cvar){
	if(cvar!=nullptr){
		cout<<"   [Pointer Function] Initial value in address "<<cvar<<" is: "<<*cvar<<endl;
		*cvar=*cvar+10.0;
		cout<<"   [Pointer Function] The new value is: "<<*cvar<<endl;
	}
}

//Κλήση με αναφορά
void changeByReference(float &cvar){
	cout<<"   [Reference Function] Initial value is: "<<cvar<<endl;
	cvar=cvar+10.0;
	cout<<"   [Reference Function] The new value is: "<<cvar<<endl;
}

void demonstrateBasics(){
	cout<<"\n--- 1. ΒΑΣΙΚΕΣ ΕΝΝΟΙΕΣ ΔΕΙΚΤΩΝ ---"<<endl;
	int V=101;
	int *P=&V;//Ο P είναι δείκτης που δείχνει στη διεύθυνση της V
	cout<<"Το & επιστρέφει τη διεύθυνση της μεταβλητής: &V="<<&V<<endl;
	cout<<"Το * επιστρέφει την τιμή της μεταβλητής μέσω του δείκτη: *P="<<*P<<endl;
	cout<<"Τιμή του δείκτη P: "<<P<<endl;
	cout<<"Επίσης με το * αλλάζουμε την τιμή της μεταβλητής μέσω του δείκτη."<<endl;
}

void demonstrateSizes(){
	cout<<"\n--- 2. ΜΕΓΕΘΗ ΔΕΙΚΤΩΝ ΚΑΙ ΤΥΠΩΝ ---"<<endl;
	int a=1;
	char b='a';
	short int m=3;
	long double o=4.3;
	//Όλοι οι δείκτες έχουν το ίδιο μέγεθος: sizeof(void *)
	void *p_void=&a;
	int *p_int=&a;
	char *p_char=&b;
	short int *p_short=&m;
	long double *p_double=&o;
	cout<<"Int: "<<sizeof(a)<<" bytes."<<endl;
	cout<<"Pointer of int: "<<sizeof(p_int)<<" bytes."<<endl;
	cout<<"Char: "<<sizeof(b)<<" bytes."<<endl;
	cout<<"Pointer of char: "<<sizeof(p_char)<<" bytes."<<endl;
	cout<<"Long double: "<<sizeof(o)<<" bytes."<<endl;
	cout<<"Pointer of long double: "<<sizeof(p_double)<<" bytes."<<endl;
	cout<<"Short int: "<<sizeof(m)<<" bytes."<<endl;
}

void demonstratePointerComparison(){
	cout<<"\n--- 3. ΣΥΓΚΡΙΣΕΙΣ ΚΑΙ ΑΝΑΘΕΣΕΙΣ ΔΕΙΚΤΩΝ ---"<<endl;
	int A=3;
	int B=5;
	int *P=&A;
	int *Q=P;//Ο Q δείχνει εκεί που δείχνει και ο P
	int *R=&B;
	cout<<"A="<<A<<", B="<<B<<endl;
	cout<<"P δείχνει στην A, Q δείχνει στην Α και ο R δείχνει στην Β."<<endl;
	cout<<"\nΤιμές μέσω δεικτών: "<<*P<<", "<<*Q<<", "<<*R<<endl;
	if(P==Q){
		cout<<"- Η συνθήκη (P==Q) είναι ΑΛΗΘΗΣ: Δείχνουν στην ίδια διεύθυνση μνήμης."<<endl;
	}
	if(P!=R){
		cout<<"- Η συνθήκη (P!=R) είναι ΑΛΗΘΗΣ: Δείχνουν σε διαφορετικές διευθύνσεις."<<endl;
	}
	if(*P!=*R){
		cout<<"- Η συνθήκη (*P!=*R) είναι ΑΛΗΘΗΣ: Οι τιμές στις οποίες δείχνουν είναι διαφορετικές."<<endl;
	}
	cout<<"\nΑλλάζουμε τον Q να δείχνει στην Β: Q=&B"<<endl;
	Q=&B;
	if(Q==R){
		cout<<"- Πλέον η συνθήκη (Q==R) είναι ΑΛΗΘΗΣ."<<endl;
	}
	if(*Q==*R){
		cout<<"- Και προφανώς η συνθήκη (*Q==*R) είναι ΑΛΗΘΗΣ."<<endl;
	}
}

void demonstrateFunctionCalls(){
	cout<<"\n--- 4. ΚΛΗΣΗ ΜΕ ΔΙΕΥΘΥΝΣΗ VS ΑΝΑΦΟΡΑ ---"<<endl;
	float x1=5.0;
	float x2=5.0;
	cout<<"Έχουμε δύο μεταβλητές: x1="<<x1<<" και x2="<<x2<<endl;
	cout<<"\n-> changeByPointer(&x1):"<<endl;
	changeByPointer(&x1);
	cout<<"Μετά τη συνάρτηση: x1="<<x1<<endl;
	cout<<"\n-> changeByReference(x2):"<<endl;
	changeByReference(x2);
	cout<<"Μετά τη συνάρτηση: x2="<<x2<<endl;
	cout<<"\nΚαι οι δύο μέθοδοι αλλάζουν την αρχική μεταβλητή, αλλά η 'Reference' είναι πιο καθαρή σθντακτικά."<<endl;
}

void showMenu(){
	cout<<"\n==========================================="<<endl;
	cout<<"   ΕΚΠΑΙΔΕΥΤΙΚΟ ΜΕΝΟΥ ΔΕΙΚΤΩΝ (ΜΕΡΟΣ 1)"<<endl;
	cout<<"==========================================="<<endl;
	cout<<"1. Βασικές Έννοιες (&, *, nullptr)"<<endl;
	cout<<"2. Μεγέθη Δεικτών (sizeof)"<<endl;
	cout<<"3. Σύγκριση και Ανάθεση Δεικτών"<<endl;
	cout<<"4. Pointers vs References σε Συναρτήσεις"<<endl;
	cout<<"0. Έξοδος"<<endl;
	cout<<"==========================================="<<endl;
	cout<<"Επίλεξε μια λειτουργεία (0-4): ";
}

int main(){
	int choice=-1;
	cout<<"Καλωσήρθες στον Προσομοιωτή Δεικτών της C++!"<<endl;
	cout<<"Ο κώδικας αυτός υλοποιεί τις έννοιες του Memory Stack και Pointer Basics."<<endl;
	while(choice!=0){
		showMenu();
		cin>>choice;
		if(cin.fail()){
			cin.clear();
			cin.ignore(10000, '\n');
			cout<<"Παρακαλώ εισάγεται έναν έγκυρο αριθμό."<<endl;
			continue;
		}
		switch(choice){
			case 1:
				demonstrateBasics();
				break;
			case 2:
				demonstrateSizes();
				break;
			case 3:
				demonstratePointerComparison();
				break;
			case 4:
				demonstrateFunctionCalls();
				break;
			case 0:
				cout<<"\nΤερματισμός προγράμματος... Σε περιμένω για το Μέρος 2!"<<endl;
				break;
			default:
				cout<<"Μη έγκυρη επιλογή. Προσπάθησε ξανά."<<endl;
				break;
		}
	}
	return 0;
}

