#include <iostream>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <atomic>
using namespace std;

namespace CyberNet{
	template <class T>
	class Point3D{
		public:
			T x, y, z;
			Point3D(T startX=0, T startY=0, T startZ=0){
				x=startX;
				y=startY;
				z=startZ;
			}
			double getEuclideanDistance(Point3D<T> target){
				return sqrt(pow(x-target.x,2)+pow(y-target.y,2)+pow(z-target.z,2));
			}
			T getManhattanDistance(Point3D<T> target){
				return abs(x-target.x)+abs(y-target.y)+abs(z-target.z);
			}
	};

	class Hacker{
		private:
			string alias;
			int maxExploits;
			int* exploits;
			Hacker();
		public:
			static int activeHackers;
			Hacker(string name, int exploitCount){
				alias=name;
				maxExploits=exploitCount;
				exploits=new int[maxExploits];
				for(int i=0;i<maxExploits;i++){
					exploits[i]=10+(rand()%40);
				}
				activeHackers++;
			}
			Hacker(const Hacker& obj){
				this->alias=obj.alias+"_ShadowClone";
				this->maxExploits=obj.maxExploits;
				this->exploits=new int[this->maxExploits];
				for(int i=0;i<this->maxExploits;i++){
					this->exploits[i]=obj.exploits[i];
				}
				activeHackers++;
			}
			~Hacker(){
				delete[] exploits;
				activeHackers--;
			}
			inline string getAlias(){return alias;}
			int useExploit(int index){
				if(index>=0&&index<maxExploits&&exploits[index]>0){
					int power=exploits[index];
					exploits[index]=0;
					return power;
				}
				return 0;
			}
			void showInventory(){
				cout<<"\n[>] Viruses available to "<<alias<<":\n";
				for(int i=0;i<maxExploits;i++){
					if(exploits[i]>0) cout<<"    Virus ["<<i<<"] - Power: "<<exploits[i]<<"TB"<<endl;
					else cout<<"    Virus ["<<i<<"] - [Depleted]"<<endl;
				}
			}
	};

	int Hacker::activeHackers=0;

	class BankMainframe{
		private:
			BankMainframe(){
				cout<<"\n[SYSTEM] The Bank Mainframe was activated."<<endl;
			}
			BankMainframe(const BankMainframe&)=delete;
			BankMainframe& operator=(const BankMainframe&)=delete;
			static BankMainframe* instance;
		public:
			volatile bool systemLockdown=false;
			atomic<int> failedAttempts{0};
			static BankMainframe* getInstance(){
				if(instance==nullptr){
					instance=new BankMainframe();
				}
				return instance;
			}
			bool isFirewallPortOpen(int portKey){
				if(portKey<=1) return false;
				auto sqRoot=sqrt(portKey);
				bool is_prime=true;
				int i=2;
				while(i<=sqRoot){
					if(portKey%i==0){
						is_prime=false;
						break;
					}
					++i;
				}
				return is_prime;
			}
			bool receiveAttack(int power,int portKey){
				if(systemLockdown){
					cout<<"   [!] ACCESS DENIED: The system is in LOCKDOWN!"<<endl;
					return false;
				}
				if(isFirewallPortOpen(portKey)){
					cout<<"   [+] SUCCESS: Port ["<<portKey<<"] was open!"<<endl;
					cout<<"   [+] The Virus power "<<power<<" TB went into the system!"<<endl;
					return true;
				} else{
					cout<<"   [-] FAILURE: Port ["<<portKey<<"] blocked the attack"<<endl;
					failedAttempts++;
					if(failedAttempts>=3){
						systemLockdown=true;
						cout<<"\n   [!!!] CRITICAL: Multiple cyber attacks have been detected! LOCKDOWN ACTIVATION! [!!!]"<<endl;
					}
					return false;
				}
			}
	};

	BankMainframe* BankMainframe::instance=nullptr;
}

using namespace CyberNet;

int main(){
	srand(time(NULL));
	clock_t sessionStart=clock();
	cout<<"\n===================================================="<<endl;
	cout<<"  [***] CYBER-HEIST SIMULATOR v1.0 [***]   "<<endl;
	cout<<"====================================================\n"<<endl;
	string playerName;
	cout<<"> Choose your Hacker Alias: ";
	cin>>playerName;
	Hacker player(playerName, 3);
	cout<<"\n[+] Welcome, "<<player.getAlias()<<". You are connected to DarkNet."<<endl;
	Point3D<int> playerLocation(0,0,0);
	Point3D<int> bankLocation(100,50,25);
	BankMainframe* targetBank=BankMainframe::getInstance();
	bool isConnected=true;
	int choice;
	while(isConnected){
		if(targetBank->systemLockdown){
			cout<<"\n[!!!] CONNECTION TERMINATED: The system found you! Game Over! [!!!]\n"<<endl;
			break;
		}
		cout<<"\n-------------------------------------------------------"<<endl;
		cout<<"   CHOICE MENU: "<<endl;
		cout<<"   1. Scan Net"<<endl;
		cout<<"   2. View Viruses"<<endl;
		cout<<"   3. Proceed to Cyber attack"<<endl;
		cout<<"   4. Disconnect"<<endl;
		cout<<"-------------------------------------------------------"<<endl;
		cout<<"> Choose (1-4): ";
		cin>>choice;
		switch(choice){
			case 1:{
				cout<<"\n[>] Scanning target: 'Central Bank'..."<<endl;
				double eucDist=playerLocation.getEuclideanDistance(bankLocation);
				int manDist=playerLocation.getManhattanDistance(bankLocation);
				cout<<"   - Euclidean Distance: "<<eucDist<<" ms"<<endl;
				cout<<"   - Manhattan Distance: "<<manDist<<" nodes"<<endl;
				break;
			}
			case 2:{
				player.showInventory();
				break;
			}
			case 3:{
				cout<<"\n[!] PREPARING ATTACK [!]"<<endl;
				player.showInventory();
				int virusIndex;
				cout<<"> Choose the number of the virus you will send (0, 1 or 2): ";
				cin>>virusIndex;
				int power=player.useExploit(virusIndex);
				if(power==0){
					cout<<"[-] Error: This virus doesn't exist or has already been used!"<<endl;
				} else{
					int targetPort;
					cout<<"> In order to enter the Firewall, give an 'Open Port'."<<endl;
					cout<<"  (Hint: It must be a Prime Number): ";
					cin>>targetPort;
					cout<<"\n[>] Firing Virus of power "<<power<<" TB to Port "<<targetPort<<"..."<<endl;
					bool success=targetBank->receiveAttack(power,targetPort);
					if(success){
						cout<<"\n[$$$] JACKPOT! The Bank has fallen! Disconnecting with cash! [$$$]"<<endl;
						isConnected=false;
					}
				}
				break;
			}
			case 4:{
				cout<<"\n[>] Safe disconnecting..."<<endl;
				isConnected=false;
				break;
			}
			default:
				cout<<"\n[-] Non valid choise."<<endl;
				break;
		}
	}
	clock_t sessionEnd=clock();
	double sessionDuration=double(sessionEnd-sessionStart)/CLOCKS_PER_SEC;
	cout<<"\n========================================================="<<endl;
	cout<<"Session Duration: "<<sessionDuration<<" seconds."<<endl;
	cout<<"[SYSTEM]: Detroying Evidence..."<<endl;
	return 0;
}


