#include <iostream>
#include <cmath>
using namespace std;

// Base Class
class Shape {
protected:
	unsigned numSides;
	double width;
	double height;
	string name;

public:
	// Constructor for Shape
	Shape(double w = 0.0, double h = 0.0){
			this->width = w;
			this->height = h;
			this->numSides = 0; // a shape with zero sides is nonsense
			this->name = "";
	}

	void setName(string s){
		this->name = s;
	}

	// Shape methods
	void setWidth(double w){
		width = w;
	}

	void setHeight(double h){
		height = h;
	}

	void setDimensions(double w, double h) {
		this->setWidth(w);
		this->setHeight(h);
	}

	// Virtual function for area - allows derived classes to override
	virtual double getArea() const {
		return 0.0; // Default implementation
	}

};

// Derived Circle Class
class Circle : public Shape {
private:
	double diameter;
	double radius;

public:
	// Constructor for Circle
	Circle(double d){
		this->width = this->height = this->diameter = d;
		this->radius = this->diameter/2;
		this->numSides = 1;
	}

	// Override the getArea method for a triangle
	double getArea() const override{
		// Area of a circle is pi * r^2
		return (M_PI * pow(this->radius,2));
	}
};


// Derived Triangle Class
class Triangle : public Shape {
public:
	// Constructor for Triangle, calls base class constructor
	Triangle(double w, double h) : Shape(w, h) {
		this->numSides = 3;
	}

	// Override the getArea method for a triangle
	double getArea() const override {
		// Area of a triangle is (width * height) / 2
		return (this->width * this->height) / 2.0;
	}
};


// Derived Square Class
class Square : public Shape {
public:
	// Constructor for Square, calls base class constructor
	Square(double w) : Shape(w, w) {
		this->numSides = 4;
	}

	// Override the getArea method for a Square
	double getArea() const override {
		// Area of a square is the square of a side
		return (pow(this->width,2));
	}

	void printSides(){
		for(int i=0; i<this->numSides; i++){
			cout << "side " << i << ": " << this->width << endl;
		}
		cout << endl;
		return;
	}
};


// Derived Rectangle Class
class Rectangle : public Shape {
public:
	// Constructor for Rectangle, calls the base class constructor
	Rectangle(double w, double h) : Shape(w, h) {
		this->numSides = 4;
	}

	// Override the getArea method for a Rectangle
	double getArea() const override {
		// Area of a rectangle is width times height
		return (this->width * this->height);
	}

	void printSides(){
		for(int i=0; i<this->numSides; i++){
			cout << "side " << i << ": ";
			if(i % 2 == 0){
				cout << this->width << endl;
			}else{
				cout << this->height << endl;
			}
		}
		cout << endl;
		return;
	}
};



int main() {
	Triangle myTriangle(5.0, 4.0);
	Square mySquare(5.0);
	Circle myCircle(5.0);
	Rectangle myRectangle(5.0,4.0);

	// Access base class method via derived class object
	myTriangle.setDimensions(6.0, 5.0);

	cout << "Sides of the square:\n";
	mySquare.printSides();

	cout << "Sides of the rectangle:\n";
	myRectangle.printSides();

	// Call derived class's overridden method
	cout << "Area of the triangle: " << myTriangle.getArea() << endl;
	cout << "Area of the square: " << mySquare.getArea() << endl;
	cout << "Area of the circle: " << myCircle.getArea() << endl;
	cout << "Area of the rectangle: " << myRectangle.getArea() << endl;

	// Demonstration of polymorphism (using a base class pointer)
	Shape* shapePtr = &myTriangle;
	cout << "Area of the Triangle via shape pointer: " << shapePtr->getArea() << endl;

	shapePtr = &mySquare;
	cout << "Area of the Square via shape pointer: " << shapePtr->getArea() << endl;

	shapePtr = &myCircle;	
	cout << "Area of the Circle via shape pointer: " << shapePtr->getArea() << endl;

	shapePtr = &myRectangle;	
	cout << "Area of the Rectangle via shape pointer: " << shapePtr->getArea() << endl;

	return 0;
}