#pragma once

#include <utility>
#include <vector>
#include "FileNotExistException.h"

struct Node {
    unsigned int ID;
    double x, y, z;
    bool isVertex;

    Node() = default;

    Node(unsigned int p_ID, double p_x, double p_y, double p_z, bool p_isVertex) : ID(p_ID), x(p_x), y(p_y), z(p_z),
                                                                                   isVertex(p_isVertex) {}

    friend std::ostream &operator<<(std::ostream &ost, const Node &node) {
        ost << "ID: " << node.ID << ", coordinates: {" << node.x << ", " << node.y << ", " << node.z << "}";
        return ost;
    }
};

struct FiniteElement {
    unsigned int ID;
    unsigned int areaID;
    std::vector<unsigned int> nodesID;

    FiniteElement() = default;

    FiniteElement(unsigned int p_ID, unsigned int p_areaID,
                  std::vector<unsigned int> &p_nodesID) : ID(p_ID),
                                                          areaID(p_areaID), nodesID(p_nodesID) {}

    friend std::ostream &operator<<(std::ostream &ost, const FiniteElement &element) {
        ost << "ID: " << element.ID << ", area ID: " << element.areaID << ", nodes ID: {";
        for (unsigned int nodeID: element.nodesID) {
            ost << nodeID << " ";
        }
        ost << "\b" << "}";
        return ost;
    }
};

struct BoundaryElement { //!!! По заданию выделять отдельно BoundaryElement не нужно. Используйте FiniteElement
    unsigned int ID;
    unsigned int boundaryID;
    std::vector<unsigned int> nodesID;

    BoundaryElement() = default;

    BoundaryElement(unsigned int p_ID, unsigned int p_boundaryID,
                    std::vector<unsigned int> p_nodesID) : ID(p_ID), boundaryID(p_boundaryID),
                                                           nodesID(std::move(p_nodesID)) {}

    friend std::ostream &operator<<(std::ostream &ost, const BoundaryElement &element) {
        ost << "ID: " << element.ID << ", boundary ID: " << element.boundaryID << ", nodes ID: {";
        for (unsigned int nodeID: element.nodesID) {
            ost << nodeID << " ";
        }
        ost << "\b" << "}";
        return ost;
    }
};

struct Edge {
    unsigned int node1, node2, middleNode;

    Edge() = default;

    Edge(unsigned int p_node1, unsigned int p_node2, unsigned int mid) : node1(std::min(p_node1, p_node2)),
                                                       node2(std::max(p_node1, p_node2)), middleNode(mid) {}

    bool operator==(const Edge &edge) const {
        return node1 == edge.node1 && node2 == edge.node2;
    }
};