#include <iostream>
#include <cstdlib>
using std::cout;
using std::endl;

class BankAccount {
public:
	BankAccount(double balance) : balance(balance) {}
	~BankAccount() {}
	
	const double &getBalance() const { return balance; }
	const double &deposit(double amt) { return (balance += amt); }
	const double &withdraw(double amt) {
		if (amt > balance) {
			exit(EXIT_FAILURE);
		}
		else {
			return (balance -= amt);
		}
	}
protected:
	double balance;
};

class SavingsAccount : public BankAccount {
public:
	/* rate must be set in an initializer since it is a const */
	SavingsAccount(double balance, double rate) :
		BankAccount(balance), rate(rate) {}
	~SavingsAccount() {}
	
	const double& accrueInterest() { return (balance *= (1.0+rate)); }

private:
	const double rate;
};

class ATMAccount : public BankAccount {
public:
	ATMAccount( double balance ) : BankAccount( balance ) {}
	~ATMAccount() {}
	
	/* withdraw method overloaded in derived class */
	const double &withdraw(double amt) {
		if (amt + fee > balance) {
			exit(EXIT_FAILURE);
		}
		else {
			return (balance -= (amt+fee));
		}
	}

private:
	/* static since all ATM fees will be the same */
	static const double fee = 1.0;
}; 

int main()
{
	BankAccount Sally( 42.0 );
	SavingsAccount Jill( 73.0 , 0.004166 );
	ATMAccount Ralph( 400.0 );
	
	cout << Sally.getBalance() << endl;
	cout << Jill.getBalance() << endl;
	cout << Ralph.getBalance() << endl;
	cout << endl;
	/* behavior is fine for calling withdraw method in all three cases */

	cout << Sally.withdraw( 10 ) << endl;
	cout << Jill.withdraw( 10 ) << endl;
	cout << Ralph.withdraw( 10 ) << endl;
	cout << endl;

	/* we can use pointers to base class to point to derived classes */
	BankAccount *a1 = &Sally;
	BankAccount *a2 = &Jill;
	BankAccount *a3 = &Ralph;
	
	/* But what about this? */
	//SavingsAccount *b1 = &Sally;
		
	/* lets withdraw $10 from each account */
	cout << a1->withdraw( 10 ) << endl;
	cout << a2->withdraw( 10 ) << endl;
	cout << a3->withdraw( 10 ) << endl;  /* oops */
	cout << endl;
	
	/* and what about this? */
	//a2->accrueInterest();
	
	/* lets try that again */
	a3->deposit( 10 );
	cout <<	( (ATMAccount *) a3 )->withdraw(10) << endl;


	return 0;
}