/* Lecture code 8.1
 *
 * Engines example.
 */

#include <iostream>
#include <vector>
#include <cstring>
#include <string>
using namespace std;

class Engine
{
public:
	Engine();
	explicit Engine(const char *name);
	Engine(const Engine &other);
	Engine& operator= (const Engine &other);
	virtual ~Engine();

	virtual void vroom() const;
	const char *getEngineName() const;

private:
	void copyOther(const Engine &other);
	void clear();

	char *name;
};

class CarEngine: public Engine
{
public:
	CarEngine() : Engine("Car Engine") {}
		
	/* Need the const here or it doesn't work correctly. */
	virtual void vroom() const
	{
		cout << "Vroom at 65 mph!" << endl;
	}
};

class SportsCarEngine: public CarEngine
{
public:
	/* Import CarEngine::vroom into the scope so it isn't hidden. */
	using CarEngine::vroom;
	void vroom(int numTimes)
	{
		for(int i = 0; i < numTimes; i++)
			cout << "Revvvvvv" << endl;
	}
};

class JetEngine: public Engine
{
public:
	JetEngine() : Engine("Jet Engine") {}
	virtual void vroom() const
	{
		cout << "\x07VVVRRROOOOOOOOOMMM!!!\x07" << endl;
	}
};

class GoldPlatedJetEngine: public JetEngine
{
public:
	virtual void vroom() const
	{
		/* Invoke superclass virtual function. */
		JetEngine::vroom();
		cout << "(Except it costs more.)" << endl;
	}
};

Engine::Engine()
{
	this->name = new char[1];
	strcpy(this->name, "");
}

Engine::Engine(const char *name)
{
	this->name = new char[strlen(name) + 1];
	strcpy(this->name, name);
}

void Engine::copyOther(const Engine &other)
{
	this->name = new char[strlen(other.name) + 1];
	strcpy(this->name, other.name);
}

Engine::Engine(const Engine &other)
{
	copyOther(other);
}

Engine& Engine::operator =(const Engine &other)
{
	if(this != &other)
	{
		clear();
		copyOther(other);
	}
	return *this;
}

Engine::~Engine()
{
	clear();
}

void Engine::clear()
{
	delete [] name;
	name = NULL;
}

void Engine::vroom() const
{
	cout << "Vroom!" << endl;
}

const char *Engine::getEngineName() const
{
	return name;
}

class PersonalEngine: public Engine
{
public:
	PersonalEngine(const char *engineName, const char *yourName);
	PersonalEngine(const PersonalEngine &other);
	PersonalEngine& operator= (const PersonalEngine &other);
	~PersonalEngine();

	const char* getOwnerName() const
	{
		return myName;
	}

private:
	void copyOther(const PersonalEngine &other);
	void clear();
	char *myName;
};

PersonalEngine::PersonalEngine(const char *engineName, const char *name) :
    Engine(engineName)
{
	myName = new char[strlen(name) + 1];
	strcpy(myName, name);
}

void PersonalEngine::copyOther(const PersonalEngine &other)
{
	this->myName = new char[strlen(other.myName) + 1];
	strcpy(this->myName, other.myName);
}

PersonalEngine::PersonalEngine(const PersonalEngine &other)
    : Engine(other) // Call Engine's copy constructor.
{
	copyOther(other);
}

PersonalEngine& PersonalEngine::operator =(const PersonalEngine &other)
{
	if(this != &other)
	{
		clear();
		/* Invoke Engine's assignment operator. */
		Engine::operator =(other);
		copyOther(other);
	}
	return *this;
}

PersonalEngine::~PersonalEngine()
{
	clear();
}

void PersonalEngine::clear()
{
	delete [] myName;
	myName = NULL;
}

/* WARNING: This slices the Engine.  Be careful! */
void GoVroom(Engine e)
{
	e.vroom();
}