/*
 * esercitazione_23_01.cc
 */

#include <iostream>
#include <vector>
#include <math.h>

using namespace std;

class Point {
    float x, y;
public:
    Point();
    Point(float a, float b);
    void set(float a, float b) { x = a; y = b; };
    float get_x() { return x;};
    float get_y() { return y;};
    Point operator+(Point& p);
    Point operator-(Point& p);
    double distance(Point & p);
    friend std::ostream &operator<<(std::ostream &out, Point& p);
};


class Polygon {
    vector<Point> vertices;
public:
    Polygon() {};
    void add_vertex(Point p) { vertices.push_back(p); };
    virtual float perimeter();
    virtual float area();
    friend std::ostream &operator<<(std::ostream &out, Polygon& p);
};


class Triangle : public Polygon {
    float a, b, c;
public:
    Triangle(float _a, float _b, float _c);
    float perimeter();
    float area();
};

Point::Point()
{
    x = 0;
    y = 0;
}

Point::Point(float a, float b)
{
    x = a;
    y = b;
}

Point Point::operator+(Point & p)
{
    Point result;

    result.set(x + p.x, y + p.y);
    return result;
}

Point Point::operator-(Point & p)
{
    Point result;

    result.set(x - p.x, y - p.y);
    return result;
}

double Point::distance(Point & p)
{
    return sqrt((x - p.get_x())*(x - p.get_x()) +
                (y - p.get_y())*(y - p.get_y()) );
}

std::ostream &operator<<(std::ostream &out, Point& c)
{
    out << "(" << c.x << "," << c.y << ")";
    return out;
}


std::ostream &operator<<(std::ostream &out, Polygon& p)
{
    for (int i = 0;i < p.vertices.size();i++)
        cout << p.vertices[i] << ",";
    return out;
}


float Polygon::perimeter()
{
    float perim = 0;
    for (int i = 0;i < vertices.size();i++) {
        Point * v1 = &vertices[i];
        Point * v2;
        if ((i+1) == vertices.size())
            v2 = &vertices[0];
        else
            v2 = &vertices[i+1];
        perim += v1->distance(*v2);
    }
    return perim;
}


float Polygon::area()
{
    float sum1 = 0, sum2 = 0;
    for (int i = 0;i < vertices.size();i++) {
        Point * vi = &vertices[i];
        Point * vi_1;
        if ((i+1) == vertices.size())
            vi_1= &vertices[0];
        else
            vi_1 = &vertices[i+1];

        sum1 += vi->get_x() * vi_1->get_y();
        sum2 += vi_1->get_x() *  vi->get_y();

    }
    return fabs(sum1 - sum2) / 2.0;
}


Triangle::Triangle(float _a, float _b, float _c) : Polygon()
{
    if ( (_a + _b) <= _c ||
         (_a + _c) <= _b ||
         (_b + _c) <= _a )
        throw "Not a triangle";
    a = _a;
    b = _b;
    c = _c;
}

float Triangle::perimeter()
{
    return a + b + c;
}

float Triangle::area()
{
    float p2 = perimeter() / 2.0;
    return sqrt(p2*(p2-a)*(p2-b)*(p2-c));
}

void show_perimeter(Polygon * p)
{
    cout << "The perimeter of the polygon is " << p->perimeter() << endl;
    cout << "... and the area is " << p->area() << endl;
}


main()
{
    Point a(1,2), b(3,4);

    Point c = a + b;
    Point d = a - b;

    cout << "A = " << a << endl;
    cout << "B = " << b << endl;
    cout << "A + B = " << c << endl;
    cout << "A - B = " << d << endl;
    cout << "Distance (A, B) = " << a.distance(b) << endl;

    Polygon poly;

    poly.add_vertex(Point(0,0));
    poly.add_vertex(Point(4,0));
    poly.add_vertex(Point(5,7));
    poly.add_vertex(Point(2,4));

    cout << endl;

    cout << "Polygon P = " << poly << endl;

    cout << "Perimeter P = " << poly.perimeter() << endl;
    cout << "Area P = " << poly.area() << endl;

    cout << endl;

    Triangle t(3,4,5);

    cout << "Triangle Perimeter = " << t.perimeter() << endl;
    cout << "Triangle Area = " << t.area() << endl;

    cout << endl;

    cout << "Testing virtual inheritance:" << endl;
    show_perimeter(&poly);
    show_perimeter(&t);

}


