#include <iostream> // подключение библиотеки ввода-вывода
#include <cmath> // подключение библиотеки для работы с математическими функциями

using namespace std; // подключение стандартного пространства имён

class Point {
    int x, y; // переменные x y, представляющие собой координаты точки
public:

    Point() { // конструктор без параметров
        cout << "\nConstructor " << this << " initialized.\n"; // сообщение об инициализации конструктора
    }

    Point(int _x, int _y) { // конструктор с параметрами
        cout << "\nConstructor " << this << " initialized.\n";
        this->x = _x; this->y = _y; // передача аргументов функции в качестве значений переменных класса
    }
/*
    // конструктор с параметрами по умолчанию
    Point(int _x = 0, int _y = 0){
        cout << "Constructor " << this << " initialized.\n"; // сообщение об инициализации конструктора
        this->x = _x; this->y = _y; // передача аргументов функции в качестве значений переменных класса
    }
*/

    void set_coordinates() { // сеттер (установка значений переменных класса)
        cout << " input x y coordinates with space: "; cin >> this->x >> this->y; // ввод значений переменных класса
    }

    void set_coordinates(int _x, int _y) { // дополнительный сеттер (с передачей значений через аргумент функции)
        this->x = _x; this->y = _y; // передача аргументов функции в качестве значений переменных класса
    }
    // геттеры
    int get_x() { return this->x; } // получение значения переменной x
    int get_y() { return this->y; } // получение значения переменной y

    int operator-(Point& b) { // перегрузка оператора вычитания
        // возвращает значение длины вектора по соответствующей формуле
        // отнимаем от значений основного объекта значения объекта, переданного в качестве аргумента
        return abs(sqrt((this->x - b.x) * (this->x - b.x) + (this->y - b.y) * (this->y - b.y)));
    }

    ~Point() { // деструктор
        cout << "Destructor " << this << " initialized.\n"; // сообщение об инициализации деструктора
    }
};

ostream& operator<< (ostream& out, Point& point) //перегрузка оператора вывода
{
    return out << point.get_x() << " " << point.get_y(); // вывод значений x и y у объекта
}

enum class e_types { // список возможных фигур
    rect, // прямоугольник
    square, // квадрат
    rhombus, // ромб
    trapezoid, // трапеция
    same_shape, // произвольный многоугольник
    parallelogram // параллелограмм
};

e_types get_typeof_shape(Point& a, Point& b, Point& c, Point& d) { // функция определения фигуры по введённым точкам

    // подсчёт сторон фигуры
    int AB = a - b;
    int BC = b - c;
    int CD = c - d;
    int DA = d - a;
    // подсчёт диагоналей между противоположными углами
    int AC = a - c;
    int BD = b - d;

    // сравнение сторон. В зависимости от их соотношения делается определение фигуры
    if (AB == CD && BC == DA)
    {
        if (AB == BC && AC == BD) return e_types::square;

        else if (AB == BC && AC != BD) return e_types::rhombus;

        else if (AB != CD && AC == BD) return e_types::rect;

        else if (AB != CD && AC != BD) return e_types::parallelogram;

        else return e_types::same_shape;
    }
    else {
        if (((a.get_x() - b.get_x()) * (c.get_y() - d.get_y()) == (a.get_y() - b.get_y()) * (c.get_x() - d.get_x())) ||
            ((a.get_x() - d.get_x()) * (b.get_y() - c.get_y()) == (a.get_y() - d.get_y()) * (b.get_x() - c.get_x()))){
                return e_types::trapezoid;
        }
        else {
            return e_types::same_shape;
        }
    }
}

static void correct_and_print(Point& point) { // статическая функция для переопределения значения статической переменной
    int x, y;
    cout << "Enter new points: "; cin >> x >> y;
    point.set_coordinates(x, y);
}

int get_square(Point& a, Point& b){ // функция получения площади квадрата
    return ((a - b) * (a - b)); // вычисление значения площади
}

void delete_points(Point *points[4]){ // функция удаления точек
    cout << "\n";
    for (int i = 0; i < 4; i++){ // цикл от 0 до 4
        delete points[i]; // удаление точки
    }
}

enum UserChoice { // список возможных выборов пользователя
    DEFAULT, // конструктор по умолчанию (без параметров)
    WITH_PARAMETERS, // конструктор с параметрами
    STATIC_EXAMPLE // использование статического метода и конструктор без параметров
};

