// ==========================================================================
// $Id: down_cast.cpp,v 1.6 2011/09/10 01:08:19 jlang Exp $
// CSI2372 example Code for lecture 7
// ==========================================================================
// (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: down_cast.cpp,v $
// Revision 1.6  2011/09/10 01:08:19  jlang
// Updates F10
//
// Revision 1.5  2008/10/15 22:01:20  jlang
// Added exception handling
//
// Revision 1.4  2007/11/05 17:09:13  jlang
// Added reference cases
//
// Revision 1.3  2007/11/05 16:55:25  jlang
// extended example
//
// Revision 1.2  2007/11/05 16:14:28  jlang
// Removed extra ^M
//
// Revision 1.1  2006/10/16 01:44:37  jlang
// small caps names
//
// Revision 1.1  2006/10/16 01:36:18  jlang
// Check in for lecture 7
//
//
// ==========================================================================
#include <iostream>

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

using std::bad_cast;

class Building {
  double d_sqft; int d_noRooms;
  float d_price;
public:
  Building( double _sqft = 1000.0, int _noRooms = 3 ) :
    d_sqft( _sqft ), d_noRooms( _noRooms ), d_price( 0 ) {}
  int getNoRooms() { return d_noRooms; }
  virtual ~Building() {   
    cout << "Building::~Building()" << endl;
  }
  void setPrice( float _price ) { d_price = _price; return; } 
  float getPrice() const { return d_price; }
  virtual float calculateTax() const { return d_price * 0.15f; }
protected:
  void setSqft( double _sqft ) { d_sqft = _sqft; }
};

class House : public Building {
  int d_noOccu;
public:
  House( int _noOccu = 2 ) :d_noOccu( _noOccu ) {}
  ~House() {
    cout << "House::~House()" << endl;
  }
  virtual float calculateTax() const { return getPrice() * 0.07f;}
  int getNoOccupants() { return d_noOccu; };	
  int getNoRooms();
  void setSqft( double _sqft );  
};

int House::getNoRooms(){ 
  // Kitchen and Bathroom do not count
  return Building::getNoRooms() - 2;
}

void House::setSqft( double _sqft ) { 
  // max 1500 per person
  if ( _sqft/d_noOccu > 1500.0 ) { _sqft = 1500.0 * d_noOccu; }
  Building::setSqft( _sqft ); 
}

float tax( const Building& _build ) {
  float res = _build.calculateTax();
  return res;
}


class OfficeTower : public Building {
  int d_noCoffeeShops;
public:
  OfficeTower( double _sqft = 50000, int _noRooms = 50 ) 
    : Building( _sqft, _noRooms), d_noCoffeeShops(1) {}
  ~OfficeTower() {
    cout << "OfficeTower::~OfficeTower()" << endl;
  }
  virtual float calculateTax() const { return getPrice() * 0.03f;}
  float getNoCoffeeShops() const { return d_noCoffeeShops; }
};

class Cottage : public Building {
public:
  Cottage( double _sqft = 500, int _noRooms = 1 ) 
    : Building( _sqft, _noRooms){}
  ~Cottage() {
    cout << "Cottage::~Cottage()" << endl;
  }
  virtual float calculateTax() const { return getPrice() * 0.06f;}
};

Building *buildingGenerator() {
  Building *res = new House();
  res->setPrice(100000); 
  return res;
}

void printProps( OfficeTower* myTower ) {
  cout << "Office tax (?): " <<  myTower->calculateTax() << endl;
  cout << "No of Coffee Shops: " <<  myTower->getNoCoffeeShops() << endl;
  return;
}

int main() 
{
  Building *myHouseAsBuilding = buildingGenerator();
  // Compile time error (if not commented out):
  // myHouseAsBuilding->getNoOfOccupants();
  // Using dynamic cast 
  House *myHouse = dynamic_cast<House *>( myHouseAsBuilding );
  // now functions as a house
  cout << "No of Occupants: " << myHouse->getNoOccupants() << endl;
  delete myHouseAsBuilding;
  // Make another house 
  myHouseAsBuilding = buildingGenerator();
  // Try to cast it to an office tower -- dynamic cast will return 0 
  OfficeTower* myTower = dynamic_cast<OfficeTower*>( myHouseAsBuilding );
  if ( myTower ) { 
    printProps( myTower );
  } else {
    cout << "myTower was not a tower!" << endl;
  }
  // Old style cast will always work and fail later (or just turn up nonsense)
  myTower = (OfficeTower*) myHouseAsBuilding;
  if ( myTower ) printProps( myTower );
  delete myTower;
  // Old style cast will always work and fail later (or just turn up nonsense)
  OfficeTower& myRefA = (OfficeTower&) *buildingGenerator();
  // Try to cast it to an office tower -- with references
  // dynamic cast can't return 0; will throw an exception 
  try {
    OfficeTower& myRefB = dynamic_cast<OfficeTower&>( *buildingGenerator() );
  } catch(bad_cast bc) {
    cout << "Down cast from Building to Office Tower failed!" << endl;
  }
  return 0;
}
