// ==========================================================================
// $Id: rtti.cpp,v 1.1 2010/12/06 17:37:21 jlang Exp $
// CSI2372 example Code for lecture 14
// ==========================================================================
// (C)opyright:
//
//   Jochen Lang
//   SITE, University of Ottawa
//   800 King Edward Ave.
//   Ottawa, On., K1N 6N5
//   Canada. 
//   http://www.site.uottawa.ca
// 
// Creator: jlang (Jochen Lang)
// Email:   jlang@site.uottawa.ca
// ==========================================================================
// $Log: rtti.cpp,v $
// Revision 1.1  2010/12/06 17:37:21  jlang
// Added example for rtti
//
//
// ==========================================================================
#include <iostream>
#include <string>
#include <stdexcept>
#include <typeinfo>

using std::cout;
using std::endl;
using std::string;


/** 
 * Parent class of all vehicles
 */
class Vehicle {
  string d_engine;
public:
  Vehicle(string _engine = "Default" );
  Vehicle(const Vehicle& _o );
  virtual ~Vehicle();
  virtual string getName();
protected:
  const string& getEngine(); 
};


/** 
 * Boat is a vehicle
 */
class Boat : public Vehicle {
  float d_displacement;
public:
  Boat( float _displacement = 0.0, string _engine = "Marine Diesel" );
  Boat( const Boat& _o );
  virtual ~Boat();
  virtual string getName();
};


/** 
 * SailBoat is a boat
 */
class SailBoat : public Boat {
  float d_displacement;
public:
  SailBoat( float _displacement = 0.0, string _engine = "Emergency" );
  SailBoat( const SailBoat& _o );
  virtual ~SailBoat();
  virtual string getName();
};


/** 
 * Bus is a vehicle
 */
class Bus : public Vehicle {
  int d_noPassengers;
public:
  Bus( int _noPassengers = 0, string _engine = "Green Diesel" );
  Bus( const Bus& _o );
  virtual ~Bus();
  virtual string getName();
};


//------------------------------------------------------
// Functions 
//------------------------------------------------------

/*
 * Boat functions
 */
Boat::Boat( float _displacement, string _engine ) 
  : Vehicle( _engine ), d_displacement( _displacement )
{ 
  cout << "Boat::Boat( " << _displacement << " )" << endl;
}


Boat::Boat( const Boat& _o ) 
  : Vehicle( _o ), d_displacement( _o.d_displacement )
{ 
  cout << "Boat::Boat(const Boat&)" << endl;
}


Boat::~Boat() {
  cout << "Boat::~Boat() " << endl; 
}

string Boat::getName() {
  return "Boat: " + getEngine();
}


/*
 * SailBoat functions
 */
SailBoat::SailBoat( float _displacement, string _engine ) 
  : Boat( _displacement, _engine )
{ 
  cout << "SailBoat::SailBoat( " << _displacement << " )" << endl;
}


SailBoat::SailBoat( const SailBoat& _o ) 
  : Boat( _o )
{ 
  cout << "SailBoat::SailBoat( const SailBoat& )" << endl;
}


SailBoat::~SailBoat() {
  cout << "SailBoat::~SailBoat() " << endl; 
}

string SailBoat::getName() {
  return "SailBoat: " + getEngine();
}


/*
 * Bus functions
 */
Bus::Bus( int _noPassengers, string _engine ) 
  : Vehicle( _engine ), d_noPassengers( _noPassengers ) 
{ 
  cout << "Bus::Bus( " << _noPassengers << " )" << endl;
}

Bus::Bus( const Bus& _o ) 
  : Vehicle( _o ), d_noPassengers( _o.d_noPassengers ) 
{ 
  cout << "Bus::Bus( const Bus& )" << endl;
}



Bus::~Bus() {
  cout << "Bus::~Bus()" << endl;
}


string Bus::getName() {
  return "Bus: " + getEngine();
}



/*
 * Vehicle functions
 */
Vehicle::Vehicle(string _engine) : d_engine( _engine )
{
  cout << "Vehicle::Vehicle( " << _engine << " )" << endl;
}


Vehicle::Vehicle(const Vehicle& _o ) : d_engine( _o.d_engine )
{
  cout << "Vehicle::Vehicle(const Vehicle& )" << endl;
}

Vehicle::~Vehicle() {
  cout << "Vehicle::~Vehicle()" << endl;
}

const string& Vehicle::getEngine() {
  return d_engine;
}

string Vehicle::getName() {
  return "Vehicle: " + getEngine();
}



/**
 * Storage container
 */
class VehicleGarage {
  int d_capacity;
  int d_size;
  Vehicle** d_garage;
public:
  VehicleGarage( int _capacity );
  VehicleGarage( const VehicleGarage& _oGar );
  ~VehicleGarage();
  const VehicleGarage& operator=( const VehicleGarage& _oGar );
  int park( Vehicle* _veh );  
  Vehicle* get();
};