int main() {
    // Важный момент - точки строятся друг за другом, то есть программа
    // производит расчёт исходя из порядка ввода точек.
    // То бишь, на одни и те же координаты, введённые в разном порядке программа выдаст 2 разных результата

    int choice; // выбор типа конструктора
    Point *points[4]; // массив указателей с классом Point

    // переменная типа e_types для определения результата
    e_types* result;
    result = new e_types;
    // вывод меню выбора
    cout << "Please, choose constructor's type:" << "\n";
    cout << "1. Default constructor (without parameters)." << "\n";
    cout << "2. Constructor with parameters." << "\n";
    cout << "3. Example static point." << "\n\n";
    // ввод пользователя
    cin >> choice;
    // выбор дальнейших действий в зависимости от ввода пользователя
    switch (choice - 1) // отнимаем 1, так как отсчёт идёт с нуля
    {
    case UserChoice::DEFAULT: // конструктор без параметров
    {
        cout << "\n" << "Default constructor." << "\n\n"; // вывод типа конструктора

        for (int i = 0; i < 4; i++){ // цикл от 0 до 4
            points[i] = new Point(); // явная инициализация объекта
            cout << i+1 << " point -"; 
            (*points[i]).set_coordinates(); // ввод значений
        }

        cout << "\n\n";
        // вывод координат точек через перегрузку оператора вывода
        for (int i = 0; i < 4; i++){
            cout << i+1 << " point coordinates: " << (*points[i]) << endl;
        }

        cout << "\n";
    
        *result = get_typeof_shape(*points[0], *points[1], *points[2], *points[3]); // определение фигуры по введённым точкам 

        break;
    }
    case UserChoice::WITH_PARAMETERS: // конструктор с параметрами
    {
        cout << "\n" << "Constructor with parameters." << "\n\n"; // вывод типа конструктора

        for (int i = 0; i < 4; i++){
            int x, y; // инициализация переменных
            cout << i+1 << " point -" << " input x y coordinates with space: "; cin >> x >> y; // ввод переменных
            points[i] = new Point(x, y); // явная инициализация через конструктор с параметрами
        }

        cout << "\n\n";
        // вывод координат точек через перегрузку оператора вывода
        for (int i = 0; i < 4; i++){
            cout << i+1 << " point coordinates: " << (*points[i]) << endl;
        }

        cout << "\n";

        *result = get_typeof_shape(*points[0], *points[1], *points[2], *points[3]); // определение фигуры по введённым точкам 

        break;
    }
    case UserChoice::STATIC_EXAMPLE: // конструктор без параметров и использование статической переменной
    {
        cout << "\n" << "Default constructor." << "\n\n";
        string change_static_value; // выбор изменения статической переменной
        static Point* example = new Point(0, -3); // создание статической переменной, задающей координаты точки

        cout << "\nStatic point's coordinates: " << (*example) << "\n"; // вывод значений статической переменной через перегрузку оператора вывода

        for (int i = 0; i < 4; i++){ // цикл от 0 до 4
            points[i] = new Point(); // явная инициализация объекта
            cout << i+1 << " point -"; 
            (*points[i]).set_coordinates(); // ввод значений
        }

        cout << "\n\n";


        cout << "Do you want correct static point? y/n: "; cin >> change_static_value;
        // если пользователь хочет изменить значение статической переменной
        if (change_static_value == "y") correct_and_print(*example); // вызыв статической функции для переопределения точки

        points[0] = new Point((*example).get_x(), (*example).get_y()); // присваивание значения статической переменной в качестве 1 элемента массива с остальными точками
        cout << "\n\n";
        // вывод координат точек через перегрузку оператора вывода
        for (int i = 0; i < 4; i++){
            cout << i+1 << " point coordinates: " << (*points[i]) << endl;
        }

        cout << "\n";

        delete example; // удаление статической переменной за ненадобностью

        *result = get_typeof_shape(*points[0], *points[1], *points[2], *points[3]); // определение фигуры

        break;
    }
    default: // если ничего не совпало
        cout << "\n" << "Incorrect input." << "\n\n"; // вывод о некорректном вводе
        delete result; // удаление result, так как переменная является динамической
        return 0; // завершение программы
        break;
    }

    cout << "\nDefined type of shape: ";
    switch (*result)// проверка результата определения фигуры
    {
    case e_types::same_shape:
        cout << "arbitrary quadrangle" << endl; // произвольный четырёхугольник
        break;
    case e_types::rect:
        cout << "rect" << endl; // прямоугольник
        break;
    case e_types::rhombus:
        cout << "rhombus" << endl;// ромб
        break;
    case e_types::square:
        cout << "square" << endl; // квадрат
        cout << "square area: " << get_square(*points[0], *points[1]) << "\n"; // определение площади квадрата
        break;
    case e_types::trapezoid:
        cout << "trapezoid" << endl; //трапеция
        break;
    case e_types::parallelogram:
        cout << "parallelogram" << endl; // параллелограмм
        break;
    }

    delete result; // удаление result, так как переменная является динамической

    delete_points(points); // удаление динамического массива с объектами класса Point

    return 0; // конец программы
}