#pragma once

#include "fesa/core/diagnostic.hpp"
#include "fesa/core/source_identity.hpp"

#include <array>
#include <cstdint>
#include <filesystem>
#include <optional>
#include <string>
#include <string_view>
#include <vector>

namespace fesa {

// Stable internal identities are vector positions assigned in declaration order.
using EntityIndex = std::uint32_t;

struct Node {
    SourceEntityId sourceId;
    std::array<double, 3> coordinates;
    SourceLocation location;
};

struct LinearElasticMaterial {
    std::string name;
    double youngsModulus;
    double poissonRatio;
    SourceLocation location;
};

struct GeneralBeamSection {
    std::string name;
    double area;
    double i11;
    double i12;
    double i22;
    double torsionalConstant;
    std::array<double, 3> firstAxis;
    std::vector<std::array<double, 2>> sectionPoints;
    SourceLocation location;
};

enum class ShellSourceElementType {
    s4,
    s4r
};

inline constexpr std::string_view kMitc4InternalFormulation{"FESA-MITC4"};

struct ShellSection {
    std::string name;
    double thickness;
    EntityIndex materialIndex;
    SourceLocation location;
};

struct Mitc4ShellDefinition {
    SourceEntityId sourceId;
    ShellSourceElementType sourceType;
    std::array<EntityIndex, 4> nodeIndices;
    EntityIndex materialIndex;
    EntityIndex sectionIndex;
    SourceLocation location;
};

struct ShellNodeInitialFrame {
    EntityIndex nodeIndex;
    std::array<double, 3> director;
    std::array<double, 3> tangentA;
    std::array<double, 3> tangentB;
};

struct EulerBeam3DDefinition {
    SourceEntityId sourceId;
    std::array<EntityIndex, 2> nodeIndices;
    EntityIndex materialIndex;
    EntityIndex sectionIndex;
    SourceLocation location;
};

struct BoundaryCondition {
    std::string target;
    int firstDof;
    int lastDof;
    double value;
    SourceLocation location;
};

struct NodalLoad {
    std::string target;
    int dof;
    double magnitude;
    SourceLocation location;
};

struct StaticStepDefinition {
    std::string name;
    std::vector<BoundaryCondition> boundaries;
    std::vector<NodalLoad> loads;
    double initialIncrement;
    double timePeriod;
    double minimumIncrement;
    double maximumIncrement;
    SourceLocation location;
};

struct NodeSet {
    std::string name;
    std::optional<std::string> instanceName;
    std::vector<EntityIndex> nodeIndices;
    SourceLocation location;
};

struct ElementSet {
    std::string name;
    std::optional<std::string> instanceName;
    std::vector<EntityIndex> elementIndices;
    SourceLocation location;
};

struct PartDefinition {
    std::string name;
    std::vector<std::int64_t> nodeSourceLabels;
    std::vector<std::int64_t> elementSourceLabels;
    std::vector<std::string> nodeSetNames;
    std::vector<std::string> elementSetNames;
    SourceLocation location;
};

struct SourceIndexMapping {
    std::int64_t sourceLabel;
    EntityIndex internalIndex;
};

struct InstanceDefinition {
    std::string name;
    std::string partName;
    std::vector<SourceIndexMapping> nodeMappings;
    std::vector<SourceIndexMapping> elementMappings;
    SourceLocation location;
};

// This construction-boundary value owns every parsed semantic record before
// it is finalized into an immutable Domain.
struct ModelDefinition {
    std::filesystem::path sourcePath;
    std::string sourceContentIdentity;
    std::string heading;
    std::vector<Node> nodes;
    std::vector<EulerBeam3DDefinition> elements;
    std::vector<Mitc4ShellDefinition> shellElements;
    std::vector<LinearElasticMaterial> materials;
    std::vector<GeneralBeamSection> sections;
    std::vector<ShellSection> shellSections;
    std::vector<ShellNodeInitialFrame> shellNodeInitialFrames;
    std::vector<NodeSet> nodeSets;
    std::vector<ElementSet> elementSets;
    std::vector<PartDefinition> parts;
    std::vector<InstanceDefinition> instances;
    std::vector<StaticStepDefinition> steps;
    std::vector<Diagnostic> warnings;
};

} // namespace fesa