VehicleGarage::VehicleGarage( int _capacity ) : 
  d_capacity(_capacity), d_size(0) {
  cout << "VehicleGarage::VehicleGarage(" << _capacity << ")" << endl; 
  if ( _capacity <= 0 ) throw std::invalid_argument("Capacity<0");
  if (!(d_garage = new Vehicle*[ d_capacity ]))  
       throw std::bad_alloc();
}

VehicleGarage::VehicleGarage( const VehicleGarage& _oGar) 
  : d_capacity(_oGar.d_capacity), d_size(_oGar.d_size) {
  cout << "VehicleGarage::VehicleGarage(const VehicleGarage& )" << endl; 
  if (!(d_garage = new Vehicle*[ d_capacity ]))
    throw std::bad_alloc();
  // Make a deep copy
  for ( int i=0; i<d_size; ++i ) {
    if ( typeid(*_oGar.d_garage[i])==typeid(Boat) ) {
      d_garage[i] = new Boat( *(dynamic_cast<Boat*>(_oGar.d_garage[i])));
      continue;
    }
    if ( typeid(*_oGar.d_garage[i])==typeid(SailBoat) ) {
      d_garage[i] = 
	new SailBoat( *(dynamic_cast<SailBoat*>(_oGar.d_garage[i])));
      continue;
    }
    if ( typeid(*_oGar.d_garage[i])==typeid(Bus) ) {
      d_garage[i] = 
	new Bus( *(dynamic_cast<Bus*>(_oGar.d_garage[i])));
	continue;
    } else {
      d_garage[i] = new Vehicle( *_oGar.d_garage[i] );
    }
  }
}

const VehicleGarage& VehicleGarage::operator=( const VehicleGarage& _oGar) {
  cout << "VehicleGarage::operator=(const VehicleGarage& )" << endl; 
  if ( &_oGar != this ) {
    d_capacity = _oGar.d_capacity;
    d_size = _oGar.d_size;
    delete[] d_garage;
    if (!(d_garage = new Vehicle*[ d_capacity ]))
      throw std::bad_alloc();
    // Make a deep copy
    for ( int i=0; i<d_size; ++i ) {
      if ( typeid(*_oGar.d_garage[i])==typeid(Boat) ) {
	d_garage[i] = new Boat( *(dynamic_cast<Boat*>(_oGar.d_garage[i])));
	continue;
      }
      if ( typeid(*_oGar.d_garage[i])==typeid(SailBoat) ) {
	d_garage[i] = 
	  new SailBoat( *(dynamic_cast<SailBoat*>(_oGar.d_garage[i])));
	continue;
      }
      if ( typeid(*_oGar.d_garage[i])==typeid(Bus) ) {
	d_garage[i] = 
	  new Bus( *(dynamic_cast<Bus*>(_oGar.d_garage[i])));
	continue;
      } else {
	d_garage[i] = new Vehicle( *_oGar.d_garage[i] );
      }
    }
  } // end check for self-assignment
  return *this;
}

VehicleGarage::~VehicleGarage() {
  cout << "VehicleGarage::~VehicleGarage()" << endl; 
  for ( int i=0; i<d_size; ++i ) {
    delete d_garage[i];
  }
  delete[] d_garage;
  cout << "VehicleGarage deleted" << endl; 
}


int VehicleGarage::park( Vehicle* _veh ) {
  if ( d_size == d_capacity ) return -1;
  if ( typeid(*_veh)==typeid(Boat) ) {
    d_garage[d_size++] = new Boat( *(dynamic_cast<Boat*>(_veh)));
    return d_size;
  }
  if ( typeid(*_veh)==typeid(SailBoat) ) {
    d_garage[d_size++] = new SailBoat( *(dynamic_cast<SailBoat*>(_veh)));
    return d_size;
  }
  if ( typeid(*_veh)==typeid(Bus) ) {
    d_garage[d_size++] = new Bus( *(dynamic_cast<Bus*>(_veh)));
    return d_size;
  } 
  d_garage[d_size++] = new Vehicle(*_veh);
  return d_size;
}


Vehicle* VehicleGarage::get() {
  if ( d_size <= 0 ) return 0;
  cout << "Returning " << d_size << endl;
  return d_garage[--d_size];
}


/** 
 * Construct a bus, boat and sailboat and store them in a garage
 */
int main() 
{
  Bus a( 40, "Diesel" );
  Boat b( 20 );
  SailBoat c( 30 );

  cout << "Parking the vehicles: " << endl;
  VehicleGarage garage(3);
  cout << "Park: " << garage.park(&a) << endl;
  cout << "Park: " << garage.park(&b) << endl;
  cout << "Park: " << garage.park(&c) << endl;


  cout << "Copying the garage: " << endl;  
  // copy construct garage
  VehicleGarage other( garage );

  cout << "Getting the vehicles: " << endl;  
  Vehicle* v; 
  while ((v= other.get())!= 0) {
    cout << "Got a " << v->getName() << " from the garage." << endl;
    delete v;
  };

  cout << "Destructing it all: " << endl;  
  return 0;
}
