#include <iostream>
#include <string>
#include <cmath>

#define PI 3.14

using namespace std;

class Figure {
  public:
    string getName() const;
    virtual void input() = 0;
    virtual float area() const = 0;
  protected:
    string name;
};

class Circle: public Figure {
  public:
    Circle(float radius = 0) : 
          radius(radius) { this->name = "circle"; }
    void input();
    float area() const;
  protected:
    float radius;
};

class Triangle: public Figure {
  public:
    Triangle(float base = 0, float height = 0) : 
          base(base), height(height) { this->name = "triangle"; }
    virtual void input();
    float area() const;
  protected:
    float base, height;
};

class Rectangle: public Figure {
  public:
    Rectangle(float width = 0, float height = 0) : 
          width(width), height(height) { this->name = "rectangle"; }
    virtual void input();
    float area() const;
  protected:
    float width, height;
};

class Square: public Rectangle {
  public:
    Square(float side = 0) : 
          Rectangle(side, side) { this->name = "square"; }
    virtual void input();
// code reusability - no need to implement method area()
};


string Figure::getName() const {
    return this->name;
}


void Circle::input() {
    cout << "Enter the radius of a circle: ";
    cin >> this->radius;    // exceptions
}

float Circle::area() const {
    return PI * this->radius * this->radius;
}


void Triangle::input() {
    cout << "Enter the base of a triangle: ";
    cin >> this->base;      // exceptions
    cout << "Enter height of a triangle: ";
    cin >> this->height;    // exceptions
}

float Triangle::area() const {
    return this->base * this->height / 2;
}


void Rectangle::input() {
    cout << "Enter the width of a rectangle: ";
    cin >> this->width;     // exceptions
    cout << "Enter the height of a rectangle: ";
    cin >> this->height;    // exceptions
}

float Rectangle::area() const {
    return this->width * this->height;
}


void Square::input() {
    cout << "Enter the side of a square: ";
    cin >> this->width;     // exceptions
    this->height = this->width;
}


ostream &operator<<(ostream &s, const Figure &f) {
    s << f.getName();

    return s;
}

void print_info(Figure *f) {    // print_info_ptr()
    cout << "The area of " << *f << " is: " << f->area() << endl;
}

void print_info(Figure &f) {    // print_info_ref()
    cout << "The area of " << f << " is: " << f.area() << endl;
}


int main() {
    int x = 0;
    Figure *figure; // static vs dynamic type

    cout << "Enter 0 for circle, 1 for triangle, 2 for rectangle or 3 for square" << endl;
    cin >> x;
    switch (x) {
        case 0:
            figure = new Circle();
            break;
        case 1:
            figure = new Triangle();
            break;
        case 2:
            figure = new Rectangle();
            break;
        case 3:
            figure = new Square();
            break;
        default:
            return 1;    // exception
    }
    figure->input();
    
    print_info(figure);     // print_info_ptr()
    print_info(*figure);    // print_info_ref()

    delete figure;
    
    return 0;
}
