/* Lecture Code 15.0
 *
 * The Engines example, which showcases a large number of inheritance
 * techniques.  Comments have been added to the relevant sections.
 */

#include <iostream>
#include <vector>
#include <string>
using namespace std;

/* Base class of all Engines.  All engines have a name and can go vroom. */
class Engine
{
public:
	virtual ~Engine() {} // Polymorphic classes need virtual dtors.

	/* Engines by default have no name, but we can explicitly say
	 * what name we want if we choose.
	 */
	explicit Engine(const string& name = "");

	/* Engines go vroom.  Note that this function is virtual so
	 * that derived classes can override it.
	 */
	virtual void vroom() const;

	/* Every engine has a name, which could be the empty string. */
	string name() const;

	/* A virtual function which duplicates the Engine.  This is sometimes
	 * referred to as the "virtual clone" or "virtual constructor" idiom
	 * and allows us to construct deep-copies of elements in a class
	 * hierarchy without having to consider what the type is; this works
	 * by asking every type to know how to clone itself.
	 */
	virtual Engine* clone() const
	{
		/* 'this' is a pointer to the receiver object.
		 * '*this' is the receiver object.
		 * 'new Engine(*this)' thus invokes the automatically-generated
		 * copy constructor to create a copy of this object.
		 */
		return new Engine(*this);
	}

private:
	string mName;
};

/* Engine constructor sets our name to a deep-copy of the specified
 * name.
 */
Engine::Engine(const string& name)
{
	mName = name;
}

/* Default engine vroom noise is "Vroom!" */
void Engine::vroom() const
{
	cout << "Vroom!" << endl;
}

/* Returning the name just hands it back. */
string Engine::name() const
{
	return mName;
}

/* An engine that uses a flux capacitor to travel through time.
 * If you don't get the reference, watch "Back to the Future."
 * You will not be disappointed.
 */
class DeLoreanEngine: public Engine
{
public:
	/* Constructor says that we are a DeLorean engine. */
	DeLoreanEngine(): Engine("DeLorean Engine")
	{
	}

	/* Vrooming this engine travels through time! */
	virtual void vroom() const
	{
		cout << "Back... to the future!" << endl;
	}

	/* Implementation of the clone function.  Note that the function
	 * returns a DeLoreanEngine* instead of an Engine*, though in the
	 * base class the function returns an Engine*.  This is okay because
	 * of return type covariance, which means that if a function returns
	 * a Base* you can override it with a function that returns a
	 * Derived*.
	 */
	virtual DeLoreanEngine* clone() const
	{
		return new DeLoreanEngine(*this);
	}
};


/* A car engine. */
class CarEngine: public Engine
{
public:
	/* Default constructor makes us have the name Car Engine. */
	CarEngine() : Engine("Car Engine") {}

	/* Cars go vroom at 65 mph! */
	virtual void vroom() const
	{
		cout << "Vroom at 65 mph!" << endl;
	}

	virtual CarEngine* clone() const
	{
		return new CarEngine(*this);
	}
};

/* Jet engines are like engines but MUCH LOUDER. */
class JetEngine: public Engine
{
public:
	JetEngine() : Engine("Jet Engine") {}

	/* Recall that \a is the beep character... this will make a lot of
	 * noise!
	 */
	virtual void vroom() const
	{
		cout << "\aVVVRRROOOOOOOOOMMM!!!\a" << endl;
	}

	virtual JetEngine* clone() const
	{
		return new JetEngine(*this);
	}
protected:

	/* This constructor exists so that subclasses of JetEngine can
	 * construct themselves to have a name other than 'Jet Engine.'
	 * It's marked protected so that clients of JetEngine can't access
	 * it, but subclasses can.
	 */
	explicit JetEngine(const string& name) : Engine(name)
	{
	}
};

/* A gold-plated jet engine that makes Bill jealous. */
class GoldPlatedJetEngine: public JetEngine
{
public:
	/* Construct ourselves using the protected JetEngine constructor
	 * so that the engine name is 'Gold-plated Jet Engine' rather than
	 * the less specific 'Jet Engine.'
	 */
	GoldPlatedJetEngine() : JetEngine("Gold-plated Jet Engine")
	{
	}

	/* GoldPlatedJetEngines do exactly the same things as JetEngines,
	 * but they cost more.
	 */
	virtual void vroom() const
	{
		/* Invoke JetEngine's vroom function.  This bypasses virtual
		 * function lookup, so it always calls the JetEngine version.
		 * If we wanted to call further up in the chain (i.e. Engine's
		 * vroom), we could use the same syntax to do so.
		 */
		JetEngine::vroom();
		cout << "(except more expensive)" << endl;
	}

	virtual GoldPlatedJetEngine* clone() const
	{
		return new GoldPlatedJetEngine(*this);
	}

};

/* A device for measuring engine power use */
class EnginePowerMonitor
{
public:
	/* Constructs an InstrumentedEngine that instruments a particular engine. */
	explicit EnginePowerMonitor(Engine* e);

	/* Deallocates the engine. */
	EnginePowerMonitor(const EnginePowerMonitor&);
	EnginePowerMonitor& operator= (const EnginePowerMonitor&);
	~EnginePowerMonitor();

	/* Tests power usage. */
	void testPowerUsage() const;

private:

	void clear();
	void copyOther(const EnginePowerMonitor&);
	Engine* e;
};

/* Constructor accepts an Engine for later use. */
EnginePowerMonitor::EnginePowerMonitor(Engine* engine) : e(engine)
{
}

/* Power monitor dtors cleans up the engine it monitors. */
EnginePowerMonitor::~EnginePowerMonitor()
{
	clear();
}

/* Deallocates the EnginePowerMonitor. */
void EnginePowerMonitor::clear()
{
	delete e;
}

/* Copy constructor and assignment operator are standard fare. */
EnginePowerMonitor::EnginePowerMonitor(const EnginePowerMonitor& other)
{
	copyOther(other);
}
EnginePowerMonitor& EnginePowerMonitor::operator= (const EnginePowerMonitor& other)
{
	if(this != &other)
	{
		clear();
		copyOther(other);
	}
	return *this;
}

/* To deep-copy an EnginePowerMonitor, we invoke clone() on the stored
 * resource of the other EnginePowerMonitor.  This gives us back a correct
 * copy of its Engine.
 */
void EnginePowerMonitor::copyOther(const EnginePowerMonitor& other)
{
	e = other.e->clone();
}

/* Goes vroom, then reports how much power was used. */
void EnginePowerMonitor::testPowerUsage() const
{
	cout << "Testing power use of: " << e->name() << endl;
	e->vroom();

	/* We need to check whether the Engine is a DeLoreanEngine so that
	 * we can tell whether it uses 1.21 GW.  The dynamic_cast operator
	 * is perfect here since it performs a typecast and returns NULL
	 * if the cast fails.
	 */
	if(DeLoreanEngine* dle = dynamic_cast<DeLoreanEngine*>(e))
		cout << "1.21 Gigawatts!  Great Scott!" << endl;
	else
		cout << "This computer still has battery life, so it must not have used too much power." << endl;
}

int main()
{
	EnginePowerMonitor monitor(new DeLoreanEngine);
	monitor.testPowerUsage();
	return 0;
}
