#include <fesa/model/beam_element.hpp>
#include <fesa/model/beam_section.hpp>
#include <fesa/model/entity_set.hpp>
#include <fesa/model/material.hpp>
#include <fesa/model/node.hpp>
#include <fesa/model/step_definition.hpp>

#include <array>
#include <vector>

#include <gtest/gtest.h>

namespace {

TEST(ModelTypes, PreservesNodeAndBeamElementValues) {
    const fesa::Node first_node{
        fesa::NodeId{0},
        fesa::EntityOrigin{"BeamPart", "Beam-1", 101},
        fesa::Vec3{1.0, 2.0, 3.0},
    };
    const fesa::BeamElement element{
        fesa::ElementId{4},
        fesa::EntityOrigin{"BeamPart", "Beam-1", 201},
        {fesa::NodeId{0}, fesa::NodeId{1}},
        fesa::MaterialId{2},
        fesa::SectionId{3},
    };

    EXPECT_EQ(first_node.id.value(), 0);
    EXPECT_EQ(first_node.origin.part_name, "BeamPart");
    EXPECT_EQ(first_node.origin.instance_name, "Beam-1");
    EXPECT_EQ(first_node.origin.local_label, 101);
    EXPECT_DOUBLE_EQ(first_node.position.x, 1.0);
    EXPECT_DOUBLE_EQ(first_node.position.y, 2.0);
    EXPECT_DOUBLE_EQ(first_node.position.z, 3.0);

    EXPECT_EQ(element.id.value(), 4);
    EXPECT_EQ(element.origin.local_label, 201);
    EXPECT_EQ(element.nodes[0].value(), 0);
    EXPECT_EQ(element.nodes[1].value(), 1);
    EXPECT_EQ(element.material.value(), 2);
    EXPECT_EQ(element.section.value(), 3);
}

TEST(ModelTypes, PreservesMaterialAndBeamSectionValues) {
    const fesa::IsotropicElastic material{
        fesa::MaterialId{2},
        "Steel",
        210.0e9,
        0.3,
    };
    const fesa::BeamSection section{
        fesa::SectionId{3},
        "General",
        0.04,
        1.2e-4,
        1.4e-4,
        2.0e-4,
        0.03,
        0.031,
        fesa::ShearPropertySource::input,
        fesa::Vec3{0.0, 1.0, 0.0},
        {{-0.1, 0.0}, {0.1, 0.0}},
    };

    EXPECT_EQ(material.id.value(), 2);
    EXPECT_EQ(material.name, "Steel");
    EXPECT_DOUBLE_EQ(material.young, 210.0e9);
    EXPECT_DOUBLE_EQ(material.poisson, 0.3);

    EXPECT_EQ(section.id.value(), 3);
    EXPECT_EQ(section.name, "General");
    EXPECT_DOUBLE_EQ(section.area, 0.04);
    EXPECT_DOUBLE_EQ(section.iy, 1.2e-4);
    EXPECT_DOUBLE_EQ(section.iz, 1.4e-4);
    EXPECT_DOUBLE_EQ(section.torsion_j, 2.0e-4);
    EXPECT_DOUBLE_EQ(section.shear_area_y, 0.03);
    EXPECT_DOUBLE_EQ(section.shear_area_z, 0.031);
    EXPECT_EQ(section.shear_source, fesa::ShearPropertySource::input);
    EXPECT_DOUBLE_EQ(section.orientation.y, 1.0);
    ASSERT_EQ(section.recovery_points.size(), 2);
    EXPECT_DOUBLE_EQ(section.recovery_points[0][0], -0.1);
    EXPECT_DOUBLE_EQ(section.recovery_points[1][0], 0.1);
}

TEST(ModelTypes, PreservesSetsAndLinearStaticStepValues) {
    const fesa::NodeSet node_set{
        "Fixed",
        {fesa::NodeId{0}, fesa::NodeId{1}},
    };
    const fesa::ElementSet element_set{
        "Beam",
        {fesa::ElementId{4}},
    };
    const fesa::StepDefinition step{
        "Load",
        {{fesa::NodeId{0}, 6, 0.25}},
        {{
            fesa::NodeId{1},
            {10.0, 20.0, 30.0, 40.0, 50.0, 60.0},
        }},
    };

    EXPECT_EQ(node_set.name, "Fixed");
    ASSERT_EQ(node_set.members.size(), 2);
    EXPECT_EQ(node_set.members[1].value(), 1);
    EXPECT_EQ(element_set.name, "Beam");
    ASSERT_EQ(element_set.members.size(), 1);
    EXPECT_EQ(element_set.members[0].value(), 4);

    EXPECT_EQ(step.name, "Load");
    ASSERT_EQ(step.prescribed_dofs.size(), 1);
    EXPECT_EQ(step.prescribed_dofs[0].node.value(), 0);
    EXPECT_EQ(step.prescribed_dofs[0].dof, 6);
    EXPECT_DOUBLE_EQ(step.prescribed_dofs[0].value, 0.25);
    ASSERT_EQ(step.nodal_loads.size(), 1);
    EXPECT_EQ(step.nodal_loads[0].node.value(), 1);
    EXPECT_DOUBLE_EQ(step.nodal_loads[0].values[5], 60.0);
}

}  // namespace
